簡單利用PoolingHttpClientConnectionManager來優化HttpClient方法,避免httpclient導致的排隊堆積從而引發java.net.SocketException: Network is unreachable (connect failed)
背景描述(無關問題,可以忽略):
之前因為公司數據庫訪問速度太慢,導致我所負責的模塊查詢效率也及低,所以對其數據庫方面做了優化,因為sql已經沒有太多的優化空間了,所以就對數據庫表做了調整。優化過程主要是將幾張舊表中合法有效的數據轉移到一張新表上,這樣避免了每次查詢的排查數據是否合法,也省去了兩表聯查三表聯查等降低查詢效率的字段。
而問題就出在將舊表數據轉移到新表上時,有很多斷鏈需要處理掉。我首先想到的是用HttpClient直接逐個請求,返回200保存到新表中,非200則丟棄。就這樣,問題來了:
問題描述
HttpClient連接沒有做關閉操作,導致new的httpclient過多造成tomcat假死 or DB死鎖。
解決思路
利用httpclient池化技術來保證請求的數量,並且及時關閉無效的請求。
實現過程,直接上代碼了
幫助類
/**
* httpclient池工具類
* @Author: zhuyu
* @Date: Create in 18:40:09 2020年12月20日 0020
*/
public class HttpClientHelper {
static PoolingHttpClientConnectionManager clientPool = new PoolingHttpClientConnectionManager();
// httpclient請求超時配置
static RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();
// 將需要配置的值都放到靜態代碼塊中,類被加載時,靜態代碼塊也會被加載
static {
// 最大連接請求數
clientPool.setMaxTotal(5);
// 最大路由連接數
clientPool.setDefaultMaxPerRoute(5);
}
// 通過getHttpclientPool方法來獲取httpclient,避免了直接去手動new
public static CloseableHttpClient getHttpclientPool() {
return HttpClients.custom().setConnectionManager(clientPool).setDefaultRequestConfig(requestConfig).build();
}
}
功能實現類
class Test {
private static final int SUCCESS_CODE = 200;
private static final String Read_Timed_Out_ERROR = "Read timed out";
public static void main(String[] args) {
// 開始時間
long startTime = System.currentTimeMillis();
// 請求鏈接
String requestHref = "http://www.baidu.com";
// 從HttpClient連接池中獲取httpclient
CloseableHttpClient httpClient = HttpClientHelper.getHttpclientPool();
int times = 0;
for (int i = 0; i < 50; i++) {
HttpGet httpGet = new HttpGet(requestHref);
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != SUCCESS_CODE) {
System.out.println("發現斷鏈");
}
times++;
} catch (Exception e) {
if (Read_Timed_Out_ERROR.equals(e.getMessage())) {
System.out.println("連接超時");
} else {
System.out.println("e.getMessage() = " + e.getMessage());
}
}finally {
httpGet.releaseConnection();
}
}
System.out.println("本次請求一共耗時:{" + (System.currentTimeMillis() - startTime) + "}"+"共請求次數 :"+times);
}
}
最后測試,大功告成。需要注意的是:
實現過程的bug
利用單例模式創建httpclient pool后,直接用httpclienthelper創建了httpclient,然后在關閉時遇到了問題,因為之前設置的最大連接數是5,

所以導致httpclient訪問5次后就卡住了,說明請求沒有被釋放。


嘗試直接close httpclient,但這樣會導致Connection pool shut down


最終只能把希望寄托於httpGet上了,進入httpGet中尋找方法,果不其然!!

CloseableHttpResponse是可以刷新請求內部狀態的,這樣既可以使得pool中的五個連接繼續發起請求,又可以避免new太多httpclient。
結語:一般如果要重復使用httpclient時,推薦使用連接池去操作,但如果只是請求幾次,那么直接new也沒什么關系,不過要記得close
