標籤:
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
http://www.cnblogs.com/zhanjindong/p/httpclient-connection-pool.html
HttpClient串連池的串連保持、逾時和失效機制