「这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战」
1. http请求存在的问题
Http1.0连接是无状态的,短链接,每次请求时建立连接、请求结束后断开连接。也可以在请求头中设置Connection:keep-alive
来实现Http的长连接。
Http连接的建立和关闭本质上就是TCP连接的建立和关闭,在建立和关闭时会有三次握手和四次挥手的过程,占用资源多、开销大。
为了降低频繁建立和断开Http连接对资源的消耗,就需要使用Http连接池来管理Http的连接,并保证一定数量的长连接,且系统能拥有更高的并发性能。
2. 创建http连接池
HttpClient中创建http连接池的方式是使用定义的PoolingHttpClientConnectionManager
类,该类实现了HttpClientConnectionManager
接口和ConnPoolControl
接口。
- 初始化连接池对象时,可以在构造函数中传入时间值参数,指定连接存活时间
- 使用对象的setMaxTotal()方法来设置连接池的最大连接数
- 使用对象的setDefaultMaxPerRoute()方法设置路由最大连接数
/** http连接池对象 */
private static PoolingHttpClientConnectionManager cm;
/** * 初始化连接池 */
public static void init(){
//创建http连接池,可以同时指定连接超时时间
cm = new PoolingHttpClientConnectionManager(60000, TimeUnit.MILLISECONDS);
//最多同时连接20个请求
cm.setMaxTotal(20);
//每个路由最大连接数,路由指IP+PORT或者域名
cm.setDefaultMaxPerRoute(50);
}
3. 通过连接池获取httpClient
HttpClient中http请求的连接由HttpClient对象发起,加入连接池后就可以从连接池中获取HttpClient对象信息。
/** * 从连接池中获取httpClient连接 */
public static CloseableHttpClient getHttpClient(PoolingHttpClientConnectionManager cm){
//设置请求参数配置,创建连接时间、从连接池获取连接时间、数据传输时间、是否测试连接可用、构建配置对象
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(1000)
.setConnectionRequestTimeout(3000)
.setSocketTimeout(10 * 1000)
.setStaleConnectionCheckEnabled(true)
.build();
//创建httpClient时从连接池中获取,并设置连接失败时自动重试(也可以自定义重试策略:setRetryHandler())
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(cm)
.disableAutomaticRetries()
.build();
return httpClient;
}
在使用http连接池获取httpClient连接时,需要配置相关参数信息:
- 定义
RequestConfig
对象来设置请求时的时间参数,如连接创建时间、从连接池中获取连接时间、数据传输请求时间、是否请求前测试可用性等RequestConfig
对象可以为整个httpClient设置,也可以针对GET请求和POST请求分别设置
- 接收一个
CloseableHttpClient
对象作为httpClient,使用建造者模式获取时需要设置连接对象的配置信息、失败重连信息以及来源的连接池信息。
4. 设定请求类型并执行请求
创建httpClient连接后,之后的请求执行流程和单独创建httpClient对象的请求流程基本一致。
需要注意的就是在使用连接池获取的httpClient请求完成后,如果希望将连接释放到连接池中,就不能使用close()方法关闭,而是调用EntityUtils.consume()方法。
/** * 执行请求 */
public static void doGetRequest(CloseableHttpClient httpClient,String url){
//创建http请求类型
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse httpResponse = null;
try {
httpResponse = httpClient.execute(httpGet);
if(200 == httpResponse.getStatusLine().getStatusCode()){
System.out.println("请求返回数据内容:" + EntityUtils.toString(httpResponse.getEntity()));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(httpResponse != null){
//执行httpResponse.close关闭对象会关闭连接池,
//如果需要将连接释放到连接池,可以使用EntityUtils.consume()方法
try {
EntityUtils.consume(httpResponse.getEntity());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5. 连接池状态观察
使用连接池获取httpClient连接发起请求时,可以通过连接池中连接的存活等信息来观察使用情况。
当使用单线程执行多次http请求时,连接池中始终保持一个连接,因为单线程总是执行完上一个才会开始下一个请求的执行。
String url = "http://www.baidu.com";
for(int i = 0; i < 5; i++){
//连接池中获取httpClient
CloseableHttpClient httpClient = getHttpClient(cm);
//单线程执行请求
doGetRequest(httpClient, url);
System.out.println(cm.getTotalStats());
}
- 因为是单线程,线程池的状态会打印5次,每次结果都是
//[leased: 0; pending: 0; available: 1; max: 20]
而使用多线程执行http请求时,每个请求开启一个线程后,最终执行完成并将连接释放至连接池后,连接池中存活了请求中并存的最大连接数。
String url = "http://www.baidu.com";
for(int i = 0; i < 5; i++){
//获取httpClient
CloseableHttpClient httpClient = getHttpClient(cm);
//每个请求开启一个单独的线程
doGetRequestMulitThread(httpClient,url);
System.out.println(cm.getTotalStats());
}
- 最终只会打印一次线程池状态,结果为
[leased: 0; pending: 0; available: 5; max: 20]
今天的文章HttpClient使用连接池分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16343.html