公司官网上的心跳交易不执行了,按理说是定时任务做的,怎么会不执行呢,遂去查看日志发现:
[ERROR] [2018-09-25 20:30:37,933] (AgtForwardServiceImpl.java,31) - 转发数据推送异常:{}nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.alibaba.druid.pool.DataSourceClosedException: dataSource already closed at Tue Sep 25 19:43:16 GMT+08:00 2018
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.alibaba.druid.pool.DataSourceClosedException: dataSource already closed at Tue Sep 25 19:43:16 GMT+08:00 2018
[ERROR] [2018-09-25 20:30:37,933] (AgentMqHandle.java,96) - Agent处理信息失败!
连接池关闭异常:连接池已经在2018.09.25 19:43:16的时候关闭。
沿着日志继续往前找,一定能找到问题的根源。
INFO] [2018-09-25 19:43:16,846] (ExecutorConfigurationSupport.java,203) - Shutting down ExecutorService 'taskExecutor'
[INFO] [2018-09-25 19:43:16,846] (ExecutorConfigurationSupport.java,203) - Shutting down ExecutorService 'tasksScheduler'
[INFO] [2018-09-25 19:43:16,860] (DruidDataSource.java,1543) - {dataSource-1} closed
[ERROR] [2018-09-25 19:43:16,863] (DruidDataSource.java,1457) - recyle error
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
recyle error
: “回收错误”, 回想一下,这个时间点好像是项目升级的时候,可能是连接池中的连接被异常中断,导致连接不能被正常回收,recyle error or java.lang.InterruptedExedException: null。之后在去使用连接池的时候就会报DataSourceClosedException(由之前的recyle error造成)。
那么为什么不能正常的回收连接呢,看一下Druid连接池的连接回收机制吧。
因为项目使用的连接池是druid-1.0.31,这个版本的处理机制是这样的:
boolean result;
final long lastActiveTimeMillis = System.currentTimeMillis();
// 旧版本这里选择忽略异常,直接抛出。
// 导致不能正常的回收连接
lock.lockInterruptibly();
try {
activeCount--;
closeCount++;
result = putLast(holder, lastActiveTimeMillis);
recycleCount++;
} finally {
lock.unlock();
}
if (!result) {
JdbcUtils.close(holder.getConnection());
LOG.info("connection recyle failed.");
}
} catch (Throwable e) {
holder.clearStatementCache();
if (!holder.isDiscard()) {
this.discardConnection(physicalConnection);
holder.setDiscard(true);
}
LOG.error("recyle error", e);
recycleErrorCount.incrementAndGet();
}
2015年的时候,这是一个已知的bug:https://github.com/alibaba/druid/issues/785
官方后来已经修复,改成了加锁的方式:
long lastActiveTimeMillis = System.currentTimeMillis();
// 新版本已经采用加锁的方式,做出中断处理
this.lock.lock();
try {
--this.activeCount;
++this.closeCount;
result = this.putLast(holder, lastActiveTimeMillis);
++this.recycleCount;
} finally {
this.lock.unlock();
}
if (!result) {
JdbcUtils.close(holder.conn);
LOG.info("connection recyle failed.");
}
} catch (Throwable var47) {
holder.clearStatementCache();
if (!holder.discard) {
this.discardConnection(physicalConnection);
holder.discard = true;
}
LOG.error("recyle error", var47);
recycleErrorCountUpdater.incrementAndGet(this);
所以使用新版的druid包即可,只能讲有点坑。