连接池超时配置_HttpClient连接池的一些思考

79cd0f952e78876a89ade4958e690871.png

前言

使用apache的httpclient进行http的交互处理已经很长时间了,而httpclient实例则使用了http连接池,想必大家也没有关心过连接池的管理。事实上,通过分析httpclient源码,发现它很优雅地隐藏了所有的连接池管理细节,开发者完全不用花太多时间去思考连接池的问题。

Apache官网例子

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget);
try {HttpEntity entity = response.getEntity();if (entity != null) {long len = entity.getContentLength();if (len != -1 && len < 2048) {System.out.println(EntityUtils.toString(entity));} else {// Stream content out}}
} finally {response.close();
}

HttpClient及其连接池配置

  • 整个线程池中最大连接数 MAX_CONNECTION_TOTAL = 800
  • 路由到某台主机最大并发数,是MAX_CONNECTION_TOTAL(整个线程池中最大连接数)的一个细分 ROUTE_MAX_COUNT = 500
  • 重试次数,防止失败情况 RETRY_COUNT = 3
  • 客户端和服务器建立连接的超时时间 CONNECTION_TIME_OUT = 5000
  • 客户端从服务器读取数据的超时时间 READ_TIME_OUT = 7000
  • 从连接池中获取连接的超时时间 CONNECTION_REQUEST_TIME_OUT = 5000
  • 连接空闲超时,清楚闲置的连接 CONNECTION_IDLE_TIME_OUT = 5000
  • 连接保持存活时间 DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000

MaxtTotal和DefaultMaxPerRoute的区别

  • MaxtTotal是整个池子的大小;
  • DefaultMaxPerRoute是根据连接到的主机对MaxTotal的一个细分;

比如:MaxtTotal=400,DefaultMaxPerRoute=200,而我只连接到http://hjzgg.com时,到这个主机的并发最多只有200;而不是400;而我连接到http://qyxjj.com 和 http://httls.com时,到每个主机的并发最多只有200;即加起来是400(但不能超过400)。所以起作用的设置是DefaultMaxPerRoute。

HttpClient连接池模型

91ec13803b9936c0c0c78a7c582454cf.png

HttpClient从连接池中获取连接分析

org.apache.http.pool.AbstractConnPool

private E getPoolEntryBlocking(final T route, final Object state,final long timeout, final TimeUnit tunit,final PoolEntryFuture<E> future)throws IOException, InterruptedException, TimeoutException {Date deadline = null;if (timeout > 0) {deadline = new Date(System.currentTimeMillis() + tunit.toMillis(timeout));}this.lock.lock();try {final RouteSpecificPool<T, C, E> pool = getPool(route);//这是每一个路由细分出来的连接池E entry = null;while (entry == null) {Asserts.check(!this.isShutDown, "Connection pool shut down");//从池子中获取一个可用连接并返回for (;;) {entry = pool.getFree(state);if (entry == null) {break;}if (entry.isExpired(System.currentTimeMillis())) {entry.close();} else if (this.validateAfterInactivity > 0) {if (entry.getUpdated() + this.validateAfterInactivity <= System.currentTimeMillis()) {if (!validate(entry)) {entry.close();}}}if (entry.isClosed()) {this.available.remove(entry);pool.free(entry, false);} else {break;}}if (entry != null) {this.available.remove(entry);this.leased.add(entry);onReuse(entry);return entry;}//创建新的连接// New connection is neededfinal int maxPerRoute = getMax(route);//获取当前路由最大并发数// Shrink the pool prior to allocating a new connectionfinal int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);if (excess > 0) {//如果当前路由对应的连接池的连接超过最大路由并发数,获取到最后使用的一次连接,释放掉for (int i = 0; i < excess; i++) {final E lastUsed = pool.getLastUsed();if (lastUsed == null) {break;}lastUsed.close();this.available.remove(lastUsed);pool.remove(lastUsed);}}//尝试创建新的连接 if (pool.getAllocatedCount() < maxPerRoute) {//当前路由对应的连接池可用空闲连接数+当前路由对应的连接池已用连接数 < 当前路由对应的连接池最大并发数final int totalUsed = this.leased.size();final int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);if (freeCapacity > 0) {final int totalAvailable = this.available.size();if (totalAvailable > freeCapacity - 1) {//线程池中可用空闲连接数 > (线程池中最大连接数 - 线程池中已用连接数 - 1)if (!this.available.isEmpty()) {final E lastUsed = this.available.removeLast();lastUsed.close();final RouteSpecificPool<T, C, E> otherpool = getPool(lastUsed.getRoute());otherpool.remove(lastUsed);}}final C conn = this.connFactory.create(route);entry = pool.add(conn);this.leased.add(entry);return entry;}}boolean success = false;try {pool.queue(future);this.pending.add(future);success = future.await(deadline);} finally {// In case of 'success', we were woken up by the// connection pool and should now have a connection// waiting for us, or else we're shutting down.// Just continue in the loop, both cases are checked.pool.unqueue(future);this.pending.remove(future);}// check for spurious wakeup vs. timeoutif (!success && (deadline != null) &&(deadline.getTime() <= System.currentTimeMillis())) {break;}}throw new TimeoutException("Timeout waiting for connection");} finally {this.lock.unlock();}
}

连接重用和保持策略

http的长连接复用, 其判定规则主要分两类。
1. http协议支持+请求/响应header指定
2. 一次交互处理的完整性(响应内容消费干净)
对于前者, httpclient引入了ConnectionReuseStrategy来处理, 默认的采用如下的约定:

  • HTTP/1.0通过在Header中添加Connection:Keep-Alive来表示支持长连接。
  • HTTP/1.1默认支持长连接, 除非在Header中显式指定Connection:Close, 才被视为短连接模式。

HttpClientBuilder创建MainClientExec

24521f7681e948f45bd281e2c8ec2f33.png

ConnectionReuseStrategy(连接重用策略)

org.apache.http.impl.client.DefaultClientConnectionReuseStrategy

b8f84e94e51301db9aa55d081617ba80.png

MainClientExec处理连接

处理完请求后,获取到response,通过ConnectionReuseStrategy判断连接是否可重用,如果是通过ConnectionKeepAliveStrategy获取到连接最长有效时间,并设置连接可重用标记。

dfb425780e422bc3f543e4ca75b81536.png

连接重用判断逻辑

  • request首部中包含Connection:Close,不复用
  • response中Content-Length长度设置不正确,不复用
  • response首部包含Connection:Close,不复用
  • reponse首部包含Connection:Keep-Alive,复用
  • 都没命中的情况下,如果HTTP版本高于1.0则复用

更多参考:https://www.cnblogs.com/mumuxinfei/p/9121829.html

连接释放原理分析

HttpClientBuilder会构建一个InternalHttpClient实例,也是CloseableHttpClient实例。InternalHttpClient的doExecute方法来完成一次request的执行。

2755c8b433083349b54c1455bc91f4cb.png

会继续调用MainClientExec的execute方法,通过连接池管理者获取连接(HttpClientConnection)。

1387916efb9da061f4e2862b55c8369d.png

构建ConnectionHolder类型对象,传递连接池管理者对象和当前连接对象。

234adfbd2974a40c7864c8173b058d5a.png

请求执行完返回HttpResponse类型对象,然后包装成HttpResponseProxy对象(是CloseableHttpResponse实例)返回。

eec21e8726f45d1d0427807d024c027b.png

CloseableHttpClient类其中一个execute方法如下,finally方法中会调用HttpResponseProxy对象的close方法释放连接。

0b180503254387eb9e286f325f138d96.png

a314ba3e7f7b99c250e19b0cbe35ce05.png

最终调用ConnectionHolder的releaseConnection方法释放连接。

a85b0c272aada8ba2be77f98debe4a2a.png

414b2b99a37ac7e8ee9157af8fbfa4db.png

CloseableHttpClient类另一个execute方法如下,返回一个HttpResponseProxy对象(是CloseableHttpResponse实例)。

6c270f9ee337d52673c921e23c082baf.png

这种情况下调用者获取了HttpResponseProxy对象,可以直接拿到HttpEntity对象。大家关心的就是操作完HttpEntity对象,使用完InputStream到底需不需要手动关闭流呢?

a366202abcb42c8ff3140d42ffc7809d.png

其实调用者不需要手动关闭流,因为HttpResponseProxy构造方法里有增强HttpEntity的处理方法,如下。

a86e3abd2ba30aaf274f6765c4fb842b.png

调用者最终拿到的HttpEntity对象是ResponseEntityProxy实例。

cb4ea1e4822a29d0da7923a27437443b.png

ResponseEntityProxy重写了获取InputStream的方法,返回的是EofSensorInputStream类型的InputStream对象。

63439c90dd77c27eaa58c20e9afc7555.png

EofSensorInputStream对象每次读取都会调用checkEOF方法,判断是否已经读取完毕。

a1c7026607b6fe9fba15bd97e579c709.png

checkEOF方法会调用ResponseEntityProxy(实现了EofSensorWatcher接口)对象的eofDetected方法。

bd425a6b5ef7542cc26f5f2e7bffa26a.png

EofSensorWatcher#eofDetected方法中会释放连接并关闭流。

56f3a3762f8fb9fd61a3264926020bf5.png

综上,通过CloseableHttpClient实例处理请求,无需调用者手动释放连接。

HttpClient在Spring中应用

创建ClientHttpRequestFactory

@Bean
public ClientHttpRequestFactory clientHttpRequestFactory()throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();httpClientBuilder.setSSLContext(sslContext).setMaxConnTotal(MAX_CONNECTION_TOTAL).setMaxConnPerRoute(ROUTE_MAX_COUNT).evictIdleConnections(CONNECTION_IDLE_TIME_OUT, TimeUnit.MILLISECONDS);httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(RETRY_COUNT, true));httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());CloseableHttpClient client = httpClientBuilder.build();HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);clientHttpRequestFactory.setConnectTimeout(CONNECTION_TIME_OUT);clientHttpRequestFactory.setReadTimeout(READ_TIME_OUT);clientHttpRequestFactory.setConnectionRequestTimeout(CONNECTION_REQUEST_TIME_OUT);clientHttpRequestFactory.setBufferRequestBody(false);return clientHttpRequestFactory;
}

