CloseableHttpClient的個性化配置借助 HttpClientBuilder來完成,HttpClientBuilder線程不安全。
調用 HttpClientBuilder實例方法除了build方法外,返回都是其本身,同時HttpClientBuilder 包含了諸多屬性,並提供了對外的設置方法。
/** * useSystemProperties() 是否讀取系統屬性, 調用該方法則可以讀取 * disableAuthCaching() 是否禁用緩, 調用該方法則禁用 * disableRedirectHandling() 是否禁用重定向, 調用該方法則禁用 * disableContentCompression() 是否禁用內容壓縮, 調用該方法則禁用 * disableAutomaticRetries() 是否禁用自動重試, 調用該方法則禁用 * disableCookieManagement() 是否禁用cookie管理, 調用該方法則禁用 * disableConnectionState() 是否禁用連接狀態, 調用該方法則禁用 * * setMaxConnTotal() 全局最大維持的連接數 * setMaxConnPerRoute() 單個Route最大連接數 * evictIdleConnections() 設置最長空閑時間及空閑時間的單位, * 調用此方法會設置evictIdleConnections=true, 表示開啟獨立線程清理空閑連接 * evictExpiredConnections() 開啟獨立線程清理過期連接 * */
setDefaultConnectionConfig()
默認的Connection設置。
HttpClientBuilder httpClientBuilder = HttpClients.custom(); ConnectionConfig.Builder connectionConfigBuilder = ConnectionConfig.custom(); //設置緩存區大小, 默認是8192(8kb) connectionConfigBuilder.setBufferSize(1024); //設置編碼 connectionConfigBuilder.setCharset(Consts.UTF_8); //設置碎片大小 connectionConfigBuilder.setFragmentSizeHint(1024); //設置消息約束 MessageConstraints messageConstraints = MessageConstraints.custom() .setMaxHeaderCount(200) .setMaxLineLength(2000) .build(); connectionConfigBuilder.setMessageConstraints(messageConstraints); connectionConfigBuilder.setUnmappableInputAction(CodingErrorAction.IGNORE); connectionConfigBuilder.setMalformedInputAction(CodingErrorAction.IGNORE); ConnectionConfig configConfig = connectionConfigBuilder.build(); //一般不修改HTTP connection相關配置,故不設置 httpClientBuilder.setDefaultConnectionConfig(configConfig);
setDefaultRequestConfig()
HttpClientBuilder httpClientBuilder = HttpClients.custom(); RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); //以下方法除build()方法都是返回同一個RequestConfig.Builder實例,所以可以進行鏈式調用 //連接超時時間, 單位毫秒 requestConfigBuilder.setConnectTimeout(2000); //從池中獲取連接超時時間 requestConfigBuilder.setConnectionRequestTimeout(500); //讀超時時間(等待數據超時時間) requestConfigBuilder.setSocketTimeout(2000); //該 API 在4.4中已過時, 可以通過ConnectionConfig中設置closeExpiredConnections和closeIdleConnections來關閉 //確保獲取到的連接都是可用連接 requestConfigBuilder.setStaleConnectionCheckEnabled(true); //確定是否應自動處理身份驗證 requestConfigBuilder.setAuthenticationEnabled(true); //確定循環重定向(重定向到相同位置)是否應該重定向 requestConfigBuilder.setCircularRedirectsAllowed(false); //重定向的最大數目。對重定向次數的限制是為了防止無限循環 requestConfigBuilder.setMaxRedirects(5); //確定是否應拒絕相對重定向。HTTP規范要求位置值是一個絕對URI requestConfigBuilder.setRelativeRedirectsAllowed(true); //確定用於HTTP狀態管理的cookie規范的名稱 requestConfigBuilder.setCookieSpec(""); //返回用於請求執行的本地地址。在具有多個網絡接口的計算機上,此參數可用於選擇其中的網絡接口連接產生。 requestConfigBuilder.setLocalAddress(); //代理配置 requestConfigBuilder.setProxy(); //在使用代理主機進行身份驗證時,確定支持的身份驗證方案的優先順序。 requestConfigBuilder.setProxyPreferredAuthSchemes(); //在使用目標主機進行身份驗證時,確定受支持的身份驗證模式的首選項順序 requestConfigBuilder.setTargetPreferredAuthSchemes(); RequestConfig requestConfig = requestConfigBuilder.build(); httpClientBuilder.setDefaultRequestConfig(requestConfig);
setDefaultSocketConfig()
HttpClientBuilder httpClientBuilder = HttpClients.custom(); SocketConfig.Builder socketConfigBuilder = SocketConfig.custom(); //是否立即發送數據,設置為true會關閉Socket緩沖,默認為false socketConfigBuilder.setTcpNoDelay(true); //是否可以在一個進程關閉Socket后,即使它還沒有釋放端口,其它進程還可以立即重用端口 socketConfigBuilder.setSoReuseAddress(true); //接收數據的等待超時時間,單位ms socketConfigBuilder.setSoTimeout(500); //關閉Socket時,要么發送完所有數據,要么等待60s后,就關閉連接,此時socket.close()是阻塞的 socketConfigBuilder.setSoLinger(60); //開啟監視TCP連接是否有效 socketConfigBuilder.setSoKeepAlive(true); //backlog, 設置容量限制功能,避免太多的客戶端socket占用太多服務器資源。 socketConfigBuilder.setBacklogSize(100); //接收緩沖區 socketConfigBuilder.setRcvBufSize(8192); //發送緩沖區 socketConfigBuilder.setSndBufSize(8192); //決定如果網絡上仍然有數據向舊的ServerSocket傳輸數據, //是否允許新的ServerSocket綁定到與舊的ServerSocket同樣的端口上。 //SO_REUSEADDR選項的默認值與操作系統有關,在某些操作系統中,允許重用端口,而在某些操作系統中不允許重用端口。 socketConfigBuilder.setSoReuseAddress(true); SocketConfig socketConfig = socketConfigBuilder.build(); httpClientBuilder.setDefaultSocketConfig(socketConfig);
setConnectionManager()
HttpClientBuilder httpClientBuilder = HttpClients.custom(); /** * 連接池管理器 * HttpClientConnectionManager -- 接口 * BasicHttpClientConnectionManager -- 實現類, 線程安全 * PoolingHttpClientConnectionManager -- 實現類, 線程安全, 一般使用它 * * 1. 當請求一個新的連接時,如果連接池有有可用的持久連接,連接管理器就會使用其中的一個,而不是再創建一個新的連接。 * PoolingHttpClientConnectionManager維護的連接數在每個路由基礎和總數上都有限制。 * 默認,每個路由基礎上的連接不超過2個,總連接數不能超過20。 * 在實際應用中,這個限制可能會太小了,尤其是當服務器也使用Http協議時。 * * 2. 當使用了請求連接池管理器后,HttpClient就可以同時執行多個線程的請求了。 * 它會根據配置來分配請求連接。如果連接池中的所有連接都被占用了,那么后續的請求就會被阻塞, * 直到有連接被釋放回連接池中。為了防止永遠阻塞的情況發生, * 我們可以把http.conn-manager.timeout的值設置成一個整數。 * 如果在超時時間內,沒有可用連接,就會拋出ConnectionPoolTimeoutException異常。 * * 3. HttpClient的實例是線程安全的,可以被多個線程共享訪問, * 但是仍舊推薦每個線程都要有自己專用實例的HttpContext。 * HttpClientContext context = HttpClientContext.create(); * * 4. 連接回收策略 * 問題:經典阻塞I/O模型的一個主要缺點就是只有當組側I/O時,socket才能對I/O事件做出反應。 * 當連接被管理器收回后,這個連接仍然存活,但是卻無法監控socket的狀態,也無法對I/O事件 * 做出反饋。如果連接被服務器端關閉了,客戶端監測不到連接的狀態變化(也就無法根據連接狀 * 態的變化,關閉本地的socket)。 * HttpClient為了緩解這一問題造成的影響,會在使用某個連接前,監測這個連接是否已經過時,如果 * 服務器端關閉了連接,那么連接就會失效。這種過時檢查並不是100%有效,並且會給每個請求 * 增加10到30毫秒額外開銷。唯一一個可行的,是建立一個監控線程,來專門回收由於長時間不活動 * 而被判定為失效的連接。這個監控線程可以周期性的調用ClientConnectionManager類 * (如:PoolingHttpClientConnectionManager)的closeExpiredConnections()方法來關閉過期的連接, * 回收連接池中被關閉的連接。它也可以選擇性的調用ClientConnectionManager類的 * closeIdleConnections()方法來關閉一段時間內不活動的連接。 * */ PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); // 連接池最大的連接數 connectionManager.setMaxTotal(200); // 默認的每個路由上最大的連接數(不能超過連接池總連接數) connectionManager.setDefaultMaxPerRoute(20); // 每個路由上最大的連接數(不能超過連接池總連接數), 優先於defaultMaxPerRoute HttpHost localhost = new HttpHost("https://www.baidu.com/", 80); connectionManager.setMaxPerRoute(new HttpRoute(localhost), 50); //空閑永久連接檢查間隔,這個牽扯的還比較多 //官方推薦使用這個來檢查永久鏈接的可用性,而不推薦每次請求的時候才去檢查(ms) connectionManager.setValidateAfterInactivity(1000); httpClientBuilder.setConnectionManager(connectionManager);
setKeepAliveStrategy()
HttpClientBuilder httpClientBuilder = HttpClients.custom(); /** * 連接存活策略 * Http規范沒有規定一個持久連接應該保持存活多久。有些Http服務器使用非標准的Keep-Alive頭消息 * 和客戶端進行交互,服務器端會保持數秒時間內保持連接。HttpClient也會利用這個頭消息。如果 * 服務器返回的響應中沒有包含Keep-Alive頭消息,HttpClient會認為這個連接可以永遠保持。然而, * 很多服務器都會在不通知客戶端的情況下,關閉一定時間內不活動的連接,來節省服務器資源。 * 在某些情況下默認的策略顯得太樂觀,我們可能需要自定義連接存活策略。 * */ ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() { public long getKeepAliveDuration(HttpResponse response, HttpContext context) { // 接收 'keep-alive' header HeaderElementIterator it = new BasicHeaderElementIterator( response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { try { return Long.parseLong(value) * 1000; } catch(NumberFormatException ignore) { } } } HttpHost target = (HttpHost) context.getAttribute( HttpClientContext.HTTP_TARGET_HOST); if ("https://www.baidu.com/".equalsIgnoreCase(target.getHostName())) { // Keep alive for 5 seconds only return 5 * 1000; } else { // otherwise keep alive for 30 seconds return 30 * 1000; } } }; httpClientBuilder.setKeepAliveStrategy(myStrategy);
setRetryHandler()
HttpClientBuilder httpClientBuilder = HttpClients.custom(); //禁用重試(參數:retryCount、requestSentRetryEnabled) //DefaultHttpRequestRetryHandler不傳任何參數, 默認是重試3次 HttpRequestRetryHandler requestRetryHandler = new DefaultHttpRequestRetryHandler(0, false); //自定義重試策略 HttpRequestRetryHandler myRequestRetryHandler = new HttpRequestRetryHandler() { public boolean retryRequest(IOException exception,int executionCount, HttpContext context) { //返回true表示重試 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; } }; httpClientBuilder.setRetryHandler(myRequestRetryHandler);