1、回收连接大致流程
image.png
2、close
com.alibaba.druid.pool.DruidPooledConnection#close
DruidPooledConnection是对holder的更抽象的封装,目的是更方便的对连接进行操作
这里的close是判断是正常连接关闭还是监控线程介入关闭,两者的后续流程会不同。
public void close() throws SQLException {
// DruidPooledConnection是对holder的在一次的抽象封装
// 判断当前连接是否为丢弃状态,是的话则直接返回不操作关闭操作,在连接检测时会将这个连接驱逐出连接池
if (this.disable) {
return;
}
DruidConnectionHolder holder = this.holder;
if (holder == null) {
if (dupCloseLogEnable) {
LOG.error("dup close");
}
return;
}
// 获取当前连接的dataSource,dataSource封装了当前连接的各种配置参数
DruidAbstractDataSource dataSource = holder.getDataSource();
// 判断当前对象封装的线程与执行线程是否为同一个线程
boolean isSameThread = this.getOwnerThread() == Thread.currentThread();
// 如果不是同一个线程,设置setAsyncCloseConnectionEnable为true
if (!isSameThread) {
dataSource.setAsyncCloseConnectionEnable(true);
}
/*
会检测是否开启了removeAbandoned
若开启后或者setAsyncCloseConnectionEnable为true 则会执行syncClose
由于和当先获取连接的线程不是同一个线程所以syncClose里面会加锁
若getConnection的线程在执行sql时发生了问题导致连接一直被占用,那么监控线程destroyTask线程就会介入并关闭这个连接
*/
if (dataSource.isAsyncCloseConnectionEnable()) {
syncClose();
return;
}
// CAS 修改“closing” 成员变量,由于该变量并没有用Atomic封装,为了保证原子操作所以使用AtomicIntegerFildUpdater
// 将closing由0 -> 1,标记当前连接正在关闭
if (!CLOSING_UPDATER.compareAndSet(this, 0, 1)) {
return;
}
try {
for (ConnectionEventListener listener : holder.getConnectionEventListeners()) {
listener.connectionClosed(new ConnectionEvent(this));
}
List<Filter> filters = dataSource.getProxyFilters();
if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(dataSource);
filterChain.dataSource_recycle(this);
} else {
recycle();
}
} finally {
CLOSING_UPDATER.set(this, 0);
}
this.disable = true;
}
3、recycle 回收连接
先看DruidPooledConnection#recycle,这个对象的recycle方法
public void recycle() throws SQLException {
if (this.disable) {
return;
}
DruidConnectionHolder holder = this.holder;
if (holder == null) {
if (dupCloseLogEnable) {
LOG.error("dup close");
}
return;
}
// 没有开启removeAbandoned,则不会直接丢掉连接,而是进入dataSource.recycle阶段,将连接重新放入连接池
if (!this.abandoned) {
DruidAbstractDataSource dataSource = holder.getDataSource();
dataSource.recycle(this);
}
// 断掉与holder的关联,监控线程不会走上面的回收流程,直接断掉
this.holder = null;
conn = null;
transactionInfo = null;
closed = true;
}
回收连接 DruidDataSource#recycle,先关注最核心的代码块 putLast(),这里就是重新将连接放入连接池
protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
。。。
lock.lock();
try {
if (holder.active) {
activeCount--;
holder.active = false;
}
closeCount++;
// 将连接重新放入连接池
result = putLast(holder, currentTimeMillis);
recycleCount++;
} finally {
lock.unlock();
}
if (!result) {
JdbcUtils.close(holder.conn);
LOG.info("connection recyle failed.");
}
} catch (Throwable e) {
holder.clearStatementCache();
if (!holder.discard) {
discardConnection(holder);
holder.discard = true;
}
LOG.error("recyle error", e);
recycleErrorCountUpdater.incrementAndGet(this);
}
}
boolean putLast(DruidConnectionHolder e, long lastActiveTimeMillis) {
// 可用连接数 > 池中支持的最大活连接数跃数 或者 discard标记为true 或者 连接已经关闭则不回收
if (poolingCount >= maxActive || e.discard || this.closed) {
return false;
}
// 标记当前连接的最后活跃时间为当前系统时间
e.lastActiveTimeMillis = lastActiveTimeMillis;
// 将这个holder封装的连接对象放入池中,并且 poolingCount++
connections[poolingCount] = e;
incrementPoolingCount();
// 标记峰值
if (poolingCount > poolingPeak) {
poolingPeak = poolingCount;
poolingPeakTime = lastActiveTimeMillis;
}
// 唤醒消费者 可以使用连接了
notEmpty.signal();
notEmptySignalCount++;
return true;
}
DruidDataSource#recycle回收连接只分析了连接放入连接池的操作,前面还有很多逻辑,holder.reset,事务的提交等等重要的逻辑
总结
Druid回收连接时会进行判断1、连接泄漏时直接丢掉连接;2、回收复用连接重新放入connections。