在windows环境下,使用Process Explorer查看链接数和链接状态。java
package http.connectionPool; import org.apache.http.HeaderElement; import org.apache.http.HeaderElementIterator; import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.SocketConfig; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultClientConnectionReuseStrategy; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeaderElementIterator; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import java.io.IOException; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class TestConnectionPool { private static CloseableHttpClient httpClient; private static ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); private static PoolingHttpClientConnectionManager cm; static{ cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(100); cm.setDefaultMaxPerRoute(100); SocketConfig socketConfig = SocketConfig.custom() .setTcpNoDelay(true) //是否当即发送数据,设置为true会关闭Socket缓冲,默认为false .setSoReuseAddress(true) //是否能够在一个进程关闭Socket后,即便它尚未释放端口,其它进程还能够当即重用端口 .setSoTimeout(5000) //接收数据的等待超时时间,单位ms //注意,此处会致使问题!!!!!!!!!!!!后文分析,会致使长时间持有链接池的锁 .setSoLinger(60) //关闭Socket时,要么发送完全部数据,要么等待60s后,就关闭链接,此时socket.close()是阻塞的 .setSoKeepAlive(true) //开启监视TCP链接是否有效 .build(); cm.setDefaultSocketConfig(socketConfig); ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() { public long getKeepAliveDuration(HttpResponse response, HttpContext context) { // Honor '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) { } } } return 10 * 1000; } }; httpClient = HttpClients.custom().setConnectionManager(cm) .setKeepAliveStrategy(myStrategy) .evictExpiredConnections() .evictIdleConnections(30,TimeUnit.SECONDS) .setConnectionReuseStrategy(DefaultClientConnectionReuseStrategy.INSTANCE) .build(); //定时打印链接池状态 executorService.scheduleAtFixedRate(() -> { System.out.println(cm.getTotalStats()); },100L,1000L, TimeUnit.MILLISECONDS); } //测试 public static void main(String[] args) { Random r = new Random(); for(int i = 0;i<100000;i++){ int n = r.nextInt(5); for(int t = 0;t<n;t++){ new Thread(()->{ HttpGet get = new HttpGet("https://sales.test.cn/group/products?id=22"); HttpContext context = HttpClientContext.create(); CloseableHttpResponse response = null; try{ response = httpClient.execute(get, context); //必定有这行代码,这行触发归还链接到链接池 EntityUtils.consume(response.getEntity()); } catch (Exception e){ e.printStackTrace(); } finally { if(response!=null){ try { response.close(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); } try { Thread.sleep(200L); } catch (InterruptedException e) { e.printStackTrace(); } } } }
使用jdk自带工具jps查看进程号,而后在Process Explorer中查看链接。 官方文档: In order to ensure proper release of system resources one must close either the content stream associated with the entity or the response itself.The difference between closing the content stream and closing the response is that the former will attempt to keep the underlying connection alive by consuming the entity content while the latter immediately shuts down and discards the connection. 大意就是为了释放系统资源,要么关闭response content stream或者关闭response,二者的区别是关闭流会尝试保持链接可是关闭response会马上终断链接。源码中也能看到对应的操做。apache