我有一个小应用程序,它只是使用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连接线程有关吗?当服务在内部捕获错误时,我认为它至少可以指出错误的位置和堆栈跟踪。
有一些明显的问题:
- 写成不有泄漏或离开资源挂在码头HttpClient的代码所需要的get()方法的代码?
- 我们怎么能赶上警告:“无法停止线程”的错误?
- 这个错误的影响是什么?有没有办法'粉碎'这样的线程卡住?
- 这是否涉及到10个悬挂连接线程呢?只有一个警告信息。
- 想象一个悬挂线程保证一个ERROR标签,而不是警告。
- 有没有在Jetty HttpClient中捕捉线程错误和错误的进程?
- HttpClient有哪些可用的属性来调整服务?
- 是否有我们可以用来直接影响线程锁定的设置?
- HttpClient的环境或上下文中有哪些属性可用于控制调整服务?
- Jetty HttpClient可以重新启动/重新启动还是刚刚停止?
- 码头呼叫仅在所示的GET方法制成(虽然具有更多的日志记录等)
- 是否RMI螺纹因子作为码头HttpClient的呼叫的一部分?
另外一个观察结果是,当我们在VisualVM中“卡住”线程时,它会在“线程”面板中显示多余的守护进程线程,而不会增加非守护进程线程。
通过在for循环中运行上面显示的代码约3或4小时,HttpClient send()调用之间的间隔为250毫秒,显示线程泄露 - 在Linux上重现很简单。日志输出显示没有警告,并且只有两次网络超时错误,距离线程泄漏至少30分钟。
建议,意见,改进和答案是最受欢迎的。我们提前致谢。
相关问题:
这些问题涵盖了一些非常相似点
- Jetty stopping without reason
- ConnectionPoolTimeoutException: timeout waiting for connection from pool
- Unable to shut down Neo4j Jetty server within Tomcat webapp -