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

 

posted @ 2020-01-15 10:17  codedot  阅读(6228)  评论(0编辑  收藏  举报