CloseableHttpClient 連接超時 失敗 資源釋放 最大連接數(shù)

CloseableHttpClient填坑經(jīng)歷 資源回收 最大連接數(shù)

因為項目中的一些原因,部分網(wǎng)絡(luò)請求無法使用封裝好的RestTemplate,只好使用CloseableHttpClient,這才有了以下的填坑經(jīng)歷,如果可以,這些知識點學(xué)習就好,項目中還是使用封裝好的組件比較妥當。

問題場景:

在使用CloseableHttpClient的請求中,都是一樣的請求,只是會分發(fā)到不同的服務(wù)器,在這些服務(wù)器中,唯獨有一臺服務(wù)器在一些操作之后,到該服務(wù)器的連接都失敗嗎,報504 Gateway Timeout;

經(jīng)過排查,排除了服務(wù)器的原因;

后續(xù)測試發(fā)現(xiàn),是若到某個服務(wù)器的某些請求因為某些原因失敗兩次后,后續(xù)到該服務(wù)器的請求都將失敗,而到該服務(wù)器的其他請求依舊正常;

問題原因

具體分析過程不再贅述,直接上結(jié)果:

在CloseableHttpClient中,有最大連接數(shù)的限制,默認值為:

1、 單個Host域名,最大連接數(shù)為2;

2、 整個客戶端內(nèi),總共最大連接數(shù)為20;

上面第一點是最騷的,竟然還會限制單個域名下的請求數(shù),一下就和問題場景對上了;

問題修復(fù)

修復(fù)一:改連接數(shù)限制

經(jīng)過分析,很明顯直接把默認的請求數(shù)限制改大一點就能解決問題:

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(XXX);//連接池最大并發(fā)連接數(shù)
cm.setDefaultMaxPerRoute(XXX);//單域名下最大連接數(shù)
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

但是這治標不治本,不從根本找出連接被占滿的原因,再大的連接限制也終究是會被撐爆的,且看改進。

修復(fù)二:

我的實現(xiàn)是一個請求函數(shù),其內(nèi)部基本是下面這樣的

String errMsg;
HttpResponse response = closeableHttpClient.execute(myRequest);
HttpEntity resEntity = response.getEntity();
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK && resEntity != null) {
    return EntityUtils.toString(resEntity, StandardCharsets.UTF_8);
} else {
    LOG.debug(JSON.toJSONString(response));
    errMsg = response.getStatusLine().getReasonPhrase();
}
throw new HttpException(errMsg);

很顯然,平時使用Connection的話需要手動關(guān)閉的連接這邊都沒有出現(xiàn),那么怎么關(guān)閉呢?

在CloseableHttpClient內(nèi)部是一個連接池,我們請求結(jié)束后,需要盡快釋放連接,避免后面的請求因為沒有連接阻塞超時,最終失敗,而釋放連接可以從三個方面入手:

  • 在execute(httpRequest)中,httpRequest是可以關(guān)閉的,httpRequest.releaseConnection();
  • HttpResponse response = closeableHttpClient.execute(myRequest)中,execute的response是繼承了Closeable的,HttpResponse 改為CloseableHttpResponse,并在請求結(jié)束后主動關(guān)閉;
  • HttpEntity resEntity = response.getEntity();,這里的resEntity內(nèi)部實際上有一個輸入流,我們在EntityUtils.toString(resEntity, StandardCharsets.UTF_8);已經(jīng)將該流關(guān)閉了,而若請求失敗的話,則沒有被關(guān)閉的操作,所以可以調(diào)用EntityUtils.consumeQuietly(resEntity);主動關(guān)閉

所以,在源頭上避免連接被耗盡,我們需要及時釋放資源,以上修改完成代碼如下:

String errMsg;
// 自動關(guān)閉返回
try (CloseableHttpResponse response = closeableHttpClient.execute(signedRequest)){
    HttpEntity resEntity = response.getEntity();
    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK 
        && resEntity != null) {
        // toString方法內(nèi)已關(guān)閉流
        return EntityUtils.toString(resEntity, StandardCharsets.UTF_8);  
    } else {
        // 主動關(guān)閉內(nèi)部的輸入流
        EntityUtils.consumeQuietly(resEntity); 
        LOG.debug(JSON.toJSONString(response));
        errMsg = response.getStatusLine().getReasonPhrase();
    }
} catch (Exception e){
    // do your business
} finally {
    // 主動釋放連接
    signedRequest.releaseConnection(); 
}
throw new HttpException(errMsg);

總結(jié)

經(jīng)過以上第一種修改,相當于客戶端容量增大了,能更好的容忍請求時間較長、并發(fā)數(shù)大等情況,而第二種修改則從源頭上保證了每個連接使用的高效性,避免無用連接占用資源,請求結(jié)束后都能夠得到有效釋放,二者結(jié)合,將最大程度的提高服務(wù)器的并發(fā)數(shù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第二章 連接管理 HttpClient有一個對連接初始化和終止,還有在活動連接上I/O操作的完整控制。而連接操作的...
    狂奔的蝸牛_wxc閱讀 1,399評論 0 0
  • Getting Started Burp Suite 是用于攻擊web 應(yīng)用程序的集成平臺。它包含了許多工具,并為...
    Eva_chenx閱讀 29,241評論 0 14
  • 參考資源 官網(wǎng) 國內(nèi)博客 GitHub官網(wǎng) 鑒于一些關(guān)于OKHttp3源碼的解析文檔過于碎片化,本文系統(tǒng)的,由淺入...
    風骨依存閱讀 12,699評論 11 82
  • 最近總是忘記很多事情 因為我的腦容量太小 除了想你 再也裝不下其它
    妖顏冫惑眾閱讀 203評論 0 0
  • 相親苦,相親累,相了半天不般配。 又搭煙,又搭糖,搭來搭去還是黃。 父母跟,家人隨,七嘴八舌直皺眉。 有說壞,有說...
    讀者愛之成就閱讀 246評論 0 0

友情鏈接更多精彩內(nèi)容