旧版本Druid回收连接引发的'recyle error'

公司官网上的心跳交易不执行了,按理说是定时任务做的,怎么会不执行呢,遂去查看日志发现:

[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包即可,只能讲有点坑。

你可能感兴趣的:(JavaWeb)