HikariPool 連接池在初始化的時候主要做了幾件事:
- 初始化底層的連接容器 ConcurrentBag
- checkFailFast() 嘗試創建一個db連接,如果失敗則直接拋出初始化異常 中斷初始化
- 初始化各類資源
public HikariPool(final HikariConfig config)
{
super(config);
// 1. 構建自定義的線程池容器 ConcurrentBag
this.connectionBag = new ConcurrentBag<>(this);
this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;
this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();
// 2. 創建連接,校驗有效性 快速失敗
checkFailFast();
// 如果傳入opentracing的 metricsTracker就可以上報連接池的一些指標信息
if (config.getMetricsTrackerFactory() != null) {
setMetricsTrackerFactory(config.getMetricsTrackerFactory());
}
else {
setMetricRegistry(config.getMetricRegistry());
}
setHealthCheckRegistry(config.getHealthCheckRegistry());
handleMBeans(this, true);
ThreadFactory threadFactory = config.getThreadFactory();
final int maxPoolSize = config.getMaximumPoolSize();
// 3. 連接新增線程池的 blockQueue,上限為 maxPoolSize
LinkedBlockingQueue<Runnable> addConnectionQueue = new LinkedBlockingQueue<>(maxPoolSize);
// 4. addConnectionQueueReadOnlyView 用來查看當前有幾個db連接任務
this.addConnectionQueueReadOnlyView = unmodifiableCollection(addConnectionQueue);
// 5. 構建負責新建db連接的線程池,1個工作線程、隊列上限maxPoolSize
this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardOldestPolicy());
// 6. 構建負責關閉db連接的線程池,1個工作線程、隊列上限maxPoolSize
this.closeConnectionExecutor = createThreadPoolExecutor(maxPoolSize, poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
// 7. 連接泄露檢測(leakDetectionThreshold默認為0,不開啟),db連接從池子中取出后開始計時,如果超過一定的時長還未歸還則認為可能發現連接泄露/慢查詢了
this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);
// 8. houseKeepingExecutor 定時任務,用來維護連接池內 minimumIdle個連接
// 間隔30ms檢查一次連接池,如果不足 minimumIlde則填充,超過minimumIlde~maxPoolSize的那部分連接如果空閑時長 > idleTimeout則關閉掉
this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(**new HouseKeeper()**, 100L, housekeepingPeriodMs, MILLISECONDS);
// 如果系統變量設置的阻塞等待 連接池填充完畢,則會等待 houseKeeperTask 把連接池填充完畢
if (Boolean.getBoolean("com.zaxxer.hikari.blockUntilFilled") && config.getInitializationFailTimeout() > 1) {
// 給負責新建db連接的線程池 加大火力
addConnectionExecutor.setCorePoolSize(Math.min(16, Runtime.getRuntime().availableProcessors()));
addConnectionExecutor.setMaximumPoolSize(Math.min(16, Runtime.getRuntime().availableProcessors()));
final long startTime = currentTime();
while (elapsedMillis(startTime) < config.getInitializationFailTimeout() && getTotalConnections() < config.getMinimumIdle()) {
quietlySleep(MILLISECONDS.toMillis(100));
}
addConnectionExecutor.setCorePoolSize(1);
addConnectionExecutor.setMaximumPoolSize(1);
}
}
幾個關鍵對象:
- addConnectionExecutor : 負責新建db連接的線程池 1個工作線程、隊列上限maxPoolSize
- addConnectionQueueReadOnlyView : 是 addConnectionExecutor 的阻塞隊列視圖,用來獲取當前有幾個db連接等待創建,避免提交過多新建任務導致超過max
- closeConnectionExecutor: 負責關閉db連接的線程池 1個工作線程、隊列上限maxPoolSize
- leakTaskFactory : db連接泄露任務工廠,在db連接申請成功后創建連接泄露檢查任務,如果超過一定時長為歸還 則認為連接泄露了(沒有close 或者慢查詢) 默認 leakDetectionThreshold = 0 不開啟
- houseKeeperTask : ”大管家“定時任務,定時檢查連接池內連接數,維持到 minimumIdle 的數量 HouseKeeper
HouseKeeper#run() 為了維持連接池的連接數量穩定在 minimumIdle 個
- 池內空閑的連接, 超過 minimumIdle 的那部分,如果空閑超過 idleTimeout 則清除掉
String afterPrefix = "Pool ";
if (idleTimeout > 0L && config.getMinimumIdle() < config.getMaximumPoolSize()) {
logPoolState("Before cleanup ");
afterPrefix = "After cleanup ";
final List<PoolEntry> notInUse = connectionBag.values(**STATE_NOT_IN_USE**);
int toRemove = notInUse.size() - config.getMinimumIdle();
for (PoolEntry entry : notInUse) {
if (toRemove > 0 && elapsedMillis(entry.lastAccessed, now) > idleTimeout && connectionBag.reserve(entry)) {
closeConnection(entry, "(connection has passed idleTimeout)");
toRemove--;
}
}
}
- 調用
fillPool();
空閑db連接數補足到 minimumIdle 個
private synchronized void fillPool()
{
final int connectionsToAdd = Math.min(config.getMaximumPoolSize() - getTotalConnections(), config.getMinimumIdle() - getIdleConnections()) - addConnectionQueueReadOnlyView.size(); //這時候 addConnectionExecutor 的隊列視圖就有用了
if (connectionsToAdd <= 0) logger.debug("{} - Fill pool skipped, pool is at sufficient level.", poolName);
for (int i = 0; i < connectionsToAdd; i++) {
addConnectionExecutor.submit((i < connectionsToAdd - 1) ? poolEntryCreator : postFillPoolEntryCreator);
}
}
- 待填充的db連接個數 = Math.min( 「最大連接數 - 總連接數」, 「最小空閑連接數 - 當前空閑連接數」) - 等待新建db連接的連接數
這邊會扣除正在進行的連接新建任務數 addConnectionQueueReadOnlyView.size() 避免重復新增 - 提交給 addConnectionExecutor 單線程池 新建連接