2

我在Scala應用程序中使用Apache Http Client。Apache HttpClient PoolingHttpClientConnectionManager泄漏連接?

該應用程序具有高吞吐量和高並行性。

我不確定,但我想我可能是在泄漏連接。似乎只要使用客戶端的代碼段變得繁忙,應用程序就會變得無法響應。我的懷疑是我正在泄漏套接字或其他導致應用程序其他方面停止工作的東西。它可能也不會泄漏連接,因爲它們不會足夠快地關閉連接。

對於更多的上下文,偶爾某些操作會導致此代碼每分鐘並行執行數百次。發生這種情況時,應用程序的Rest API(Spray)將無響應。應用程序的其他領域也以高並行性運行,並且這些應用程序響應性永遠不會造成問題。

減少這部分代碼的並行性似乎可以緩解問題,但不是一個可行的長期解決方案。

我忘了配置什麼,或者配置不正確?

我使用的代碼是這樣的:

class SomeClass { 
    val connectionManager = new PoolingHttpClientConnectionManager() 
    connectionManager.setDefaultMaxPerRoute(50) 
    connectionManager.setMaxTotal(500) 
    val httpClient = HttpClients.custom().setConnectionManager(connectionManager).build() 

    def postData() { 
    val post = new HttpPost("http://SomeUrl") // Typically this URL is fixed. It doesn't vary much if at all. 
    post.setEntity(new StringEntity("Some Data")) 
    try { 
     val response = httpClient.execute(post) 
     try { 
     // Check the response 
     } finally { 
     response.close() 
     } 
    } finally { 
     post.releaseConnection() 
    } 
    } 
} 

編輯

我可以看到我建立了大量處於TIME_WAIT狀態的連接。我已經嘗試將DefaultMaxPerRoute和MaxTotal調整爲各種值,但沒有明顯的效果。這似乎是我錯過了一些東西,因此連接沒有被重新使用,但我找不到任何文件表明我缺少的東西。這些連接重新使用至關重要。

EDIT 2

隨着進一步的調查,使用lsof的-p,我可以看到,如果我設置MaxPerRoute至10,實際上被列爲 「ESTABLISHED」 10個連接。我可以看到端口號不會改變。這似乎暗示我實際上正在重新使用這些連接。

什麼不解釋是爲什麼我仍然泄漏在此代碼中的連接?在TIME_WAIT狀態下顯示的重用連接和泄漏連接(與netstat -a一起發現)共享相同的基本URL。所以他們絕對是相關的。是否有可能我正在重新使用連接,但不知何故不正確地關閉響應?

EDIT 3

位於所述TIME_WAIT 「泄漏」 的來源。這是在一個不相關的代碼部分。所以這與HttpClient沒有任何關係。然而,在修復該代碼後,所有的TIME_WAIT都消失了,但是當多次訪問HttpClient代碼時,應用程序仍然沒有響應。仍在調查那部分。

+0

要分析爲什麼您的噴霧應用程序變得無響應(如果您認爲這可能是一個問題),您可能需要收集一些堆棧痕跡以查看Akka /噴霧線程在哪裏花費時間。在控制檯上使用'jps'和'jstack'來做到這一點。隨意在噴霧郵件列表上發佈關於它的信息。 – jrudolph

回答

1

你應該真的考慮重新使用HttpClient實例或至少是支持它的連接池,而不是爲每個新的請求執行創建它們。如果您希望繼續執行後者,則還應關閉客戶端或在連接池超出範圍之前關閉連接池。

至於泄漏而言,它應該是比較容易通過運行帶有上下文記錄連接管理你的應用程序來跟蹤橫空出世描述here

+0

上面的示例代碼並沒有完全清楚,但上面的類只創建一次。因此,HttpClient和ConnectionManager將被重用於每個請求。 – Wally

+1

我明白了。每當此類的實例被解除引用或即將超出範圍時,您仍然應該關閉連接池。此外,關於連接管理上下文日誌的位仍然存在。這應該至少讓你知道所有連接是否被正確釋放回池中 – oleg

0

IMO - 你可以爲每個使用MAXCONNECTION的要少得多域(如5而不是50),並且仍然完全飽和你的網絡帶寬,如果你有效地使用http的話。

我不是一個斯卡拉人(android,java),但在http客戶端線程池上做了很多很多優化。國際海事組織(IMO) - 盲目地將每個域的連接數增加到50個,正在掩蓋其他一些與吞吐量有關的嚴重問題

2點:

,如果你使用的是共享「sharedPoolingClientConnManager」,正確地將每個域一個小型游泳池,您符合釋放你的康恩回池中的推薦方式(你應該能夠調試所有這些都看到每個線程池實例的連接狀態的運行指標),那麼你應該很好。

無論scala的並行性特性如何,您應該瞭解某個域上池中的5個相應線程如何共享套接字?根據android/java的經驗,IMO認爲即使每個線程執行者都在該httpclient.exec語句的範圍內阻止對服務器的I/O,但實際的通道管理允許非常高的吞吐量,而無需藉助ASNyC客戶端庫HTTP。

Android體驗可能不相關,因爲客戶端只有4個線程。話雖如此,即使你有64或更多的線程可用,我只是不明白每個域需要超過10個連接才能讓你的底層http套接字非常非常忙碌。