连接池管理器一般使用PoolingHttpClientConnectionManager,它管理着连接池,可以同时为很多线程提供http连接请求。Connections are pooled on a per route basis.当请求一个新的连接时,如果连接池有有可用的持久连接,连接管理器就会使用其中的一个,而不是再创建一个新的连接。PoolingHttpClientConnectionManager
维护的连接数在每个路由基础和总数上都有限制。默认,每个路由基础上的连接不超过2个,总连接数不能超过20。在实际应用中,这个限制可能会太小了,尤其是当服务器也使用Http协议时。
多线程请求执行
当使用了请求连接池管理器(比如PoolingClientConnectionManager
)后,HttpClient就可以同时执行多个线程的请求了。PoolingClientConnectionManager
会根据它的配置来分配请求连接。如果连接池中的所有连接都被占用了,那么后续的请求就会被阻塞,直到有连接被释放回连接池中。为了防止永远阻塞的情况发生,我们可以把http.conn-manager.timeout
的值设置成一个整数。如果在超时时间内,没有可用连接,就会抛出ConnectionPoolTimeoutException
异常。
HttpClient的实例是线程安全的,可以被多个线程共享访问,但是仍旧推荐每个线程都要有自己专用实例的HttpContext。
官方示例
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// 将最大连接数增加到200
cm.setMaxTotal(200);
// 将每个路由基础的连接增加到20
cm.setDefaultMaxPerRoute(20);
//将目标主机的最大连接数增加到50
HttpHost localhost = new HttpHost("www.yeetrack.com", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
在自己项目中也用到了连接池管理器,处理了普通的http请求与忽略https证书。这货很灵活,代码仅供参考,毕竟只是为了方便项目。
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
/**
* HttpClient连接池<br/>
* Author:杨杰超<br/>
* Date:2018年3月12日 下午3:08:19 <br/>
* Copyright (c) 2018, yangjiechao@dingtalk.com All Rights Reserved.<br/>
*
*/
public class GlobalHttpClientConnectionManager {
// 设置访问协议
private PoolingHttpClientConnectionManager connMrg = null;
// 超时参数
private long timeToLive = 10;
// 超时参数单位
private TimeUnit tunit = TimeUnit.SECONDS;
// 设置整个连接池最大连接数
private int maxTotal = 256;
// 单个路由最大连接数
private int defaultMaxPerRoute = 64;
/**
* 初始化
*/
public GlobalHttpClientConnectionManager() {
connMrg = new PoolingHttpClientConnectionManager(timeToLive, tunit);
connMrg.setMaxTotal(maxTotal);
connMrg.setDefaultMaxPerRoute(defaultMaxPerRoute);
}
/**
* 初始化
*
* @param timeToLive
* 超时参数
* @param tunit
* 超时单位
* @param maxTotal
* 连接池最大连接数
* @param defaultMaxPerRoute
* 主机对MaxTotal的一个细分,比如:MaxtTotal=256
* DefaultMaxPerRoute=64,而我只连接到http://www.baidu.com时,
* 到这个主机的并发最多只有64而不是128
*/
public GlobalHttpClientConnectionManager(long timeToLive, TimeUnit tunit, int maxTotal, int defaultMaxPerRoute) {
this.timeToLive = timeToLive;
this.tunit = tunit;
this.maxTotal = maxTotal;
this.defaultMaxPerRoute = defaultMaxPerRoute;
}
public PoolingHttpClientConnectionManager getManager() {
return connMrg;
}
public CloseableHttpClient getHttpClient() {
return HttpClients.custom().setConnectionManager(connMrg).build();
}
/**
* 获取忽略证书的HttpClient
*
* @return
*/
public CloseableHttpClient getHttpsClient() {
return getHttpsClient(createSSLConnSocketFactory());
}
public CloseableHttpClient getHttpsClient(SSLConnectionSocketFactory sslSocketFactory) {
return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).setConnectionManager(connMrg).build();
}
/**
* 创建SSL安全连接
*
* @return
*/
private SSLConnectionSocketFactory createSSLConnSocketFactory() {
SSLConnectionSocketFactory sslsf = null;
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 信任所有
return true;
}
}).build();
sslsf = new SSLConnectionSocketFactory(sslContext);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
return sslsf;
}
}
然而我们在使用的时候,需求是需要连接多个不同的服务器,所以需要多个PoolingHttpClientConnectionManager。然后再上一份代码~
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.apache.http.impl.client.CloseableHttpClient;
/**
* HttpClient连接池工厂<br/>
* Author:杨杰超<br/>
* Date:2018年3月12日 下午3:16:18 <br/>
* Copyright (c) 2018, yangjiechao@dingtalk.com All Rights Reserved.<br/>
*
*/
public class GlobalHttpClientConnectionManagerFactory {
// 超时参数
public static final long timeToLive = 10;
// 超时参数单位
public static final TimeUnit tunit = TimeUnit.SECONDS;
// 设置整个连接池最大连接数
public static final int maxTotal = 256;
// 单个路由最大连接数
public static final int defaultMaxPerRoute = 256;
public static Map<String, GlobalHttpClientConnectionManager> cache = new ConcurrentHashMap<>();
public static GlobalHttpClientConnectionManager getGlobalHttpClientConnectionManager(String key) {
return getGlobalHttpClientConnectionManager(key, timeToLive, tunit, maxTotal, defaultMaxPerRoute);
}
public static GlobalHttpClientConnectionManager getGlobalHttpClientConnectionManager(String key, long timeToLive,
TimeUnit tunit, int maxTotal, int defaultMaxPerRoute) {
GlobalHttpClientConnectionManager manager = cache.get(key);
if (null == manager) {
synchronized (cache) {
manager = cache.get(key);
if (null == manager) {
manager = new GlobalHttpClientConnectionManager(timeToLive, tunit, maxTotal, defaultMaxPerRoute);
cache.put(key, manager);
}
}
}
return manager;
}
public static CloseableHttpClient getCloseableHttpClient(String key) {
return getCloseableHttpClient(key, timeToLive, tunit, maxTotal, defaultMaxPerRoute);
}
public static CloseableHttpClient getCloseableHttpClient(String key, long timeToLive, TimeUnit tunit, int maxTotal,
int defaultMaxPerRoute) {
return getGlobalHttpClientConnectionManager(key, timeToLive, tunit, maxTotal, defaultMaxPerRoute)
.getHttpClient();
}
}
这货版本更新变化得太多了,加上jsoup做抓取真不错~