创建RestTemplate

@Bean
public RestTemplate restTemplate()  {RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());restTemplate.setErrorHandler(new DefaultResponseErrorHandler());// 修改StringHttpMessageConverter内容转换器restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));return restTemplate;
}

Spring官网例子

@SpringBootApplication
public class Application {private static final Logger log = LoggerFactory.getLogger(Application.class);public static void main(String args[]) {SpringApplication.run(Application.class);}@Beanpublic RestTemplate restTemplate(RestTemplateBuilder builder) {return builder.build();}@Beanpublic CommandLineRunner run(RestTemplate restTemplate) throws Exception {return args -> {Quote quote = restTemplate.getForObject("https://gturnquist-quoters.cfapps.io/api/random", Quote.class);log.info(quote.toString());};}
}

总结

Apache的HttpClient组件可谓良心之作,细细的品味一下源码可以学到很多设计模式和比编码规范。不过在阅读源码之前最好了解一下不同版本的HTTP协议,尤其是HTTP协议的Keep-Alive模式。使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服 务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。这里推荐一篇参考链接:https://www.jianshu.com/p/49551bda6619。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397836.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android界面布局错位,IOS 浏览器页面布局错位(如:点不到)的分析与解决

IOS 浏览器页面布局错位(如&#xff1a;点不到)的分析与解决IOS 浏览器软键盘的拉起与收缩、微信 IOS 浏览器底部导航条的显示与隐藏&#xff0c;很容易导致页面布局错位(相对窗体的绝对定位元素)&#xff1a;明明按钮在这里&#xff0c;却要在上面一点儿点击屏幕才能点到它明明…

做进度条 根据自己的数据显示进度

做了很多种方法 1&#xff1a; 线程 thread的方法 2&#xff1a; backGroundWorker的方法 3&#xff1a; 自定义线程类 4&#xff1a; 做一个进度条的窗体 通过自定义设置做&#xff08;最方便快捷&#xff09; public partial class waitingProcessbar : Form{public waitin…

视图和模型变换

视图变换&#xff0c;是指变换照相机的位置&#xff0c;角度。 模型变换&#xff0c;是指变换被照物体的位置&#xff0c;角度。 这两个变换&#xff0c;都会影响最终图形中&#xff0c;物体的位置&#xff0c;角度。而这两个变换&#xff0c;可以达到相同的效果。比如&#x…

phoenix的元数据一般存在哪里_Phoenix的一些问题

date: 2020-09-10 13:50:00updated: 2020-09-14 16:30:001. Phoenix索引全局索引&#xff1a;适合读多写少的场景。写数据时因为索引表分布在不同数据节点&#xff0c;跨节点数据传输带来巨大的性能消耗。全局索引必须是查询语句中所有列都包含在全局索引中&#xff0c;它才会生…

鸿蒙os全面升级,华为突然宣布,鸿蒙OS正式版6月底全面升级,幸福来得太突然...

原标题&#xff1a;华为突然宣布&#xff0c;鸿蒙OS正式版6月底全面升级&#xff0c;幸福来得太突然摘要&#xff1a;早在今年2月华为Mate X2折叠屏新品发布会上&#xff0c;余承东曾表示&#xff0c;鸿蒙OS正式版将于今年4月份全面上线。或许是因为华为宣布卖车分散了很多的精…

5-python学习——条件语句

5-python学习——条件语句 5-python学习——条件语句 条件语句if else形式if else条件语句说明 测试一下编程语言一般都由这么几个部分组成 变量条件分支语句循环语句函数这里要说的就是条件分支语句。 python的条件语句和shell脚本的非常像&#xff0c;也就是if else if else这…

eclipse启动失败:An internal error occurred during: reload maven project

2019独角兽企业重金招聘Python工程师标准>>> 1.找到workspace文件夹下的/.metadata文件夹&#xff0c;将其删除掉&#xff0c;然后在讲项目重新导入进去eclipse中。但是这个有一点不好的地方&#xff0c;之前对eclipse所做的配置也会恢复为默认配置 2.在.metadata下…

Quartz2D知识点聚合案例

Quartz2D知识点聚合 基本 //画图片UIImage *image [UIImage imageNamed:"阿狸头像"];[image drawInRect:rect];//字体NSString *title "标题";NSMutableDictionary *atr [NSMutableDictionary dictionary];atr[NSFontAttributeName] [UIFont systemFon…

skt7850鸿蒙策略,lol 英雄联盟 SKT状态回暖轻取外卡,SUP难挡Faker

MSI 第四日 SUP vs SKT双方bpBAN LISTBAN&#xff1a;SUP:流浪 牛头 豹女SKT&#xff1a;巴德 妖姬 鱼人PICKSUP:大树 男枪 冰女 卢锡安 锤石SKT&#xff1a;艾克 千珏 沙皇 EZ 布隆比赛开始&#xff0c;双方正常对线开局。前期下路锤石多次勾中ez&#xff0c;男枪也来逼出EZ布…

spring集成struts2

Struts2前身是WebWork&#xff0c;核心并没有改变&#xff0c;其实就是把WebWork改名为struts2&#xff0c;与Struts1一点关系没有。 Struts2中通过ObjectFactory接口实现创建及获取Action实例&#xff0c;类似于Spring的IoC容器&#xff0c;所以Action实例可以由ObjectFactory…

slqite3库查询数据处理方式_SQLite3命令操作大全

SQLite3命令操作大全SQLite库包含一个名字叫做sqlite3的命令行,它可以让用户手工输入并执行面向SQLite数据库的SQL命令。本文档提供一个样使用sqlite3的简要说明.一.qlite3一些常用Sql语句操作创建表: create table 表名(元素名 类型,…);删除表: drop …

Android学习之查看网络图片

在这里小编学习了查看网络图片的小案例,: 初始界面: 点击浏览后,效果如下: 需要注意的是 该案例需要获取联网权限,即: <uses-permission android:name"android.permission.INTERNET"/>具体步骤如下: 1.定义并初始化控件: private EditText etImageUrl;private …

AutoLayout 浅析动画

1.AutoLayout相关的几个易混淆的方法 setNeedsLayout layoutIfNeeded layoutSubViews setNeedsUpdateConstraints updateConstraitsIfNeed updateConstraints 子视图在界面上的显示大概经过了&#xff1a;更新约束-通过约束依赖关系得到具体的frame-展示到界面。上面几个是和au…

vue 转为静态html,Vue CLI 3使用:HTML和静态资源(五)

HTMLpublic/index.html 文件是一个会被 html-webpack-plugin 处理的模板。构建中&#xff0c;各种资源路径会被注入解析。可以使用 lodash template 语法插入内容。用来做不转义插值&#xff1b;用来做 HTML 转义插值&#xff1b;用来描述 JavaScript 流程控制。除了被 html-we…

animate css3 应用的借鉴,一个同事写的JS

$("#banner").height($(window).height()-125);$(window).resize(function(){ $("#banner").height($(window).height()-125);}); //首页幻灯$(".indeximgs:first").show();var i0;$(".leftbut").click(function(){$(".indexim…

从html导出带样式的excel,Jquery导出带样式的Excel

工作中做导出的时候&#xff0c;需要导出自定义的表格或嫌弃导出的Excel格式太难看了。需要设置颜色、字号大小、加粗、合并单元格等等。特性&#xff1a;支持过滤 某个位置支持过滤 img 标签支持过滤 a 标签支持过滤 input 标签支持包含 行内样式。HTML页面&#xff1a;HTML页…

elementui from表单提交_elementui upload与form一起提交

学生基本信息管理操作中&#xff0c;有照片&#xff0c;可以上传也可以不上传&#xff0c;在表单界面可以修改照片&#xff0c;el-upload控件可以带额外参数提交&#xff0c;jquery的post模拟不了成表单带文件提交的方式&#xff0c;因此&#xff0c;判断如果有上传文件时&…

information_schema.character_sets 学习

information_schema.character_sets 表用于查看字符集的详细信息 1、character_sets 常用列说明&#xff1a; 1、character_set_name&#xff1a;    字符集名 2、default_collate_name&#xff1a;    默认排序规则   3、description&#xff1a;         …

asp.net mvc 用Redis实现分布式集群共享Session。

1、这两天研究Redis搞分布式session问题&#xff0c;网上找的资料都是用ServiceStack.Redis来实现的&#xff0c;但是在做性能测试的时候发现最新的v4版本有限制每小时候最多请求6000次&#xff0c;因为官网开始商业化要收费了&#xff0c;好坑爹的说&#xff0c;还好我前期弄了…

如何用计算机求和,求和计算器

求和计算器您可以使用此求和计算器快速计算预定范围内某个表达式的序列之和。如何使用求和计算器输入总和的表达式输入上限和下限提供表达式中使用的变量的详细信息单击“计算”按钮生成结果。求和Σ符号计算器k ∑n 0变量:nixyzabc 103740支持的运算符常量和函数算术运算符加“…