先看示例代码,代码有点长,不过大家理清思路来看应该不是问题:
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的使用方法。