【问题标题】:Seeking advice on Jetty HttpClient Hang求教Jetty HttpClient Hang
【发布时间】:2014-09-02 04:03:15
【问题描述】:

我有一个小型应用程序,它使用 Jetty v9.2 HttpClient 简单地轮询服务器。几天后,应用程序将冻结。最初,我们确定线程池需要为increased in size 以减轻性能损失。该更改在几天内恢复了性能。锁定仍然存在。原因已被隔离到 HTTP GET 调用(如果我们注释掉该方法,问题就会消失)。

Jetty HttpClient 连接管理线程管理的根本原因。通常 Jetty HttpClient 会创建一组线程来处理 HTTP GET(见下文),这些线程会按照您的预期启动和消失。大约 40 小时或运行后,JDK VisualVM 显示至少 9 个连接线程不会立即消失

  • HttpClient - 调度程序 x 1
  • HttpClient - 选择器客户端 SecectorManager x 4
  • HttpClient x 4

还有

  • RMI TCP 连接

总共九或十个线程。在下一次读取时,会创建新的线程实例来承载负载并且客户端继续。此外应用程序。有一个带有专用线程的时钟,在应用程序锁定后继续运行,这表明 JVM、操作系统和机器本身都很好。

有时,我们会看到这些“卡住”的线程会停留长达一个小时,然后才会退出 VisualVM 线程显示。至少 36 小时后,我们看到线程仍然存在并且我们没有看到它们消失。

经过足够多的时间后,软件就会锁定。指示的解释是未清理的线程实例泄漏。它出现了应用程序。线程用完了,不能做更多的工作。它肯定会阻止服务器日志所见证的 HTTP GET。

主要的HTTP调用使用下面的代码,HttpClient GET方法:

 /**
  *   GET
  *   @return null or string returned from server
  **/
 public static String get( final String command ){

    String          rslt        = null;
    final String    reqStr      = "http://www.google.com";  //  (any url)

    HttpClient      httpClient  = new HttpClient();
    Request         request;
    ContentResponse response;

    try {
            //-- Start HttpClient
        httpClient.start();

        request   = httpClient.newRequest( reqStr );

        response  = request.send();

        if( null == response ){
            LOG.error( "NULL returned from previous HTTP request.");
        }
        else {
            if( (501 == response.getStatus()) || (502 == response.getStatus()) ){
                setNetworkUnavailable(String.format("HTTP Server error: %d", response.getStatus() ));
            }
            else {
                if(  404 == response.getStatus() ){
                    Util.puts(LOG,"HTTP Server error: 404");
    //              ignore message since we are talking to an old server
                }
                else if( 200 == response.getStatus() ){
                    rslt = response.getContentAsString();
                }
                else {
                    LOG.error(String.format( "    * Response status: \"%03d\".", response.getStatus() ));
                }
                setNetworkAvailable();
            }
        }
    }
    catch ( InterruptedException iEx ){
        LOG.warn( "InterruptException processing: "+reqStr, iEx );
    }
    catch ( Exception ex ){

        Throwable cause = eEx.getCause();
        if( (cause instanceof NoRouteToHostException) ||
            (cause instanceof EOFException)           ||
            (cause instanceof SocketException)
                && cause.getMessage().startsWith( EX_NETWORK_UNREACHABLE ) ){

            setNetworkUnavailable( cause.getMessage() );
        }
        else {
            LOG.error( "Exception on: "+command, ex );
        }
    }
    finally {
        try {
            httpClient.stop();
        }
        catch ( Exception ex ){
            LOG.error( "Exception httpClient.stop(), ServerManager::get()", ex );
        }
    }

    return rslt;

}//get method

这是基于简单的示例,关于使用 HttpClient 的细节很少。一切都是按照霍伊尔做的吗?

在不同的执行运行中,我们还会看到以下异常和日志消息:

  • [36822522] WARN 2014-Sep-02 02:46:28.464> HttpClient@2116772232{STOPPING,8

我们想知道这条消息是否与其中一个卡住的线程有关?或者,此消息是否表明我们需要检查一个单独且不同的问题?另外:

  • java.util.concurrent.TimeoutException (ExecutionException)

这似乎是线程超时异常。哪个线程?这是否与他的 HTTP 连接线程有关?我认为,当服务在内部捕获错误时,它们至少可以指示错误的位置和堆栈跟踪。

有一些明显的问题:

  1. get() 方法代码是否按要求编写,以免 Jetty HttpClient 代码发生泄漏或使资源挂起?
  2. 我们如何捕捉警告:“无法停止线程”错误?
    • 此错误有什么影响?有没有办法“粉碎”这样卡住的线程?
    • 这是否与 10 个悬挂连接线程有关?只有一条警告消息。
    • 可以想象一个挂起的线程需要一个 ERROR 标签,而不是一个警告。
  3. Jetty HttpClient 中是否有捕获线程错误和一般错误的流程?
  4. HttpClient 可以使用哪些属性来调整服务?
    • 是否有我们可以用来直接影响线程锁定的设置?
  5. HttpClient 的环境或上下文中有哪些属性可用于控制调整服务?
  6. Jetty HttpClient 是否可以重新启动/重新启动或只是停止?
    • Jetty 调用仅在所示的 GET 方法中进行(尽管有更多日志记录等)
  7. RMI 线程是否是 Jetty HttpClient 调用的一部分?

另一个观察结果是,当我们在 VisualVM 中“卡住”线程时,它会在“线程”面板中显示过多的守护程序线程,而不是非守护程序线程的增加。

通过在 for 循环中运行上面显示的代码大约 3 或 4 小时,并在 HttpClient send() 调用之间有 250 毫秒的中断,显示了线程泄漏——在 Linux 上很容易重现。日志输出在距离线程泄漏至少 30 分钟的网络上没有显示 WARNing 和只有两个超时错误。

非常欢迎提出建议、观察、改进和回答。提前致谢。

相关问题

这些问题涵盖了一些非常相似的点

【问题讨论】:

    标签: java multithreading httpclient freeze jetty-9


    【解决方案1】:

    这种情况似乎可以通过确保两件事来解决。

    1. 确保应用程序的线程池中有足够的线程
    2. 确保使用 Jetty 清理代码并捕获/管理所有异常。

    这两个动作是相互关联的。如果有时 HttpClient 错过异常或错误,则线程会挂起。避免这种情况的唯一方法似乎是确保使用的每个 HttpCLient 都调用 HttpCLient.stop()。这需要放在 finally {...} 子句中。

    其次,异步调用必须在调用 HttpCLient.stop() 之前等待 CompleteListener。这似乎是确保停车“干净”完成的唯一方法。在某些情况下,stop() 调用似乎可以正常进行。最终有些会导致异常,您的应用程序会慢慢泄漏资源。外观就像 JVM 已冻结,但一些非守护程序任务可能会继续(例如 GUI 线程),并且您可能不会注意到问题,直到 PC 本身耗尽资源或崩溃。这是一个极端情况## Heading ##running 数周。

    此处显示了适当关闭 HttpClient 的可靠示例:

    线程数取决于您的应用程序。我建议使用jVisualVM 或类似的东西来确保您的 Jetty 线程都正确清理,然后再调整线程池中的线程数。

    我觉得文档需要强调清理并确保调用 stop()。据我所知,如何结束异步调用的信息没有记录。只要您的 Jetty 调用干净地停止,那么提供足够的线程似乎可以解决这个问题——通常需要注意管理并发性。

    【讨论】:

      猜你喜欢
      • 2013-08-05
      • 1970-01-01
      • 2018-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多