HttpClient相比傳統JDK自帶的URLConnection,增長了易用性和靈活性,它不只使客戶端發送Http請求變得容易,並且也方便開發人員測試接口(基於Http協議的),提升了開發的效率,也方便提升代碼的健壯性。所以熟練掌握HttpClient是很重要的必修內容,掌握HttpClient後,相信對於Http協議的瞭解會更加深刻。html
org.apache.commons.httpclient.HttpClient與org.apache.http.client.HttpClient的區別Commons的HttpClient項目如今是生命的盡頭,再也不被開發, 已被Apache HttpComponents項目HttpClient和HttpCore 模組取代,提供更好的性能和更大的靈活性。java
在前面的博客中http協議中,能夠看到http的請求頭中能夠設置connection能夠設置爲Keep-Alive,在HTTP/1.1使用Keep-Alive爲默認值,若是須要關閉則須要手動關閉。nginx
在HTTP 1.0之前,每一個http請求都要求打開一個TCP socket鏈接,而且使用一次以後就斷開這個TCP鏈接,這會致使頻繁地建立和銷燬TCP。HTTP 1.1經過使用keep-alive能夠改善這種狀態,即在一次TCP鏈接中能夠持續發送多份數據而不會斷開鏈接,以此提升性能和提升http服務器的吞吐率(更少的tcp鏈接意味着更少的系統內核調用,socket的accept()和close()調用)。apache
當保持長鏈接時,如何判斷一次請求已經完成?
Content-Length
Content-Length表示實體內容的長度。瀏覽器經過這個字段來判斷當前請求的數據是否已經所有接收。
因此,當瀏覽器請求的是一個靜態資源時,即服務器能明確知道返回內容的長度時,能夠設置Content-Length來控制請求的結束。但當服務器並不知道請求結果的長度時,如一個動態的頁面或者數據,Content-Length就沒法解決上面的問題,這個時候就須要用到Transfer-Encoding字段。瀏覽器
Transfer-Encoding
Transfer-Encoding是指傳輸編碼,在上面的問題中,當服務端沒法知道實體內容的長度時,就能夠經過指定Transfer-Encoding: chunked來告知瀏覽器當前的編碼是將數據分紅一塊一塊傳遞的。固然, 還能夠指定Transfer-Encoding: gzip, chunked代表實體內容不只是gzip壓縮的,仍是分塊傳遞的。最後,當瀏覽器接收到一個長度爲0的chunked時, 知道當前請求內容已所有接收。服務器
Keep-Alive timeout:
Httpd守護進程,通常都提供了keep-alive timeout時間設置參數。好比nginx的keepalive_timeout,和Apache的KeepAliveTimeout。這個keepalive_timout時間值意味着:一個http產生的tcp鏈接在傳送完最後一個響應後,還須要hold住keepalive_timeout秒後,纔開始關閉這個鏈接。
當httpd守護進程發送完一個響應後,理應立刻主動關閉相應的tcp鏈接,設置 keepalive_timeout後,httpd守護進程會想說:」再等等吧,看看瀏覽器還有沒有請求過來」,這一等,即是keepalive_timeout時間。若是守護進程在這個等待的時間裏,一直沒有收到瀏覽器發過來http請求,則關閉這個http鏈接。cookie
tcp連接創建以後,若是應用程序或者上層協議一直不發送數據,或者隔很長時間才發送一次數據,當連接好久沒有數據報文傳輸時如何去肯定對方還在線,究竟是掉線了仍是確實沒有數據傳輸,連接還需不須要保持,這種狀況在TCP協議設計中是須要考慮到的。
TCP協議經過一種巧妙的方式去解決這個問題,當超過一段時間以後,TCP自動發送一個數據爲空的報文給對方,若是對方迴應了這個報文,說明對方還在線,連接能夠繼續保持,若是對方沒有報文返回,而且重試了屢次以後則認爲連接丟失,沒有必要保持連接。網絡
HTTP位於網絡協議棧的應用層,而TCP位於網絡協議棧的傳輸層,二者的KEEP-ALIVE雖然名稱相同,可是做用不一樣。http keep-alive是爲了讓tcp活得更久一點,以便在同一個鏈接上傳送多個http,提升socket的效率。而tcp keep-alive是TCP的一種檢測TCP鏈接情況的保鮮機制。t檢測對端是否依然存活。併發
優勢:Keep-Alive模式更加高效,由於避免了鏈接創建和釋放的開銷。 app
缺點:長時間的Tcp鏈接容易致使系統資源無效佔用,浪費系統資源。
因此對於須要頻繁發送HTTP請求的應用,須要在客戶端開啓keep-alive,使用HTTP長鏈接。
httpClient = HttpClients.custom() //鏈接池配置 .setConnectionManager(poolingHttpClientConnectionManager) //requestConfig配置 .setDefaultRequestConfig(requestConfig) .disableCookieManagement() .disableConnectionState() .disableAuthCaching() //默認socketConfig配置 .setDefaultSocketConfig(socketConfig) //默認頭配置 .setDefaultHeaders(defaultHeaders) //重試handle .setRetryHandler(httpRequestRetryHandler) .build();
兩個主機創建鏈接的過程是很複雜的一個過程,涉及到多個數據包的交換,而且也很耗時間。Http鏈接須要的三次握手開銷很大,這一開銷對於比較小的http消息來講更大。可是若是咱們直接使用已經創建好的http鏈接,這樣花費就比較小,吞吐率更大。在高併發大量的請求網絡的時候,使用鏈接池能提高吞吐量。
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry); // 設置整個鏈接池的最大鏈接數 cm.setMaxTotal(maxTotal); // 設置每一個route默認的最大鏈接數 cm.setDefaultMaxPerRoute(maxPerRoute); HttpHost httpHost = new HttpHost(hostname, port); // 設置某個route的最大鏈接數,優先於defaultMaxPerRoute。 cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute); //該方法關閉超過鏈接保持時間的鏈接,並從池中移除。 cm.closeExpiredConnections(); //該方法關閉空閒時間超過timeout的鏈接,空閒時間從交還給鏈接池時開始,不論是否已過時,超過空閒時間則關閉。 cm.closeIdleConnections(timeout,tunit);
connectionConfig配置
//消息約束 MessageConstraints messageConstraints = MessageConstraints.custom() .setMaxHeaderCount(200) .setMaxLineLength(2000) .build(); //Http connection相關配置 ConnectionConfig connectionConfig = ConnectionConfig.custom() .setMalformedInputAction(CodingErrorAction.IGNORE) .setUnmappableInputAction(CodingErrorAction.IGNORE) .setCharset(Consts.UTF_8) .setMessageConstraints(messageConstraints) .build(); //通常不修改HTTP connection相關配置,故不設置 //cm.setDefaultConnectionConfig(connectionConfig); //cm.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT);
具體源碼解析可參考:https://www.cnblogs.com/shoren/p/httpclient-leaseConnection.html
主要用於獲取和配置一些外部的網絡環境
RequestConfig requestConfig = RequestConfig.custom() //設置從connectManager獲取Connection 超時時間 .setConnectionRequestTimeout(1000) //設置鏈接超時時間 .setConnectTimeout(10000) //請求獲取數據的超時時間 .setSocketTimeout(10000) //肯定是否應自動處理身份驗證 .setAuthenticationEnabled(true) //肯定循環重定向(重定向到相同位置)是否應該重定向 .setCircularRedirectsAllowed(false) //重定向的最大數目。對重定向次數的限制是爲了防止無限循環 .setMaxRedirects(5) //肯定是否應自動處理重定向 .setRedirectsEnabled(true) //肯定是否應拒絕相對重定向。HTTP規範要求位置值是一個絕對URI .setRelativeRedirectsAllowed(true) //肯定是否應自動解壓縮壓縮實體 .setContentCompressionEnabled(true) //肯定用於HTTP狀態管理的cookie規範的名稱 .setCookieSpec("") //返回用於請求執行的本地地址。在具備多個網絡接口的計算機上,此參數可用於選擇其中的網絡接口鏈接產生。 .setLocalAddress() //代理配置 .setProxy() //在使用代理主機進行身份驗證時,肯定支持的身份驗證方案的優先順序。 .setProxyPreferredAuthSchemes() //在使用目標主機進行身份驗證時,肯定受支持的身份驗證模式的首選項順序 .setTargetPreferredAuthSchemes() .build();
SocketConfig.custom() //開啓監視TCP鏈接是否有效 .setSoKeepAlive(false) //是否能夠在一個進程關閉Socket後,即便它尚未釋放端口,其它進程還能夠當即重用端口 .setSoReuseAddress(true) //接收數據的等待超時時間,單位ms .setSoTimeout(10000) //是否當即發送數據,設置爲true會關閉Socket緩衝,默認爲false .setTcpNoDelay(false) .build();
Collection<Header> defaultHeaders = new ArrayList<>(); defaultHeaders.add(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate")); defaultHeaders.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")); defaultHeaders.add(new BasicHeader(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")); defaultHeaders.add(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive"));
//禁用重試(參數:retryCount、requestSentRetryEnabled) HttpRequestRetryHandler requestRetryHandler = new DefaultHttpRequestRetryHandler(0, false); //自定義重試策略 httpRequestRetryHandler = new HttpRequestRetryHandler() { public boolean retryRequest(IOException exception,int executionCount, HttpContext context) { if (executionCount >= 3) {// 若是已經重試了3次,就放棄 return false; } if (exception instanceof NoHttpResponseException) {// 若是服務器丟掉了鏈接,那麼就重試 return true; } if (exception instanceof SSLHandshakeException) {// 不要重試SSL握手異常 return false; } if (exception instanceof InterruptedIOException) {// 超時 return false; } if (exception instanceof UnknownHostException) {// 目標服務器不可達 return false; } if (exception instanceof ConnectTimeoutException) {// 鏈接被拒絕 return false; } if (exception instanceof SSLException) {// SSL握手異常 return false; } return false; }
}
其實咱們在實際使用中也是使用默認的 socketConfig 和 connectionConfig。在實際應用中鏈接數相關配置(如maxTotal、maxPerRoute),還有請求相關的超時時間設置(如connectionTimeout、socketTimeout、connectionRequestTimeout)是比較重要的。
具體鏈接池原理參考文檔:
httpClient 4.3.x configuration 官方樣例
使用httpclient必須知道的參數設置及代碼寫法、存在的風險
---恢復內容結束---