在我们日常的开发中经常遇到通过http 请求第三方接口。在我们项目中使用一个基于HttpClient-4.4.1封装的一个工具类也就是com.arronlong.httpclientutil 目前该作者实现最新版本是1.0.4 该工具类git地址是:git地址 maven 地址是:maven 地址
- 简单使用
String URL = "http://localhost/common/pool?pool=test";
HttpConfig httpConfig = HttpConfig.custom().timeout(20000);//最新版本可以在这里配置连接超时间和读取数据时间
httpConfig.url(URL).headers(HttpHeader.custom().build());
try {
String rep = HttpClientUtil.get(httpConfig);//HttpClientUtil 通过静态代码块初始化了http、https client
System.out.println("test====>" + i);
} catch (HttpProcessException e) {
// TODO Auto-generated catch block
System.err.println("http请求出现异常:" + e.getMessage());
}
目前这样使用没有任何问题。但是随着业务上涨、用户量上升,高并发随之而来。突然有天在几千并发下特别是调用第三方耗时的接口出现了我们常见的一个异常如下:
http请求出现异常:org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
由此可见请求连接一直等待直到等待超时也没拿到连接。
- 测试猜想
实现思路就是通过循环加异步同时异步代码中sleep 模拟耗时操作。伪代码如下:
@GetMapping("testHttp")
public ResponseEntity liveness() {
long s= System.currentTimeMillis();
for(int i=0; i< 10000;i++) {
task.TestHttp(i);
}
System.err.println("结束时间:" + (System.currentTimeMillis() - s));
return ResponseEntity.ok().body("res");
}
@Override
@Async
public void TestHttp(int i) {
// TODO Auto-generated method stub
String URL = "http://localhost/common/pool?pool=test";
HttpConfig httpConfig = HttpConfig.custom().timeout(20000);
httpConfig.url(URL).headers(HttpHeader.custom().build());
try {
String rep = HttpClientUtil.get(httpConfig);
System.out.println("test====>" + i);
} catch (HttpProcessException e) {
// TODO Auto-generated catch block
System.err.println("http请求出现异常:" + e.getMessage());
}
}
- 效果图
- 解决方案
通过验证知道高并发下得不到client 连接对象。通过查看工具类源码发现
static{
try {
client4HTTP = HCB.custom().build();
client4HTTPS = HCB.custom().ssl().build();
} catch (HttpProcessException e) {
Utils.errorException("创建https协议的HttpClient对象出错:{}", e);
}
}
继续 看HCB 中有
/**
* 设置连接池(默认开启https)
*
* @param maxTotal 最大连接数
* @param defaultMaxPerRoute 每个路由默认连接数
* @return 返回当前对象
* @throws HttpProcessException http处理异常
*/
public HCB pool(int maxTotal, int defaultMaxPerRoute) throws HttpProcessException{
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", ssls.getSSLCONNSF(sslpv)).build();
//设置连接池大小
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connManager.setMaxTotal(maxTotal);// Increase max total connection to $maxTotal
connManager.setDefaultMaxPerRoute(defaultMaxPerRoute);// Increase default max connection per route to $defaultMaxPerRoute
//connManager.setMaxPerRoute(route, max);// Increase max connections for $route(eg:localhost:80) to 50
isSetPool=true;
return (HCB) this.setConnectionManager(connManager);
}
默认不开启 他pool 最大是20 所以我们根据自己业务来设置pool 大小。我们通过测试最大连接数设置200 路由设置100. 经过测试10000并发不会出现等待超时问题。结合timeout 来实现测试。可能不同timeout 并发有不一样。最终我们可以修改client4HTTP = HCB.custom().build(); 加入pool设置。代码如下:
/** 最大连接数 **/
private final static int MAX_TOTAL = 200;
/** 路由最大数 **/
private final static int DEFAULT_MAX_PERROUTE = 100;
static{
try {
client4HTTP = HCB.custom().pool(MAX_TOTAL,DEFAULT_MAX_PERROUTE).build();
client4HTTPS = HCB.custom().pool(MAX_TOTAL, DEFAULT_MAX_PERROUTE).ssl().build();
} catch (HttpProcessException e) {
Utils.errorException("创建https协议的HttpClient对象出错:{}", e);
}
}
- 最终10000并发测试