在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