先看示例代码,代码有点长,不过大家理清思路来看应该不是问题:
import java.io.IOException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; import org.apache.http.pool.PoolStats; import org.apache.http.util.EntityUtils; /* * httpclient版本: 4.5.2 * httpclient的使用始于HttpClientBuilder */ public class TestHttpClientPool { public static void main(String[] args) throws Exception{ HttpClientBuilder builder=HttpClientBuilder.create(); /*一、为HttpClientBuilder设置绕过不安全的https证书 */ Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", trustHttpsCertificates()) .build(); /*二、为HttpClientBuilder设置PoolingHttpClientConnectionManager */ PoolingHttpClientConnectionManager cm=new PoolingHttpClientConnectionManager(registry); cm.setMaxTotal(200);//维护的httpclientConnection总数 //每一个route的最大连接数,route可以理解为一个主机,如http://www.roadjava.com/2.html //和http://www.roadjava.com/1.html是一个主机 cm.setDefaultMaxPerRoute(20); builder.setConnectionManager(cm); /*三、为HttpClientBuilder设置从连接池获取连接的超时时间、连接超时时间、获取数据响应超时时间 */ RequestConfig requestConfig=RequestConfig.custom(). setConnectionRequestTimeout(5000). setConnectTimeout(5000). setSocketTimeout(5000).build(); builder.setDefaultRequestConfig(requestConfig); /* * 四、设置默认的header */ List<BasicHeader> basicHeaders=new ArrayList<>(); BasicHeader basicHeader=new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"); basicHeaders.add(basicHeader); builder.setDefaultHeaders(basicHeaders); PoolStats totalStats = cm.getTotalStats(); System.out.println("最大:"+totalStats.getMax()); System.out.println("占用的:"+totalStats.getLeased()); System.out.println("可用的:"+totalStats.getAvailable()); for(int i=0;i<30;i++){ //HttpClients.createDefault();内部调用的是HttpClientBuilder.create().build(); //配置是采用的默认配置 CloseableHttpClient client = builder.build(); String url="http://www.roadjava.com/"; HttpGet httpGet=new HttpGet(url); //处理响应部分 CloseableHttpResponse response =null; try { response = client.execute(httpGet); HttpEntity entity = response.getEntity(); // System.out.println("获取到的内容:"+EntityUtils.toString(entity,"UTF-8")); PoolStats totalStats2 = cm.getTotalStats(); System.out.println("最大:"+totalStats2.getMax()); System.out.println("占用的:"+totalStats2.getLeased()); System.out.println("可用的:"+totalStats2.getAvailable()); EntityUtils.consume(entity);//关闭entity } catch (Exception e) { e.printStackTrace(); } finally{ //不要调用这句,如果调用了就把连接从连接池真正销毁了,连接池使用失去了意义 // if (client!=null) { // try {client.close();} catch (IOException e) {e.printStackTrace();} // } if (response!=null) { try {response.close();} catch (IOException e) {e.printStackTrace();} } } } PoolStats totalStats3 = cm.getTotalStats(); System.out.println("最大:"+totalStats3.getMax()); System.out.println("占用的:"+totalStats3.getLeased()); System.out.println("可用的:"+totalStats3.getAvailable()); } //创建并返回SSLConnectionSocketFactory对象 public static ConnectionSocketFactory trustHttpsCertificates() throws Exception { SSLConnectionSocketFactory socketFactory = null; TrustManager[] trustAllCerts = new TrustManager[1]; TrustManager tm = new myTM(); trustAllCerts[0] = tm; SSLContext sc = null; try { sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, null); socketFactory = new SSLConnectionSocketFactory(sc, NoopHostnameVerifier.INSTANCE); } catch (Exception e) { e.printStackTrace(); } return socketFactory; } static class myTM implements TrustManager, X509TrustManager { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkServerTrusted(X509Certificate[] certs, String authType) { } public void checkClientTrusted(X509Certificate[] certs, String authType) { } } }运行结果:
从httpclient使用连接池可以看到PoolingHttpClientConnectionManager之后,我们可以看到,针对同一个route,httpclient不再是每次请求都新建一个连接(httpconnection)了,而是从连接池里面找可以复用的连接,这样就省去了建立连接以及关闭连接时的耗时。
因为我设置的每个route可用的并发数是20,如果我把
EntityUtils.consume(entity);//关闭entity以及
if (response!=null) { try {response.close();} catch (IOException e) {e.printStackTrace();} }注释掉,也就意味着,连接使用之后,并没有放到连接池的AvailableSet里面,表示这个连接一直处于占用状态,那么我们预期在请求到20次的时候它就会从连接池里面无法获取到可用连接,又因为我设置了从连接池里面获取可用连接的等待超时时间是5秒(setConnectionRequestTimeout(5000)),在等待5秒之后就应该报错,是不是这样呢?如下图,正如我们预期,报了org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool这个错误:
这就是httpclient里面连接池PoolingHttpClientConnectionManager的使用方法。