“自以为对的”MyBatis空闲连接探测的机制

最近碰到个现象,某个应用,每天在21:00-23:00才会执行,连接数据库执行操作,间隔性出现连接超时的错误,

创新互联专业为企业提供定襄网站建设、定襄做网站、定襄网站设计、定襄网站制作等企业网站建设、网页设计与制作、定襄企业网站模板建站服务,10余年定襄做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。

 
 
 
 
  1. Connection timed out (Read failed) 

因为应用和数据库是跨网段,咨询了下,防火墙超时时间配置的是30分钟,应用用的MyBatis连接池,相关配置如下,

相关参数解释,如下所示,

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。这种处理方式很流行,能使并发 Web 应用快速响应请求。

除了上述提到UNPOOLED下的属性外,还有更多属性用来配置POOLED的数据源:

poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10

poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。

poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)

poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。

poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和。默认值:3(新增于 3.4.5)

poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。

poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。

poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

P.S.

https://mybatis.org/mybatis-3/zh/configuration.html#environments

按照这字面意思,一开始我们理解poolPingConnectionsNotUsedFor参数控制的是连接多久没用,即处于空闲状态,在参数poolPingEnabled开启时,就会执行poolPingQuery定义的SQL主动探测。

如果按照这理解,poolPingConnectionsNotUsedFor设置了3000,即3秒,远小于30分钟防火墙超时的设置,不应该出现连接超时的现象。

我们怀疑过防火墙的配置,但从应用端看,并不是所有的请求都超时,而且防火墙端,没看到什么异常。数据库层,应该未设置过相关的配置。

原因是什么?

作为一款成熟的产品,不太可能因为bug,更多还是对他的理解存在偏差。

下载3.3.0的源码,链接如下,

https://github.com/mybatis/mybatis-3/releases/tag/mybatis-3.3.0

搜索这几个参数所在的文件,找到了PooledDataSource类,可以看到这三个参数都设置了初始值,

看下这个pingConnection方法,

如果连接未关闭,判断逻辑如下,

1. poolPingConnectionsNotUsedFor的值>=0;

2. getTimeElapsedSinceLastUse()>poolPingConnectionsNotUsedFor;

getTimeElapsedSinceLastUse()定义如下,

lastUsedTimestamp是在构造函数PooledConnection中定义的,

PooledConnection会在获取连接(popConnection)和回收连接(pushConnection)的时候调用,获取连接和回收连接则会被getConnection()和invoke()调用,因此,(2)的意思是当前这个连接空闲的时间是否大于这个参数poolPingConnectionsNotUsedFor定义的时间。

3. 如果满足条件(1)和(2),则会执行poolPingQuery的SQL,此处就是"select 1 from dual",如果执行失败,会关闭这个连接,

从应用日志,能看到这些信息,

 
 
 
 
  1. Testing connection 0000000000 ... 
  2. Execution of ping query 'select 1 from dual' failed:  Connection timed out (Read failed) 

这个问题的关键,就是这个pingConnection,在什么时候调用,就决定了poolPingConnectionsNotUsedFor什么时候起作用,可以看到,他是在这个isValid的方法中调用的,

而这个isValid是在每次获取连接和回收连接时调用的,换句话说,他是被动调用,并不是我们认为的空闲时主动调用,所以这个应用,只是晚上会跑,空闲连接超过30分钟是很正常的,

应用开了debug,这两段之间的间隔时间,就是得到超时连接的时间,

经过单线程测试,大约在15分钟,

因此,对这种testOnBorrow的连接探测机制,各有优缺点,优点就是会在一定程度保证应用正常的业务请求得到可用的连接,毕竟不可用的连接都已经被poolPingQuery定义的SQL测试了,一般情况下,不会让正常的业务请求出现报错,除非连接池没任何可用的连接。缺点就是如果配置的poolPingConnectionsNotUsedFor很小,某些请求都会在执行之前先进行验证,但是换个角度,如果是高并发,只要参数不是0,一般可能都不会满足需要验证的条件,如果设置为0,就可能会有很多pingQuery定义的SQL执行。而且,如果像上述单线程的操作,他会一个连接一个连接的尝试,等待一个连接出现超时错误的时间间隔是15分钟,这就很低效了。

对连接池的选择和配置,确实得结合实际场景需求来决策。

通过这个问题,至少让我明白,“自以为对的”机制正确还是错误,还是看他的实现,这才是最可靠的验证,而且,通过他的逻辑,可以让我们借鉴一些设计路径,多考虑他这么做背后的意义和影响,更有助我们将其用到正确的场景。

网页标题:“自以为对的”MyBatis空闲连接探测的机制
网页URL:http://www.shufengxianlan.com/qtweb/news19/407919.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联