案发现场
在给某个陈旧项目的连接池组件替换成Druid,开开心心地用上了网上淘来的一份推荐配置,在本地运行了下没有问题便发布到环境上去试跑看下,随后大概是过了一杯咖啡的时间,环境上就出现了如下报错信息,
[DEBUG][com.alibaba.druid.pool.DruidDataSource:1319][-][-] skip not validate connection.
[DEBUG][com.alibaba.druid.util.JdbcUtils:75][-][-] close connection error
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Communications link failure. Transaction resolution unknown.
再过了一段时间后还看到keepalive有关的报错信息,
[Druid-ConnectionPool-Destroy-228168005][ERROR][com.alibaba.druid.pool.DruidDataSource:2853][-][-] keepAliveErr
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 58,663 milliseconds ago. The last packet sent successfully to the server was 3,008 milliseconds ago.
排坑的过程
先贴一份跟这次案发现场有关的Druid池的部分配置——
// 从连接池获取连接后,如果超过被空闲剔除周期,是否做一次连接有效性检查
testWhileIdle=true
// 从连接池获取连接后,是否马上执行一次检查
testOnBorrow=false
// 归还连接到连接池时是否马上做一次检查
testOnReturn=false
// 是否开启连接保活
keepAlive=true
// 周期性剔除长时间呆在池子里未被使用的空闲连接, 1 min 一次
timeBetweenEvictionRunsMillis=60000;
// 设置连接最少存活时长和最大存活时长,超过上限才会被清理
minEvictableIdleTimeMillis=200000
maxEvictableIdleTimeMillis=280000
另外还有一个同样关键的配置,那就是MySQL的超时配置是300秒,超过300秒MySQL会主动关闭链接(我是直连MySQL的,如果是走LVS->RDS这样的链路的话就需要看LVS对连接存活的保留时间了)。一些有经验的同学看到这个配置可能已经发现问题了,还没发现的也不急,接下去看。
线索日志基本都给了,先来看看com.alibaba.druid.pool.DruidDataSource
打印了关键日志的地方,
if (idleMillis >= timeBetweenEvictionRunsMillis
|| idleMillis < 0 // unexcepted branch
) {
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
discardConnection(realConnection);
continue;
}
}
这里的逻辑是这样的,从池中拿到链