线上有个业务,是批量拉取数据,然后入库,每批500条,如此反复。 但是经常报错,
开始druid是低版本,报错是connection holder is null, 这个错误我以前见过,数据库杀连接会报这个错。 Caused by: java.sql.SQLException: connection holder is null at com.alibaba.druid.pool.DruidPooledConnection.checkStateInternal(DruidPooledConnection.java:1140) at com.alibaba.druid.pool.DruidPooledConnection.checkState(DruidPooledConnection.java:1131) at com.alibaba.druid.pool.DruidPooledConnection.prepareStatement(DruidPooledConnection.java:333) -------------- 升级druid到1.2.8版本后,报错变成了connection closed <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> Caused by: java.sql.SQLException: connection closed at com.alibaba.druid.pool.DruidPooledConnection.checkStateInternal(DruidPooledConnection.java:1190) at com.alibaba.druid.pool.DruidPooledConnection.checkState(DruidPooledConnection.java:1181) at com.alibaba.druid.pool.DruidPooledConnection.prepareStatement(DruidPooledConnection.java:364) -------------- 以为是数据库杀超时sql导致的,看了数据库日志,没有线索。 抓包看了,报错时也没有关TCP的情况。 然后,将注意力放到了druid的参数上。
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);//毫秒 druidDataSource.setMinEvictableIdleTimeMillis(300000); druidDataSource.setRemoveAbandoned(true); druidDataSource.setRemoveAbandonedTimeout(1800);//秒 druidDataSource.setLogAbandoned(true);
试验了一次,大概31分钟报错,与1800秒高度吻合。 尝试改小时间,如360秒,果然差不多就报错了。 尝试druid杀连接的地方下断点,果然进入断点,证实了就是它的问题。 --------------
removeAbandoned=true 启用"连接泄露检测"功能,Druid会监控所有从连接池获取的连接 如果连接长时间未归还(达到removeAbandonedTimeout设置的值),Druid会强制回收该连接
removeAbandonedTimeout=1800(单位:秒 = 30分钟) 设置连接被认定为"泄露"的超时阈值
大概意思就是:连接最多用30分钟,就给你关了!!!
-------------- 查看DruidDataSource代码, DestroyConnectionThread里,每隔timeBetweenEvictionRunsMillis毫秒,执行destroyTask, 后者执行removeAbandoned(),
遍历每个活动连接, long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000); if (timeMillis >= removeAbandonedTimeoutMillis) {
当前时间与获取连接时间,做差值,超过removeAbandonedTimeoutMillis毫秒数,就关闭连接,将连接标记为abandoned=true -------------- 不过,为什么后续还能获取这个连接呢?持续报错呢?为什么不从连接池清理出去呢?
在initCheck()方法中有一段代码:
if (removeAbandoned) { LOG.warn("removeAbandoned is true, not use in production."); } 意思很明确,removeAbandoned参数并不是用在生产环境中的,是专为测试环境准备的参数。 也不知道谁加上的参数,无语 -------------- 回头看业务代码,是因为开始就获取连接,然后循环批量处理,而且处理得还很慢, 当超过30分钟时就被关连接了,导致报错。 -------------- 参考文档: https://segmentfault.com/a/1190000043745778 https://www.cnblogs.com/tiancai/p/17651907.html
|