在上一篇文章中,我们介绍了弹性数据库连接失效的背景,并探讨了 HikariCP 连接池探活策略的相关内容。在本文中,我们将会继续探讨另一个线上常用的连接池——Druid,并为您介绍如何在使用 Druid 时实现最佳实践的弹性数据库连接池探活策略。
Druid 的版本迭代更新比较快,同时探活配置的参数也比较多,这导致即使是相同的参数在不同的版本中达到的效果也可能不一样。但与探活相关的逻辑实现只存在源码里的两个函数里, 我们先列举一下跟 Druid 探活相关的参数,在具体看一下源码的实现对这些参数的使用。日后我们在开发中遇到配置探活不生效的情况下,可以看一下对应版本源码来判断自己的探活是否配置正确。
下面是与 Druid 探活相关的参数:
参数名称 | 说明 | 默认值 |
---|---|---|
initialSize | 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时。 | 0 |
minIdle | 最小连接池数量。 | 0 |
maxActive | 最大连接池数量。 | 8 |
testOnBorrow | 申请连接时执行 validationQuery 配置的 SQL 检测连接是否有效,做了这个配置会降低性能。 | false |
testOnReturn | 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。 | false |
testWhileIdle | 建议配置为 true,不影响性能,并且保证安全性。在连接池中申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效。 | 大多数版本为 True |
timeBetweenEvictionRunsMillis | 1) Destroy 线程会检测连接的间隔时间,每隔这个值的时间就会执行一次 DestroyTask。 2) testWhileIdle 的判断依据,详细看 testWhileIdle 属性的说明。 | 大多数版本是 1 分钟 |
keepAlive | 连接池中的 minIdle 数量以内的连接,空闲时间超过 minEvictableIdleTimeMillis,则会执行探活操作此参数在 1.0.28 以上的版本才支持 详细说明参考官方文档。 | false |
keepAliveBetweenTimeMillis | 配合 keepAlive 使用在低版本不支持,如果空闲时间小于 timeBetweenEvictionRunsMillis 但大于 keepAliveBetweenTimeMillis 扔执行探活操作 | 大多数版本是 2 分钟 |
validationQuery | 用来检测连接是否有效的 sql,要求是一个查询语句。 | select 1 |
validationQueryTimeout | 单位:秒,检测连接是否有效的超时时间。底层调用 jdbc Statement 对象的 void setQueryTimeout(int seconds) 方法 | |
minEvictableIdleTimeMillis | 连接空闲时间大于该值时关闭空闲连接大于 minIdle 的连接,类似 hikaricp 的 idleTimeout | 30 分钟 |
maxEvictableIdleTimeMillis | 连接空闲时间大于该值时不管 minIdle 都关闭该连接,类似 hikaricp 的 maxlifetime(低版本不支持) | 7 小时 |
Druid 的探活主要有以下两个函数来实现:
getConnectionDirect 是每次从连接池中取连接时会被调用的函数。我们从下面的代码中可以看出,如果 testOnBorrow 为 true,则每次获取连接之前都会检测连接是否有效。如果 testOnBorrow 为 false 且 testWhileIdle 为 true,则需要判断连接的空闲时间是否超过 timeBetweenEvictionRunsMillis 设置的值,如果超过则进行探活检测。失效的连接会被丢弃,并且会补充到连接池的 minIdle 数量。timeBetweenEvictionRunsMillis 在大多数版本中的默认值为 1 分钟。只要这个值设置的时间小于十分钟,并且保证 testWhileIdle 开启,就能保证拿不到网关关闭的失效连接。
在不支持 keepalive 的低版本中,只能依靠 testOnBorrow 或 testWhileIdle 来进行探活。建议配置 testWhileIdle 来进行探活。在高并发的场景下,这种方式的性能消耗会更小一些。
在下面的代码中我们可以看出,shrink 方法是在 DestroyTask 线程的 run 方法中调用的,用于销毁连接池中的连接。如果 timeBetweenEvictionRunsMillis 大于 0,则每隔这个时间间隔就会调用 destroyTask.run(boolean, boolean) 方法,即执行 shrink 方法。
从上面的代码中可以看出,shrink 方法会使用 keepAlive 参数。需要注意的是,在不同版本的 Druid 中,keepAlive 参数的支持和实现逻辑可能不同。官方建议在使用 keepAlive 参数时,应该使用 1.1.21 以上的版本。尽管官方文档中说明了空闲时间超过 minEvictableIdleTimeMillis,就会执行探活操作,但是在高版本中,这个探活操作的执行时间也受到了 keepAliveBetweenTimeMillis 参数的影响。因此,在高版本中,如果想要正确地使用 keepAlive 参数,就需要了解其在具体版本中的实现逻辑。
下面代码是 1.1.10 和 1.1.21 版本中关于 shrink 方法的源码对比:
首先看一下 1.1.10 版本的源码,它首先会判断连接空闲时间是否大于 minEvictableIdleTimeMillis,如果是,则接下来进行第二步的判断:是否是多于 minIdle 的空闲连接。如果是,就将这些连接加入到驱逐连接的数组中,以便进行后续的驱逐操作。如果不是,就再次判断连接空闲时间是否大于 maxEvictableIdleTimeMillis,如果是,则将这些连接加入到驱逐连接的数组中。如果也不是,则进行最后的判断:是否开启了 keepAlive 配置。如果开启了,就将这些连接加入到保活连接数组中,以进行后续的探活操作。
在 1.1.21 版本中,shrink 方法的总体逻辑与 1.1.10 版本类似,但是新增了一个名为 keepAliveBetweenTimeMillis 的参数。这个参数决定了使用 keepAlive 进行探活的时间间隔,其默认值为 2 分钟,keepalive 开启且空闲时间大于这个值会进行探活。
另一个不同点是,在进行探活操作时,1.1.10 版本仅会关闭无效的连接,但 1.1.21 版本则更进一步,除了关闭无效连接外,还会自动添加连接以达到 minIdle 的最小连接数。
1.1.10 1.1.21
总结,druid 的探活参数在 1.0.28 版本之前没有定时的探活功能只能在每次拿到连接前进行检测是否有效,建议配置 testWhileIdle 为 true 在高并发情况下不会太影响性能,如果对可用性要求高的可以开启 testOnBorrow,以在每次获取连接时检测连接的有效性。在高版本中可以用 keepAlive 参数对连接进行保活。针对线上使用 Druid 连接池的应用建议使用支持 keepAlive 的 1.1.21 或者更高版本。
<propertyname="testWhileIdle"value="true"/>
<propertyname="validationQuery"value="SELECT 1"/>
<propertyname="timeBetweenEvictionRunsMillis"value="30000"/>
<propertyname="minEvictableIdleTimeMillis"value="300000"/>
<propertyname="keepAlive"value=true/>
此版本支持 keepAlive 可以配置 minEvictableIdleTimeMillis 时间小于 10 分钟,能够高效的进行探活防止网关关闭连接。
同 1.1.10
<propertyname="testWhileIdle"value="true"/>
<propertyname="validationQuery"value="SELECT 1"/>
<propertyname="timeBetweenEvictionRunsMillis"value="30000"/>
<propertyname="minEvictableIdleTimeMillis"value="300000"/>
此版本不支持 keepAlive 只能在获取连接对象的时候检测,对可用性高的也可以开启 testOnBorrow。
作者:京东零售 王雷鑫
来源:京东云开发者社区 转载请注明来源