周末整理了一下init方法的整个流程,这里顺便贴一下在文章中,以免之后遗漏了。
然后我们继续来讨论源码:之前讲到过,创建连接是采用异步方式,进入到DruidDataSource.CreateConnectionThread.run()。当不需要创建连接时,该线程进入empty.await()状态,此时需要用户线程调用empty.signal()来唤醒。
public void run() {
// 用于唤醒初始化数据源的线程
initedLatch.countDown();
long lastDiscardCount = 0;
// 注意,这里是死循环,当需要创建连接对象时,这个线程会受到signal,否则会一直await
for (;;) {
// 加锁
try {
lock.lockInterruptibly();
} catch (InterruptedException e2) {
break;
}
// 丢弃数量discardCount
long discardCount = DruidDataSource.this.discardCount;
boolean discardChanged = discardCount - lastDiscardCount > 0;
lastDiscardCount = discardCount;
try {
// 这个变量代表了是否有必要新增连接,true代表没必要
boolean emptyWait = true;
if (createError != null
&& poolingCount == 0
&& !discardChanged) {
emptyWait = false;
}
if (emptyWait
&& asyncInit && createCount < initialSize) {
emptyWait = false;
}
if (emptyWait) {
// 必须存在线程等待,才创建连接
if (poolingCount >= notEmptyWaitThreadCount //
&& (!(keepAlive && activeCount + poolingCount < minIdle))
&& !isFailContinuous()
) {
// 等待signal,前面已经讲到,当某线程需要创建连接时,会发送signal给它
empty.await();
}
// 防止创建超过maxActive数量的连接
if (activeCount + poolingCount >= maxActive) {
empty.await();
continue;
}
}
} catch (InterruptedException e) {
lastCreateError = e;
lastErrorTimeMillis = System.currentTimeMillis();
break;
} finally {
// 解锁
lock.unlock();
}
PhysicalConnectionInfo connection = null;
try {
// 创建原生的连接对象,并包装
connection = createPhysicalConnection();
} catch (SQLException e) {
//出现SQLException会继续往下走
//······
} catch (RuntimeException e) {
// 出现RuntimeException则重新进入循环体
LOG.error("create connection RuntimeException", e);
setFailContinuous(true);
continue;
} catch (Error e) {
LOG.error("create connection Error", e);
setFailContinuous(true);
break;
}
// 如果为空,重新进入循环体
if (connection == null) {
continue;
}
// 将连接对象包装为DruidConnectionHolder,并放入connections数组中
// 注意,该方法会去调用notEmpty.signal(),即会去唤醒正在等待获取连接的线程
boolean result = put(connection);
}
}
testOnBorrow 和testWhileIdle参数的应用
// 如果开启了testOnBorrow
if (testOnBorrow) {
// 这里会去调用validConnectionChecker的isValidConnection方法来校验,validConnectionChecker不存在的话,则以普通JDBC方式校验
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
Connection realConnection = poolableConnection.conn;
// 丢弃连接,丢弃完会发送signal给CreateConnectionThread来创建连接
discardConnection(realConnection);
continue;
}
} else {
Connection realConnection = poolableConnection.conn;
if (poolableConnection.conn.isClosed()) {
discardConnection(null); // 传入null,避免重复关闭
continue;
}
if (testWhileIdle) {
final DruidConnectionHolder holder = poolableConnection.holder;
// 当前时间
long currentTimeMillis = System.currentTimeMillis();
// 最后活跃时间
long lastActiveTimeMillis = holder.lastActiveTimeMillis;
long lastKeepTimeMillis = holder.lastKeepTimeMillis;
if (lastKeepTimeMillis > lastActiveTimeMillis) {
lastActiveTimeMillis = lastKeepTimeMillis;
}
// 计算连接对象空闲时长
long idleMillis = currentTimeMillis - lastActiveTimeMillis;
long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
// 空闲检测周期
if (timeBetweenEvictionRunsMillis <= 0) {
timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
}
// 当前连接空闲时长大于空间检测周期时,进入检测
if (idleMillis >= timeBetweenEvictionRunsMillis
|| idleMillis < 0 // unexcepted branch
) {
// 接下来的逻辑和前面testOnBorrow一样的
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
discardConnection(realConnection);
continue;
}
}
}
}
进入DruidDataSource.getConnectionDirect(),这里不会进行检测,只是将连接对象放入activeConnections,具体泄露连接的检测工作是在DestroyConnectionThread线程中进行
if (removeAbandoned) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
poolableConnection.connectStackTrace = stackTrace;
// 记录连接借出时间
poolableConnection.setConnectedTimeNano();
poolableConnection.traceEnable = true;
activeConnectionLock.lock();
try {
// 放入activeConnections
activeConnections.put(poolableConnection, PRESENT);
} finally {
activeConnectionLock.unlock();
}
}
进入DruidDataSource.removeAbandoned(),当连接对象使用时间超过removeAbandonedTimeoutMillis,则会被丢弃掉。
public int removeAbandoned() {
int removeCount = 0;
long currrentNanos = System.nanoTime();
List<DruidPooledConnection> abandonedList = new ArrayList<DruidPooledConnection>();
// 加锁
activeConnectionLock.lock();
try {
Iterator<DruidPooledConnection> iter = activeConnections.keySet().iterator();
// 遍历借出的连接
for (; iter.hasNext();) {
DruidPooledConnection pooledConnection = iter.next();
if (pooledConnection.isRunning()) {
continue;
}
// 计算连接对象使用时间
long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);
// 如果超过设置的丢弃超时时间,则加入abandonedList
if (timeMillis >= removeAbandonedTimeoutMillis) {
iter.remove();
pooledConnection.setTraceEnable(false);
abandonedList.add(pooledConnection);
}
}
} finally {
// 解锁
activeConnectionLock.unlock();
}
// 遍历需要丢弃的连接对象
if (abandonedList.size() > 0) {
for (DruidPooledConnection pooledConnection : abandonedList) {
final ReentrantLock lock = pooledConnection.lock;
// 加锁
lock.lock();
try {
// 如果该连接已经失效,则继续循环
if (pooledConnection.isDisable()) {
continue;
}
} finally {
// 解锁
lock.unlock();
}
// 关闭连接
JdbcUtils.close(pooledConnection);
pooledConnection.abandond();
removeAbandonedCount++;
removeCount++;
}
}
return removeCount;
}