使用apache的httpclient進行http的交互處理已經很長時間了,而httpclient實例則使用了http連接池,想必大家也沒有關心過連接池的管理。事實上,通過分析httpclient源碼,發現它很優雅地隱藏了所有的連接池管理細節,開發者完全不用花太多時間去思考連接池的問題。
2|0Apache官網例子
3|0HttpClient及其連接池配置
- 整個線程池中最大連接數 MAX_CONNECTION_TOTAL = 800
- 路由到某台主機最大並發數,是MAX_CONNECTION_TOTAL(整個線程池中最大連接數)的一個細分 ROUTE_MAX_COUNT = 500
- 重試次數,防止失敗情況 RETRY_COUNT = 3
- 客戶端和服務器建立連接的超時時間 CONNECTION_TIME_OUT = 5000
- 客戶端從服務器讀取數據的超時時間 READ_TIME_OUT = 7000
- 從連接池中獲取連接的超時時間 CONNECTION_REQUEST_TIME_OUT = 5000
- 連接空閑超時,清楚閑置的連接 CONNECTION_IDLE_TIME_OUT = 5000
- 連接保持存活時間 DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000
4|0MaxtTotal和DefaultMaxPerRoute的區別
- MaxtTotal是整個池子的大小;
- DefaultMaxPerRoute是根據連接到的主機對MaxTotal的一個細分;
比如:MaxtTotal=400,DefaultMaxPerRoute=200,而我只連接到http://hjzgg.com時,到這個主機的並發最多只有200;而不是400;而我連接到http://qyxjj.com 和 http://httls.com時,到每個主機的並發最多只有200;即加起來是400(但不能超過400)。所以起作用的設置是DefaultMaxPerRoute。
5|0HttpClient連接池模型
6|0HttpClient從連接池中獲取連接分析
org.apache.http.pool.AbstractConnPool
7|0連接重用和保持策略
http的長連接復用, 其判定規則主要分兩類。
1. http協議支持+請求/響應header指定
2. 一次交互處理的完整性(響應內容消費干凈)
對於前者, httpclient引入了ConnectionReuseStrategy來處理, 默認的采用如下的約定:
- HTTP/1.0通過在Header中添加Connection:Keep-Alive來表示支持長連接。
- HTTP/1.1默認支持長連接, 除非在Header中顯式指定Connection:Close, 才被視為短連接模式。
7|1HttpClientBuilder創建MainClientExec
7|2ConnectionReuseStrategy(連接重用策略)
org.apache.http.impl.client.DefaultClientConnectionReuseStrategy
7|3MainClientExec處理連接
處理完請求后,獲取到response,通過ConnectionReuseStrategy判斷連接是否可重用,如果是通過ConnectionKeepAliveStrategy獲取到連接最長有效時間,並設置連接可重用標記。
7|4連接重用判斷邏輯
- request首部中包含Connection:Close,不復用
- response中Content-Length長度設置不正確,不復用
- response首部包含Connection:Close,不復用
- reponse首部包含Connection:Keep-Alive,復用
- 都沒命中的情況下,如果HTTP版本高於1.0則復用
更多參考:https://www.cnblogs.com/mumuxinfei/p/9121829.html
8|0連接釋放原理分析
HttpClientBuilder會構建一個InternalHttpClient實例,也是CloseableHttpClient實例。InternalHttpClient的doExecute方法來完成一次request的執行。
會繼續調用MainClientExec的execute方法,通過連接池管理者獲取連接(HttpClientConnection)。
構建ConnectionHolder類型對象,傳遞連接池管理者對象和當前連接對象。
請求執行完返回HttpResponse類型對象,然后包裝成HttpResponseProxy對象(是CloseableHttpResponse實例)返回。
CloseableHttpClient類其中一個execute方法如下,finally方法中會調用HttpResponseProxy對象的close方法釋放連接。
最終調用ConnectionHolder的releaseConnection方法釋放連接。
CloseableHttpClient類另一個execute方法如下,返回一個HttpResponseProxy對象(是CloseableHttpResponse實例)。
這種情況下調用者獲取了HttpResponseProxy對象,可以直接拿到HttpEntity對象。大家關心的就是操作完HttpEntity對象,使用完InputStream到底需不需要手動關閉流呢?
其實調用者不需要手動關閉流,因為HttpResponseProxy構造方法里有增強HttpEntity的處理方法,如下。
調用者最終拿到的HttpEntity對象是ResponseEntityProxy實例。
ResponseEntityProxy重寫了獲取InputStream的方法,返回的是EofSensorInputStream類型的InputStream對象。
EofSensorInputStream對象每次讀取都會調用checkEOF方法,判斷是否已經讀取完畢。
checkEOF方法會調用ResponseEntityProxy(實現了EofSensorWatcher接口)對象的eofDetected方法。
EofSensorWatcher#eofDetected方法中會釋放連接並關閉流。
綜上,通過CloseableHttpClient實例處理請求,無需調用者手動釋放連接。
9|0HttpClient在Spring中應用
9|1創建ClientHttpRequestFactory
9|2創建RestTemplate
9|3 Spring官網例子
10|0總結
Apache的HttpClient組件可謂良心之作,細細的品味一下源碼可以學到很多設計模式和比編碼規范。不過在閱讀源碼之前最好了解一下不同版本的HTTP協議,尤其是HTTP協議的Keep-Alive模式。使用Keep-Alive模式(又稱持久連接、連接重用)時,Keep-Alive功能使客戶端到服 務器端的連接持續有效,當出現對服務器的后繼請求時,Keep-Alive功能避免了建立或者重新建立連接。這里推薦一篇參考鏈接:https://www.jianshu.com/p/49551bda6619。
__EOF__
作 者:胡峻崢
出 處:https://www.cnblogs.com/hujunzheng/p/11629198.html
關於博主:編程路上的小學生,熱愛技術,喜歡專研。評論和私信會在第一時間回復。或者直接私信我。
版權聲明:署名 - 非商業性使用 - 禁止演繹,協議普通文本 | 協議法律文本。
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是博主的最大動力!
