最近在连接池上面栽了个跟头(参见这里),引起我对池技术的强烈关注,这几天总结了一下很多场景都会使用的池技术;
池概念
pool,中文翻译为水池,但是在英文中,还有一种解释是
an organization of people or resources that can be shared;
不知道古代中文是否包含共享资源的意思,欧美语言中,池就是有资源共享的意思;
为什么要采用池技术
精确的说,应该是为什么要使用连接池技术;我们先看看这些使用池技术的组件都有哪些,httpclient HTTP协议组件,dbcp数据库连接池,jedis redis客户端;可以说代表了三种截然不同的应用场景;但是他们背后,却都有一个共同点,那就是TCP长连接;
综上,我个人认为主要是出于以下几方面
1、TCP连接每次建立和释放都比较耗时,特别是对于小的HTTP请求,如果能在业务调用时省去这段时间,则业务代码性能更好,这就需要提前建立TCP连接或者事后释放TCP连接;
2、业务代码会存在多次资源调用,但是不希望TCP连接对象在多次调用之间传来传去,这样会让代码变的复杂;
3、组件希望提供更友好的接口,而将底层的TCP技术使用池进行了封装;
有些人可能对长短连接概念不是很清楚,大家可以简单的认为,像HTTP协议请求完就会与服务器的连接断掉是短连接,通常我们上网都是短连接。像开发过程中使用的数据库客户端,一般会长时间与数据库维持一个TCP连接,这个可以就认为是长连接。除了DB,还有redis,java中的RMI等协议都是长连接;
长连接比短连接各有优劣:
好处:省去每次TCP3次握手和4次挥手的过程,发送请求和响应耗时更短;
坏处:服务器切换影响比较大,通常只能通过强制手段让客户端重新建立连接才能完成后端服务的切换;单纯从运维角度看,长连接非常不提倡;
池抽象
如果画一张图,我想应该是这样
就是在一个大池子里面,有好多资源。这些资源随时可能被拿出去占用或者随时有新的资源被归还,好借好还,再借不难;正常情况应该是这样
这就是池技术的基本原理,这个模型很重要,httpclient,dbcp,jedis,c3p0,druid,okhttp这些组件都使用到了池技术,大家可以自行去官网查看;下面我再抽几个重点场景给大家几个常见的重要配置参数;
连接池总资源数
既然是池,其容量总是有限制的,并且不同的组件,其总量限制默认都很低。
组件 |
最大资源数属性 |
默认最大资源数 |
httpclient4 |
MaxTotal |
20 |
jedis2 |
maxTotal |
8 |
druid |
maxActive |
8 |
c3p0 |
maxPoolSize |
15 |
关于httpclient,还要特殊说明一下,这个maxTotal,存在误区,可以参见这里;
那么问题来了,如果TCP连接的另外一端响应突然变慢,导致租户无法及时归还资源,新的用户又要借用,但连接池中没有资源了,组件会如何处理?
答案是等;而且,如果你没有修改默认设置的话,默认是无限的等;你可能会说,我不相等,我想让系统有自我保护功能,当后端依赖出现问题的时候,我们尽快的反馈给调用方,而不是把自己耗死;OK,你的想法很不错,但是你需要修改配置,让调用方不是无限等,可以设置调用方不等或者等待有限时间
组件 |
属性 |
httpclient4 |
RequestConfig.ConnectionRequestTimeout |
jedis2 |
MaxWaitMillis |
druid |
maxWait |
c3p0 |
breakAfterAcquireFailure |
dbcp |
maxWaitMillis |
TCP连接的问题
因为网络协议太复杂了,当组件采用池技术后,一系列后遗症也逐渐暴露出来;有时发现从池中取出连接使用时,发现连接已经被服务器端关闭了;并且这种情况,各种池组件无法感知(这个说起来又能说一篇),这些连接在英文中称为stale;针对这种情况,各种组件基本上围绕使用流程在使用前,使用后以及定时任务清理三种策略来避免这种情况;
使用前
组件通常采用在用户代码请求时,组件先自己测试TCP连接是否还可用,但是这种手段通常仅对DB连接池组件有效;如dbcp组件,通常会向服务器端发送一个测试sql来测试连接是否还可用;
使用后
同使用前检测一样,这种方法也是通常应用在数据库连接池中;在数据库出现问题时,通常连接已经不可用,这个时候再return给连接池,也会给其他后申请者造成影响,不如直接释放连接,后续再建立新的连接;
定期检测
通常连接池在不同的时间,池中空闲的连接数量是不同的,在业务低峰期,长时间维持一些没用的连接也是一种浪费。通常这个时候会有一个定时任务来定期清理长期不活跃的连接。具体的清理策略各式各样,有按照连接时长清理的,有按照长时间没有活动清理的。这个清理又会涉及很多参数设置,大家可以自行阅读参考;
另外,在数据库连接池中针对低峰期空闲连接多的问题(通常会导致系统time_wait多的问题),连接池通常还有一个最大空闲连接数(maxIdle)和最小空闲连接数(minIdle)两个参数,这两个参数的含义如下:
minIdle:保证池中最少要有minIdle个空闲的连接可用。如果少于这个数,则开始预创建连接;
maxIdle:保证池中最多有maxIdle个空闲的连接,当连接池被不断归还时,如果空闲连接数超过maxIdle,则开始对空闲的连接数进行释放。
最后还有一点,druid,dbcp和jedis池技术都是采用或者参考的apache的common-pool,很多参数都跟common-pool一样。okhttp是后起之秀,虽然也使用了池技术,但是在同步调用中,并没有对池的大小设置闲置,可以认为,okhttp是一个无限制的连接池;
搜索公众号“猿界汪汪队”,关注更多有深度的文章;
参考资料:
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/index.html
http://commons.apache.org/proper/commons-dbcp/configuration.html
https://www.mchange.com/projects/c3p0/#configuration_properties