默認 footer ive tcp連接 部分 i/o模型 不存在 復雜 header

HTTP是一種無連接的事務協議,底層使用的還是TCP,連接池復用的就是TCP連接,目的就是在一個TCP連接上進行多次的HTTP請求從而提高性能。每次HTTP請求結束的時候,HttpClient會判斷連接是否可以保持,如果可以則交給連接管理器進行管理以備下次重用,否則直接關閉連接。這裏涉及到三個問題:

1、如何判斷連接是否可以保持?

要想保持連接,首先客戶端需要告訴服務器希望保持長連接,這就是所謂的Keep-Alive模式(又稱持久連接,連接重用),HTTP1.0中默認是關閉的,需要在HTTP頭加入"Connection: Keep-Alive",才能啟用Keep-Alive;HTTP1.1中默認啟用Keep-Alive,加入"Connection: close ",才關閉。

但客戶端設置了Keep-Alive並不能保證連接就可以保持,這裏情況比較復。要想在一個TCP上進行多次的HTTP會話,關鍵是如何判斷一次HTTP會話結束了?非Keep-Alive模式下可以使用EOF(-1)來判斷,但Keep-Alive時服務器不會自動斷開連接,有兩種最常見的方式。

使用Conent-Length

顧名思義,Conent-Length表示實體內容長度,客戶端(服務器)可以根據這個值來判斷數據是否接收完成。當請求的資源是靜態的頁面或圖片,服務器很容易知道內容的大小,但如果遇到動態的內容,或者文件太大想多次發送怎麽辦?

使用Transfer-Encoding

當需要一邊產生數據,一邊發給客戶端,服務器就需要使用 Transfer-Encoding: chunked 這樣的方式來代替 Content-Length,Chunk編碼將數據分成一塊一塊的發送。它由若幹個Chunk串連而成,以一個標明長度為0 的chunk標示結束。每個Chunk分為頭部和正文兩部分,頭部內容指定正文的字符總數(十六進制的數字 )和數量單位(一般不寫),正文部分就是指定長度的實際內容,兩部分之間用回車換行(CRLF) 隔開。在最後一個長度為0的Chunk中的內容是稱為footer的內容,是一些附加的Header信息。

對於如何判斷消息實體的長度,實際情況還要復雜的多,可以參考這篇文章:https://zhanjindong.com/2015/05/08/http-keep-alive-header

總結下HttpClient如何判斷連接是否保持:

檢查返回response報文頭的Transfer-Encoding字段,若該字段值存在且不為chunked,則連接不保持,直接關閉。
檢查返回的response報文頭的Content-Length字段,若該字段值為空或者格式不正確(多個長度,值不是整數),則連接不保持,直接關閉。
檢查返回的response報文頭的Connection字段(若該字段不存在,則為Proxy-Connection字段)值:
如果這倆字段都不存在,則1.1版本默認為保持, 1.0版本默認為連接不保持,直接關閉。
如果字段存在,若字段值為close 則連接不保持,直接關閉;若字段值為keep-alive則連接標記為保持。
2、 保持多長時間?

保持時間計時開始時間為連接交換至連接池的時間。 保持時長計算規則為:獲取response中 Keep-Alive字段中timeout值,若該存在,則保持時間為 timeout值*1000,單位毫秒。若不存在,則連接保持時間設置為-1,表示為無窮。

3、保持過程中如何保證連接沒有失效?

很難保證。傳統阻塞I/O模型,只有當I/O操做的時候,socket才能響應I/O事件。當TCP連接交給連接管理器後,它可能還處於“保持連接”的狀態,但是無法監聽socket狀態和響應I/O事件。如果這時服務器將連接關閉的話,客戶端是沒法知道這個狀態變化的,從而也無法采取適當的手段來關閉連接。

針對這種情況,HttpClient采取一個策略,通過一個後臺的監控線程定時的去檢查連接池中連接是否還“新鮮”,如果過期了,或者空閑了一定時間則就將其從連接池裏刪除掉。ClientConnectionManager提供了 closeExpiredConnections和closeIdleConnections兩個方法。

參考文章

HTTP協議頭部與Keep-Alive模式詳解

又見KeepAlive

引申閱讀

淺談CLOSE_WAIT

又見CLOSE_WAIT

HttpClient連接池的連接保持、超時和失效機制