CloseableHttpClient的个性化配置借助 HttpClientBuilder来完成,HttpClientBuilder线程不安全。
调用 HttpClientBuilder实例方法除了build方法外,返回都是其本身,同时HttpClientBuilder 包含了诸多属性,并提供了对外的设置方法。
/** * useSystemProperties() 是否读取系统属性, 调用该方法则可以读取 * disableAuthCaching() 是否禁用缓, 调用该方法则禁用 * disableRedirectHandling() 是否禁用重定向, 调用该方法则禁用 * disableContentCompression() 是否禁用内容压缩, 调用该方法则禁用 * disableAutomaticRetries() 是否禁用自动重试, 调用该方法则禁用 * disableCookieManagement() 是否禁用cookie管理, 调用该方法则禁用 * disableConnectionState() 是否禁用连接状态, 调用该方法则禁用 * * setMaxConnTotal() 全局最大维持的连接数 * setMaxConnPerRoute() 单个Route最大连接数 * evictIdleConnections() 设置最长空闲时间及空闲时间的单位, * 调用此方法会设置evictIdleConnections=true, 表示开启独立线程清理空闲连接 * evictExpiredConnections() 开启独立线程清理过期连接 * */
1.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);
2. 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);
3. 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);
4. 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);
5. 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);
6. 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);