近期的項目中,有一個特殊的需求,對於每個客戶端程序有若干個機構,對於每個機構有不同的客戶端證書,程序間隔一段時間向服務端進行請求,根據請求的成功與否更新各機構的狀態(如正常,證書未配置,證書過期等)。
實際投入測試環境進行使用的時候,運行了一段時間之后,客戶端程序出現了大量的CLOSE_WAIT的情況,導致壓力測試無法正常進行。
對相關的代碼進行了檢查之后,發現了之前的做法是對於每一個機構,維護一個RestTemplate對象,在其中進行讀取證書等操作。懷疑和大量的restTemplate有關這個問題,因為本地開發的時候基本只有幾個機構進行測試,所以未出現以上情況。根據CLOSE_WAIT出現在客戶端的情況進行分析,是服務端發起了關閉連接的請求,而客戶端進行了響應之后,接收了數據完成后並沒有進行關閉,導致出現了CLOSE_WAIT。
首先增加了連接池的參數 setValidateAfterInactivity(如下),發現不起作用。
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); connectionManager.setValidateAfterInactivity(200);
然后在初始化HttpClient時增加了參數 evictIdleConnections ,發現生效。
CloseableHttpClient httpClient = HttpClients.custom() .setSSLSocketFactory(csf) .setConnectionManager(connectionManager) .evictIdleConnections(2, TimeUnit.SECONDS) .build();
通過分析源碼發現,RestTemplate在不做額外配置的情況下,會默認開啟KeepAlive,而服務端不進行額外配置的時候,不會返回額外的內容,此時客戶端進行了相關的判斷之后,如果響應頭沒有Keep-Alive會返回-1,即認為默認不釋放,后續服務端進行相應的連接的斷開時,客戶端認為可重用,就不進行清理,導致一致處於CLOSE_WAIT狀態,而由於使用了大量的RestTemplate,導致大量的CLOSE_WAIT出現。
連接池的配置不起作用是因為底層調用的是連接池的release方法,而release方法內部會進行相應的判斷,如果發現是可重用的鏈接,就不會釋放。
CloseableHttpClient的配置生效是因為底層是查看是否過期,如果過期,則調用關閉方法。