【Http】HTTPclient保持長連接需要消費返回信息 不然很卡

在這裏插入圖片描述

首先解釋一下什麼是長連接,當我們向一臺服務器發起請求時,我們需要和對方建立一條通道,去傳輸數據,所謂的短連接,就是說我們建立起了通道,然後在傳輸完數據,就把通道摧毀,下次需要的時候再重新去建立通道。長連接呢,就是指,我們建立了一條通道,傳遞完數據後,先不摧毀,下次如果還需要傳輸數據,就複用這條通道。因爲通道的建立是需要花費時間的,所以長連接的優勢就在於響應速度快,但是服務器壓力大,因爲同時有很多人在向服務器建立通道,即便有些通道已經傳輸完數據了,由於長連接的原因,通道也不會被摧毀;短連接呢,則是,響應速度慢,服務器壓力小。由於現在更多的是強調用戶的體驗,所以長連接目前是最常見的。

如何在java中實現一個長連接呢,其實很簡單,只需要在請求的請求頭中加入特定的參數 :“Connection”:"keep-alive"即可。這樣如果對方支持長連接的話,那麼這個連接就會保持長連接了。

問題的關鍵就來了,在一次壓測某個https請求響應速度的代碼中,我發現了,當對方響應數據爲null,也就是responseBody中帶的數據爲null時,響應速度特別快,大概在5ms左右,但是一旦對方返回了響應數據,本次響應就可能達到了20ms。然後請運維同事抓包,發現每次連接,都會耗費時間在用戶認證上,其實也就是從某個方面反應出,每次都是新建立了一個連接。

HttpPost httpPost = new HttpPost("xxxxx");
httpPost.addHeader("Connection", "keep-alive");

CloseableHttpClient httpClient = null;
for(int i =0 ;i<5000;i++){
    long t1 = System.currentTimeMillis();
	CloseableHttpResponse response = httpClient.execute(httpPost);
	long t2 = System.currentTimeMillis();
}

上了一段簡單的代碼,表示一下大概的邏輯,實際的壓測代碼還包括了線程池,連接池的完整參數等等,如果需要的可以留言或者翻看本人的其他的博客,有寫線程池和連接池。

就是這樣一段代碼,導致每次連接都是新建立的連接,那麼原因是什麼呢,我們先上代碼:

for (int i = 0; i<5000;i++){
     long t1 = System.currentTimeMillis();
      CloseableHttpResponse response = httpClient.execute(httpPost);
      if(null != response.getEntity()){
           EntityUtils.consume(response.getEntity());
      }
      long t2 = System.currentTimeMillis();

我們只需要簡單加上三行代碼,就可以解決這個問題了,這是爲什麼呢,讓我們點進去源碼看一下
在這裏插入圖片描述

這麼一看, 其實這個方法也並沒有做什麼,只是簡單的取到了流去關閉,爲什麼就保持長連接了呢。

後來仔細讀http連接的原理才得知,當一個連接建立,響應數據時,會封裝CloseableHttpResponse這個對象裏面,其中的Entity對象就是包含着響應體的數據,我們需要用流去獲取。如果你不去獲取,那麼這個數據就會存在於這個對象中,連接池就會認爲,這個通道里有未處理的數據,然後它不會去複用這個通道,而是選擇重建一個通道。這就完美解釋了爲什麼壓測時,對方返回null時,響應速度特別快,而攜帶返回數據時,響應速度特別慢了。

那麼再仔細想想,爲什麼我們平常不知道這個知識點,卻從來沒有報過錯呢,那是因爲正常情況下,我們都是需要會對response做處理,比如String responseContent = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); 類似這種,我們點進源碼看,其實也是取到了流,並做了關閉操作。平常還是要多閱讀源碼,想想源碼。
————————————————

原文鏈接:https://blog.csdn.net/MrHamster/article/details/94590184

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章