数据测试 弹性数据库连接池探活策略调研 (三)——DBCP | 京东云技术团队

京东云开发者 · 2023年09月08日 · 1513 次阅读

前言

在之前的文章中,我们介绍了弹性数据库连接失效的背景,并探讨了 HikariCP、Druid 连接池探活策略的相关内容。在本文中,我们将会继续探讨另一个线上常用的连接池——DBCP,并为您介绍如何在使用 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 网关故障重启时能够有效避免应用报错。

JED 配置模版:

DBCP1.4

<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"/>   

DBCP2.2.0

<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"/>   

DBCP2.1.1

同 2.2.0

总结

本文以 JED 的网关超时报错为背景,对常见的数据库连接池进行了调研,并介绍了连接池探活相关的参数和探活逻辑。通过本文的内容,读者应该了解到不同连接池的探活内容,可以根据不同的参数设置连接池,有效避免应用拿到网关关闭的连接。本文提供了在 JED 数据库中的连接池配置模板,读者可以根据自己的应用需求进行调整。

作者:京东零售 王雷鑫

来源:京东云开发者社区 转载请注明来源

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册