在之前的文章中,我们介绍了弹性数据库连接失效的背景,并探讨了HikariCP、Druid连接池探活策略的相关内容。在本文中,我们将会继续探讨另一个线上常用的连接池——DBCP,并为您介绍如何在使用DBCP时实现最佳实践的弹性数据库连接池探活策略。
DBCP有两个版本:1.x和2.x(也称为DBCP2)。DBCP 2基于Commons Pool 2,相比1.x版本,在性能、JMX支持和其他许多方面都有所提高。由于DBCP 2.x与DBCP 1.x不是二进制兼容,所以升级到2.x的用户应该知道Java包名称已经改变,以及Maven坐标。
首先我们先列出关于DBCP探活相关的参数:
参数名称 | 说明 | 默认值 |
---|---|---|
initialSize | 初始化时建立物理连接的个数。 | 0 |
minIdle | 最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建 | 0 |
maxIdle | 最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制 | 8 |
maxActive/maxTotal | 最大活动连接:连接池在同一时间能够分配的最大活动连接的数量,超过这个值的请求进入等待队列, 如果设置为非正数则表示不限制(1.x版本 maxActive 2.x版本maxTotal) | 8 |
testOnBorrow | 指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 | true |
testOnReturn | 指明是否在归还到池中前进行检验。 | false |
testWhileIdle | 指明连接是否被空闲连接回收器进行检验。如果检测失败,则连接将被从池中去除.注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串 | false |
timeBetweenEvictionRunsMillis | 驱逐连接的线程运行的时间间隔,以毫秒为单位. 如果设置为非正数,则不运行空闲连接回收器线程 | -1 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。 | select 1 |
validationQueryTimeout | 单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法 | |
minEvictableIdleTimeMillis | 连接在池中处于空闲状态的最小时间,超过这个时间会被驱逐驱逐。 | 30分钟 |
softMinEvictableIdleTimeMillis | 这个参数与minEvictableIdleTimeMillis相比是这个参数受minIdle的限制,当到达这个值的时候只会驱逐大于minIdle的连接数 | -1 |
numTestsPerEvictionRun | 在每次空闲连接回收器线程运行时检查的连接数量。 | 3 |
DBCP 和与Druid 的探活配置相比,尽管它们的许多参数名字和功能相似,但在细节和默认值上存在不同之处。例如,testWhileIdle 参数在 Druid 中用于判断是否在申请连接时开启探活,且需大于 timeBetweenEvictionRunsMillis 参数值。而在 DBCP 中,该参数在驱逐连接时进行判断,若开启,则直接进行验证,类似Druid中的keepAlive参数。两个连接池中,驱逐空闲连接的时间间隔都受到 timeBetweenEvictionRunsMillis 参数的控制。此外,testOnBorrow 参数的功能相同,但默认值有所不同。
另外在DBCP在驱逐线程中也受numTestsPerEvictionRun参数影响,这个参数是指每次驱逐线程运行时驱逐连接的个数,不会一次检查池内全部的连接。此外DBCP的minEvictableIdleTimeMillis跟Druid不同超时驱逐的连接数不受minidle控制。
下图是DBCP1.4.0的驱逐连接线程的源码:org.apache.commons.pool.impl.GenericObjectPool#evict
我们可以从源码中看到驱逐的连接个数从getNumTests获取,getNumTests返回的是连接池的现有size和numTestsPerEvictionRun的最小值。驱逐过程的第一步先判断空闲时间是否超过minEvictableIdleTimeMillis,没有的话再判断softMinEvictableIdleTimeMillis是否超时和现有的连接是否大于minIdle,第三个if是判断testWhileIdle的配置为true时且上面没有回收该连接,在第四步的时候对此连接进行探活。
总结:DBCP在各个版本中对探活变动不大,一般都是在驱逐连接数时可以使用testWhileIdle来进行探活,驱逐线程运行的间隔时间是timeBetweenEvictionRunsMillis参数的值,此外numTestsPerEvictionRun参数是每次驱逐线程的个数,因此我们只要使用这两个参数设置的可以在10分钟之内对池内所有连接(最大值为maxActive/maxTotal)进行探活就能有效避免JED网关的失效连接。
总的来说,DBCP在不同版本中对探活的实现方式变化不大。通常情况下,可以通过在驱逐连接时使用testWhileIdle参数来进行连接的探活。驱逐线程的运行间隔时间受timeBetweenEvictionRunsMillis参数的控制,而numTestsPerEvictionRun参数则决定了每次驱逐线程可以处理的连接数。建议numTestsPerEvictionRun配置的值和maxActive/maxTotal一致,并配置timeBetweenEvictionRunsMillis小于10分钟能够保证对所有连接进行探活,避免拿到网关失效的连接。
此外,应用使用DBCP时,默认开启testOnBorrow参数一般可以有效避免拿到失效的连接,而Druid默认不开启testOnBorrow参数。关于是否开启testOnBorrow参数,应用可以自行评估。虽然开启testOnBorrow参数会在每次拿到连接前进行连接验证,损耗一小部分性能,但是这样能够及时销毁无效连接并重建新连接,在遇到JED网关故障重启时能够有效避免应用报错。
<propertyname="minIdle"value="5"/>
<propertyname="maxActive"value="10"/>
<propertyname="testWhileIdle"value="true"/>
<propertyname="validationQuery"value="SELECT 1"/>
<propertyname="timeBetweenEvictionRunsMillis"value="300000"/>
<propertyname="numTestsPerEvictionRun"value="10"/>
<propertyname="minIdle"value="5"/>
<propertyname="maxTotal"value="10"/>
<propertyname="testWhileIdle"value="true"/>
<propertyname="validationQuery"value="SELECT 1"/>
<propertyname="timeBetweenEvictionRunsMillis"value="300000"/>
<propertyname="numTestsPerEvictionRun"value="10"/>
同2.2.0
本文以JED的网关超时报错为背景,对常见的数据库连接池进行了调研,并介绍了连接池探活相关的参数和探活逻辑。通过本文的内容,读者应该了解到不同连接池的探活内容,可以根据不同的参数设置连接池,有效避免应用拿到网关关闭的连接。本文提供了在JED数据库中的连接池配置模板,读者可以根据自己的应用需求进行调整。
作者:京东零售 王雷鑫
来源:京东云开发者社区 转载请注明来源