在Jetty HttpClient上寻求建议

时间:2014-09-02 04:03:15

标签: java multithreading httpclient hang jetty-9

我有一个小应用程序,只使用Jetty v9.2, HttpClient 轮询服务器。几天后,应用程序将冻结。最初我们确定了线程池需要increased in size来缓解性能损失。这一变化在几天内恢复了表现。锁定仍然存在。原因已被隔离到HTTP GET调用(如果我们注释掉该方法,问题就会消失。)

显示 Jetty HttpClient 连接管理线程管理的基础的根本原因。通常,Jetty HttpClient会创建一组线程来处理HTTP GET(见下文),这些启动和消失正如您所期望的那样。大约40个小时或操作后,JDK VisualVM显示至少9个连接线程, 立即消失

  • HttpClient - scheduler x 1
  • HttpClient - 选择器客户端SeclectorManager x 4
  • HttpClient x 4

  • RMI TCP连接

共有9个或10个主题。在下一次读取时,将创建新的线程实例以承载负载并且客户端继续。此外,应用程序。有一个带有专用线程的时钟,在应用程序锁定后继续运行,表明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< = 0< = 200,i = 0,q = 0}无法停止线程[HttpClient @ 2116772232-729770,5,]

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

  • 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调用的一部分吗?
  8. 另一个观察结果是当我们遇到" VisualVM中的线程,它在Threads面板中显示了多余的Daemon线程,而不是非Daemon线程的增加。

    通过在f​​or循环中运行上面显示的代码大约3或4个小时,HttpClient之间的间隔为250毫秒,send()调用显示线程泄漏 - 在Linux上重现很简单。日志输出显示没有警告,并且网络上只有两个超时错误,距离线程泄漏至少30分钟。

    欢迎提出建议,意见,改进和答案。我们提前感谢。

    相关问题

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

1 个答案:

答案 0 :(得分:1)

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

  1. 确保应用程序的线程池中有足够的线程
  2. 确保使用Jetty的代码清理并捕获/管理所有异常。
  3. 这两个行动是相互关联的。如果有时HttpClient错过了异常或错误,则线程会挂起。似乎避免这种情况的唯一方法是确保每个HttpCLient都使用调用 HttpCLient.stop()。这需要进入 finally {...} 子句。

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

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

    线程数取决于您的应用程序。我建议使用jVisualVM或类似的东西,以确保在调整线程池中的线程数之前,首先正确清理Jetty线程。

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