2014-09-02 63 views
2

我有一个小应用程序,它只是使用Jetty v9.2轮询服务器,HttpClient。几天后,应用程序将冻结。最初我们确定需要的线程池为increased in size以减轻性能影响。这种改变在几天内恢复了性能。锁定仍然存在。原因已被隔离到HTTP GET调用(如果我们注释掉方法,问题就会消失)。在Jetty HttpClient Hang上寻求建议

其中出现码头 HttpClient的连接管理线程管理底层的根本原因。通常Jetty HttpClient使一组线程处理HTTP GET(见下文),这些启动和消失如你所愿。大约40小时,或操作后,JDK VisualVM的显示至少9个连接线程消失立即:

  • 的HttpClient - 调度器X 1
  • 的HttpClient - 选择器客户机SeclectorManager×4
  • HttpClient的×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九月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. 写成不有泄漏或离开资源挂在码头HttpClient的代码所需要的get()方法的代码?
  2. 我们怎么能赶上警告:“无法停止线程”的错误?
    • 这个错误的影响是什么?有没有办法'粉碎'这样的线程卡住?
    • 这是否涉及到10个悬挂连接线程呢?只有一个警告信息。
    • 想象一个悬挂线程保证一个ERROR标签,而不是警告。
  3. 有没有在Jetty HttpClient中捕捉线程错误和错误的进程?
  4. HttpClient有哪些可用的属性来调整服务?
    • 是否有我们可以用来直接影响线程锁定的设置?
  5. HttpClient的环境或上下文中有哪些属性可用于控制调整服务?
  6. Jetty HttpClient可以重新启动/重新启动还是刚刚停止?
    • 码头呼叫仅在所示的GET方法制成(虽然具有更多的日志记录等)
  7. 是否RMI螺纹因子作为码头HttpClient的呼叫的一部分?

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

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

建议,意见,改进和答案是最受欢迎的。我们提前致谢。

相关问题

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

回答

1

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

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

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

其次异步调用在调用HttpCLient.stop()之前必须等待CompleteListener。这似乎是确保停止“干净”完成的唯一途径。对于某些情况,stop()调用似乎继续确定。最终有些会导致异常,你的应用程序会慢慢泄漏资源。外观就像JVM已冻结,但一些非deamon任务可能会继续(例如GUI线程),并且直到PC本身资源不足或崩溃时才会发现问题。这是一个极端的情况## Heading ##运行数周。

可靠的例子适当地关闭HttpClient的如下所示:

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

我觉得文档需要强调清理并确保stop()被调用。据我所知,如何结束Async调用的信息是无证的。只要您的Jetty调用停止干净,然后提供足够的线程来解决此问题 - 通常需要管理并发性。