我有一个小应用程序,只使用Jetty v9.2, HttpClient 轮询服务器。几天后,应用程序将冻结。最初我们确定了线程池需要increased in size来缓解性能损失。这一变化在几天内恢复了表现。锁定仍然存在。原因已被隔离到HTTP GET调用(如果我们注释掉该方法,问题就会消失。)
显示 Jetty HttpClient 连接管理或线程管理的基础的根本原因。通常,Jetty HttpClient会创建一组线程来处理HTTP GET(见下文),这些启动和消失正如您所期望的那样。大约40个小时或操作后,JDK VisualVM显示至少9个连接线程, 立即消失:
也
共有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的使用的细节很少。一切都是根据霍伊尔完成的吗?
在不同的执行运行中,我们还会看到以下异常和日志消息:
我们想知道这条消息是否与卡住的线程有关?或者,此消息是否表明我们需要检查一个单独且不同的问题?也:
这似乎是一个线程超时异常。虽然哪个线程?这与HTTP连接线程有关吗?我认为,至少当服务在内部捕获错误时,他们至少可以指出错误的位置和堆栈跟踪。
有一些明显的问题:
另一个观察结果是当我们遇到" VisualVM中的线程,它在Threads面板中显示了多余的Daemon线程,而不是非Daemon线程的增加。
通过在for循环中运行上面显示的代码大约3或4个小时,HttpClient之间的间隔为250毫秒,send()调用显示线程泄漏 - 在Linux上重现很简单。日志输出显示没有警告,并且网络上只有两个超时错误,距离线程泄漏至少30分钟。
欢迎提出建议,意见,改进和答案。我们提前感谢。
相关问题:
这些问题涵盖了一些非常相似的观点
答案 0 :(得分:1)
这种情况似乎可以通过确保两件事来解决。
这两个行动是相互关联的。如果有时HttpClient错过了异常或错误,则线程会挂起。似乎避免这种情况的唯一方法是确保每个HttpCLient都使用调用 HttpCLient.stop()。这需要进入 finally {...} 子句。
其次,在调用HttpCLient.stop()之前,异步调用必须等待CompleteListener。这似乎是确保阻止干净利落的唯一方法。完成。在某些情况下,stop()调用似乎继续正常。最终有些会导致异常并且您的应用程序会慢慢泄漏资源。外观就像JVM已经冻结,但是一些非deamon任务可能会继续(例如GUI线程),并且您可能不会注意到问题,直到PC本身耗尽资源或崩溃。这是一个极端情况## Heading ##运行了几个星期。
此处显示了适当关闭HttpClient的可靠示例:
线程数取决于您的应用程序。我建议使用jVisualVM或类似的东西,以确保在调整线程池中的线程数之前,首先正确清理Jetty线程。
我觉得文档需要强调清理并确保调用stop()。据我所知,有关如何结束异步调用的信息没有记录。只要您的Jetty调用完全停止,那么提供足够的线程似乎可以解决这个问题 - 通常需要注意管理并发性。