我有一個小應用程序,它只是使用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 -