2014-09-02 48 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調用停止乾淨,然後提供足夠的線程來解決此問題 - 通常需要管理併發性。