CloseableHttpClient的個性化配置


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);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM