HikariDataSource#getConnection → HikariPool#getConnection
public Connection getConnection(final long hardTimeout) throws SQLException
{
suspendResumeLock.acquire(); //获取信号量,这个是提供给jmx控制的功能 默认是空实现,可忽略
final long startTime = currentTime();
try {
long timeout = hardTimeout;
do {
//从connectionBag获取连接,如果超时则直接退出
PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
if (poolEntry == null) {
break; // We timed out... break and throw exception
}
final long now = currentTime();
// 如果poolEntry被标记为evicted,则要关闭这个连接
// 如果poolEntry上次访问时间到现在超过 aliveBypassWindowsMs=500ms,需要检测下连接是否可用,如果不可用的话,关闭连接
if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
timeout = hardTimeout - elapsedMillis(startTime);
}
else {
metricsTracker.recordBorrowStats(poolEntry, startTime);
// 返回代理db连接
return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
}
} while (timeout > 0L);
metricsTracker.recordBorrowTimeoutStats(startTime);
throw createTimeoutException(startTime);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
}
finally {
suspendResumeLock.release();
}
}
connectionBag.borrow() 的过程参见 Hikari定制连接池容器ConcurrentBag
成功获取到 poolEntry 后时,leakTaskFactory.schedule(poolEntry)
会启动一个连接泄露的检测任务 ProxyLeakTask ,如果超过 leakDetectionThreshold(默认是0,不开启) 时长还未归还则认为这个连接泄露了
ProxyLeakTask schedule(final PoolEntry poolEntry)
{
// 默认是关闭的
return (leakDetectionThreshold == 0) ? ProxyLeakTask.NO_LEAK : scheduleNewTask(poolEntry);
}
private ProxyLeakTask scheduleNewTask(PoolEntry poolEntry) {
ProxyLeakTask task = new ProxyLeakTask(poolEntry);
//一次性任务,在leakDetectionThreshold后执行一次
task.schedule(executorService, leakDetectionThreshold);
return task;
}
executorService使用的是 houseKeepingExecutorService 管家调度线程池, 在连接从池子里拿出 leakDetectionThreshold 时间后执行一次 ProxyLeakTask#run()
public void run()
{
isLeaked = true;
final StackTraceElement[] stackTrace = exception.getStackTrace();
final StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 5];
System.arraycopy(stackTrace, 5, trace, 0, trace.length);
exception.setStackTrace(trace);
LOGGER.warn("Connection leak detection triggered for {} on thread {}, stack trace follows", connectionName, threadName, exception);
}
标识已经泄露了 isLeaked = true, 并打印 'Connection leak detection xxx' 警告日志
当连接 close() 归还回池子时,会取消这个 ProxyLeakTask#cancel()
void cancel()
{
scheduledFuture.cancel(false);
// 如果是被标记为”泄露"后再取消的,则打印个日志说明下
if (isLeaked) {
LOGGER.info("Previously reported leaked connection {} on thread {} was returned to the pool (unleaked)", connectionName, threadName);
}
}