关于httpClient 使用的注意事项
用例
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
// 最大连接数-不设置默认20
connectionManager.setMaxTotal(200);
// 每个路由最大连接数-不设置默认2
connectionManager.setDefaultMaxPerRoute(100);
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(
RequestConfig.custom()
// 连接服务器(握手)的超时时间
.setConnectTimeout(3000)
// 被请求服务器响应(response)的超时时间
.setSocketTimeout(5000)
// 从连接池中获取连接的超时时间
.setConnectionRequestTimeout(3000)
.build()
).setConnectionManager(connectionManager)
.build();
三个连接超时时间
- connectionRequestTimeout:从连接池中获取连接的超时时间,超过该时间未拿到可用连接,会抛出ConnectionPoolTimeoutException: Timeout waiting for connection from pool
- connectTimeout:连接上服务器(握手成功)的时间,超出该时间抛出connect timeout
- socketTimeout:服务器返回数据(response)的时间,超过该时间抛出read timeout
连接池的大小
如果使用默认配置,连接池大小默认为20,即假设被请求接口响应时间为200ms,我们的程序每秒只能完成100个请求响应,剩余的则会陷入等待从连接池获取连接。则在某些高并发的场景中容易出现问题。
路由的大小
DefaultMaxPerRoute 是什么?
可以理解为 IP+端口的一条通路
例如连接池大小(MaxTotal)设置为300,路由连接数设置为200(DefaultMaxPerRoute),对于www.a.com与www.b.com两个路由每个路由的连接数不能超过200来说,两个路由的总连接数不能超过300。
关于setDefaultMaxPerRoute测试
// 服务端测试接口
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("doTest")
public Object test(){
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3));
return "ok";
}
@RequestMapping("doTest1")
public Object tes1t(){
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3));
return "ok";
}
}
// 客户端测试方法
public class ATest {
public static Logger log = LoggerFactory.getLogger(ATest.class);
public static void main(String[] args) {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
// 最大连接数-不设置默认20
connectionManager.setMaxTotal(2);
// 每个路由最大连接数-不设置默认2
connectionManager.setDefaultMaxPerRoute(1);
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(
RequestConfig.custom()
// 连接服务器(握手)的超时时间
.setConnectTimeout(3000)
// 被请求服务器响应(response)的超时时间
.setSocketTimeout(50000)
// 从连接池中获取连接的超时时间
.setConnectionRequestTimeout(50000)
.build()
).setConnectionManager(connectionManager)
.build();
new Thread(()->{
doGet(httpClient,"http://localhost:8080/test/doTest");
}).start();
new Thread(()->{
doGet(httpClient,"http://localhost:8080/test/doTest1");
}).start();
}
private static void doGet(CloseableHttpClient httpClient,String url) {
HttpGet httpGet = new HttpGet(url);
try {
log.info("start");
CloseableHttpResponse response = httpClient.execute(httpGet);
log.info("message={}", EntityUtils.toString(response.getEntity()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
结果
配置setDefaultMaxPerRoute value为1 ,即同一个ip+端口只能存在一个连接,服务端模拟请求耗时3s,thread-0在请求3s后完成获得响应,并且我们可以看到thread-1实际是在等待thread-0完成请求释放连接后才开始请求,在请求6s后才获得响应