最近工做中遇到了一個rpc高頻調用出現java
java.net.BindException: 地址已在使用
問題,因爲線下測試的時候並無龐大的數據量來支撐,這個問題並無在測試階段被發現。研究發現,出現改問題的主要是由於短期內OS的socket被迅速燒光,程序初啓動前20秒內一切正常,後面開始狂拋此類異常。以前的http調用主要使用httpclient框架實現,代碼以下:
多線程
HttpClient httpClient = HttpClient(); GetMethod method = GetMethod(); httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(); httpClient.getHttpConnectionManager().getParams().setSoTimeout(); { httpClient.executeMethod(method); } (Throwable t) { System..println(); } String responseBodyAsString = ; { responseBodyAsString = method.getResponseBodyAsString(); System..println(); } (Throwable t) { System..println(); }
該段代碼被封裝在一個普通的發放內 每次調用傳入相應的URL和參數便可。正常狀況下並不會出現什麼問題。深刻分析代碼後發現httpclient 對象內部會默認建立一個HttpClientParams對象和SimpleHttpConnectionManager對象 manager 對象內部會持有一個connection 這裏能夠類比與jdbc 中的connection。很是蛋疼的一點是這個鏈接在執行完請求後並不會被當即關閉。SimpleHttpConnectionManager對象中有這樣一句框架
alwaysClose = ;
這意味着該鏈接默認不會被關閉。固然隨着方法被執行完,這些對象就成了垃圾數據會被gc掉。可是底層資源的釋放速度可就很差說了。偏偏在於上面的那一大段代碼處於方法裏,在高頻調用這個方法時,會建立超多的底層tcp鏈接。下面的故事就不用多說了。socket
可能有人會想到既然沒關掉這個鏈接那就想辦法關掉就是了 。本人初期也是這麼想的 關掉不就好了嗎。網上蕩了一份代碼tcp
HttpClient httpClient = HttpClient(HttpClientParams(),SimpleHttpConnectionManager());
聽說這樣就能夠保證鏈接會斷開(客戶端斷開),然並卵~~~測試
繼續坑,既然斷掉也不行那就複用好吧,翻看源碼發現spa
HttpConnectionManager
這個接口的實現有三個(包含SimpleHttpConnectionManager),其中一個叫作.net
MultiThreadedHttpConnectionManager
發現了曙光,爲多線程準備的ConnectionManager .搞來玩玩。 因而乎就出現瞭如下的代碼線程
MultiThreadedHttpConnectionManager =MultiThreadedHttpConnectionManager(); { .getParams().setMaxTotalConnections(); .getParams().setDefaultMaxConnectionsPerHost(); }
須要指出的是,這個管理器能夠管理多個鏈接池,底層是一個Map key爲host Value 則是connectionPoolcode
setDefaultMaxConnectionsPerHost 就是指每一個host容許創建的最多鏈接數。不須要太多!
結合
new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true));
想到了什麼 ?
是的 :
new HttpClient(new HttpClientParams(),cm);
下面的代碼原封不動,可是要主要的是獲取完數據後記得調用
method.releaseConnection();
正所謂好借好還,再借不難嘛
這些鏈接會被管理器揣在兜裏須要的時候問他申請,有就給你,沒有嘛一邊呆着等
我的猜想是藉助了http鏈接的keepAlive 特性。實現多個請求複用一個通道。
注:本文借鑑了多篇該領域的文章,同時結合自身的一些實踐心得整理而成