HttpClient在我们项目中普遍使用,同时我们也会使用它的连接池来复用连接,减少了每次连接建立的过程。HttpClient的连接池是在4.3之后的版本才有,我们可以通过PoolingHttpClientConnectionManager
来管理我们的连接池以及查看连接池的使用情况。那么连接池主要有哪些指标呢?
maxTotal
:单个连接池实例能允许的最大连接数,当然如果你使用了多个连接池实例,也就有多个maxTotal指标
defaultMaxPerRoute
:每个路由(Route)的最大数
我们在使用HttpClient的连接池时,主要会设置最大连接数maxTotal
,以及单个路由的最大连接数defaultMaxPerRoute
,当然也可以针对特定的路由去单独配置最大的连接数。这里的路由怎么理解呢?可以简单的理解为目标host,比如我请求了www.baidu.com
和www.sina.com
,那么这里就有两个路由
除了上述两个最常使用的指标,还有下面一些指标值得我们关注
leased
:连接池中正在使用的连接数
available
:连接池中可用的连接数,这里的是连接被使用完后可被复用
pending
:等待获取连接的线程数
在HttpClient中,连接池由CPool
表示,该类继承AbstractConnPool
,此类的一些成员变量如下:
其中,leased保存的是正在被使用的连接集合,其size便是这个连接池的正在使用的连接数大小,available保存的是可用的连接(这些连接是已经和目标host建立过连接的),pending保存的是正在等待获取连接的线程,这三个值是从连接池纬度总体的指标,不同的路由对应的指标信息保存在routeToPool
,其中key为Route
,value为RouteSpecificPool
, RouteSpecificPool
如下:
可以看到单个路由下也有leased、available、pending三个指标,所有路由的指标值的和等于总的连接池的指标值
获取连接
从连接池里获取连接的核心方法是:org.apache.http.pool.AbstractConnPool#getPoolEntryBlocking
- 对连接池加锁
- 根据
route
从routeToPool
中获取对应route
的连接池,如果没有则创建一个,并添加到routeToPool
- 从第2步中获取的route对应的连接池中循环获取获取可用的连接
- 如果
available
列表不为空(说明有可用的),则从前往后遍历,如果有则将该连接从route下的available
中移除,加到route的leased
中(表明被使用),返回该连接;如果available
列表为空,返回null
- 如果获取到了连接则需要判断连接是否过期,如果过期了,则需要从此route下移除该连接,同时还需要在总的连接池的
available
列表中移除该连接
- 如果
- 经过第3个步骤如果拿到了连接,则说明获取到了一个之前存在的可复用的连接,从总的连接池的available列表中移除该连接并加入到总的连接池的
leased
中 - 如果第3个步骤没有拿到连接,则说明新建立连接
- 获取该route允许的最大连接数
- 判断此route已分配(available+leased)的连接数是否超过该route的最大连接数
- 如果超过,那么将available从后往前关闭连接并从总的连接池的available移除,同时从此route的连接池中移除
- 如果经过2步骤后发现已分配的连接数还是大于route的最大连接数说明连接超过上限,该请求需要等待,因此加此route下的连接池的pending中,同时也会加入总的连接池的pending中,并等待设置的超时时间
- 如果经过2步骤发现已分配的连接数小于路由最大连接数,说明可以分配连接,这时还需要在总的数量上做判断
- 判断总的连接池数量有没有超过连接池的
maxTotal
- 如果超过了还是需要等待,即进入5.3步骤
- 如果没有超过
- 如果总的连接数都是在available中(即available+leased=maxTotal的情况),则需要先从available中移除一个连接(可能是别的route下的连接)
- 创建连接,并加入到该route下的连接池的leased和总的连接池的leased中
- 判断总的连接池数量有没有超过连接池的
- 释放锁
释放连接
连接释放位于org.apache.http.pool.AbstractConnPool#release
其中有一个参数reusable
来判断此连接是否复用
- 连接池加锁
- 从连接池的
leased
中移除此连接 - 根据连接的
route
获取此route
的连接池- 从
route
的连接池的available
中移除此连接 - 如果连接可复用,即
reusable
为true
,则加入到route
的连接池的available
- 从
- 如果连接可复用,即
reusable
为true
,则加入到总的连接池的available
- 从
route
下的连接池的pending
中获取并移除一个等待连接的任务- 如果存在那么从总的连接池的
pending
中移除此任务 - 如果不存在,那么从总的连接池的
pending
中获取并移除一个任务
- 如果存在那么从总的连接池的
- 唤醒获取的这个任务
- 释放锁
参数reusable
是通过连接可重用策略决定,默认实现是org.apache.http.impl.DefaultConnectionReuseStrategy
如何监控
因为连接池也是比较重要的资源,那么如何去监控连接池的使用情况呢?目前因为我们接入了pinpoint,参照其监控数据库资源的套路对httpclient连接池进行监控,注意通过PoolingHttpClientConnectionManager获取连接池指标时,因为都加锁了,所以注意获取指标的频率别过高。