org.springframework.dao.RecoverableDataAccessException:
### Error querying database. Cause: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet successfully received from the server was 59,911 milliseconds ago. The last packet sent successfully to the server was 59,912 milliseconds ago.
### The error may exist in URL [jar:file:/data/app.jar!/BOOT-INF/classes!/mapper/eweishop/OrderGoodsMapper.xml]
### The error may involve com.renzhenmall.oms.storage.mapper.eweishop.OrderGoodsMapper.selectByPrimaryKey-Inline
### The error occurred while setting parameters
### SQL: select id, uid, shop_id, order_id, goods_id, status, member_id, price, price_unit, price_original, price_discount, price_change, credit, credit_unit, credit_original, coupon_price, member_discount_price, total, title, option_id, option_title, thumb, goods_code, unit, weight, volume, deduct_credit, add_credit, form_data, refund_status, refund_type, refund_id, package_id, package_cancel_reason, comment_status, activity_package, create_time, dispatch, is_join_member_price, member_price, source, commission_self_money, cost_price, parent_shop_id, parent_order_id from es_shop_order_goods where id = ?
### Cause: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet successfully received from the server was 59,911 milliseconds ago. The last packet sent successfully to the server was 59,912 milliseconds ago.
; Communications link failure
The last packet successfully received from the server was 59,911 milliseconds ago. The last packet sent successfully to the server was 59,912 milliseconds ago.; nested exception is com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet successfully received from the server was 59,911 milliseconds ago. The last packet sent successfully to the server was 59,912 milliseconds ago.
@Override
public DruidPooledConnection getConnection() throws SQLException {
return getConnection(maxWait);
}
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
init();
if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(this);
return filterChain.dataSource_connect(this, maxWaitMillis);
} else {
return getConnectionDirect(maxWaitMillis);
}
}
在getConnection方法第一行就会调用 init方法来执行初始化。
DestroyTask
接着,我们来看DestroyTask 都做了些什么:
public class DestroyTask implements Runnable {
public DestroyTask() {
}
@Override
public void run() {
shrink(true, keepAlive);
if (isRemoveAbandoned()) {
removeAbandoned();
}
}
}
我司后端以前遗留的Spring Boot服务数据库连接池都用的阿里巴巴Druid,最近隔三差五会收到异常报警:
这个错误的原因很明显,使用了一个已经失效的连接去做db操作,就会返回这个错误。也就是说db操作的时候从数据库连接池里拿到了一个不可用的连接。
但是正常来说这种情况是不会发生的,因为我们是配置了time-between-eviction-runs-millis的,理论上应该是会定时对连接池中的连接做检查的,但是实际结果显然并没有。
这也是官方文档上推荐的配置,那么问题到底出在哪儿了?
注:依赖的druid-spring-boot-starter版本为1.1.22
只能去翻druid源码了,DruidDataSourceWrapper 它继承了
com.alibaba.druid.pool.DruidDataSource
:在核心类DruidDataSource中有个init方法:
在 init方法中会执行 createAndStartDestroyThread方法来创建一个定时任务来执行DestroyTask:
这里 你可能会好奇 init方法是在哪里被调用的,我们来看看 DruidDataSource#getConnection 方法:
在getConnection方法第一行就会调用 init方法来执行初始化。
DestroyTask
接着,我们来看DestroyTask 都做了些什么:
其中isRemoveAbandoned()没有配置,默认是false的。所以,这个task就执行了一句话:shrink(true, keepAlive)
在shrink方法里面的最后我们可以看到有这段代码:
我们重点关注 validateConnection 方法:
validConnectionChecker 初始化逻辑在 DruidDataSource#initValidConnectionChecker()方法中:
由于 我们使用的MySQL数据库,所以直接看 MySqlValidConnectionChecker类:
解决办法
从代码中可以看出来,keepAlive的默认值是false,这样就会导致每次shrink之后的keepAliveCount==0,也就进不到最后检查存活连接的逻辑中。并且会导致minIdle的配置失效,连接池最后会被回收至只剩0个连接。所以,我们还差一下配置,keepAlive必须配置为true。