![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SYmdjY2cTZxQ2MxITNyM2Y3YTMmVzY1UWNzQDOkBTNi9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
前言
本章是HikariCP的最後一章,學習HikariCP如何設計歸還連接配接和關閉連接配接。
一、歸還連接配接
因為暴露給使用者使用的是Connection的代理對象
HikariProxyConnection
,使用者調用
close
方法關閉連接配接,實際執行的是
ProxyConnection#close
歸還連接配接。
PoolEntry#recycle
HikariPool#recycle
将PoolEntry歸還給ConcurrentBag。
private final ConcurrentBag connectionBag;@Overridevoid recycle(final PoolEntry poolEntry) {
connectionBag.requite(poolEntry);
}
ConcurrentBag#requite
修改Entry狀态。
Hikari連接配接池如何實作借走和歸還?
- PoolEntry包裝Connection被放入ConcurrentBag
- 使用者借走通過PoolEntry構造的ProxyConnection
- 使用中…
- ProxyConnection.close修改ProxyConnection裡的委托對象為CLOSED_CONNECTION,拒絕使用者後續操作
- PoolEntry修改狀态,歸還給ConcurrentBag,PoolEntry就可以被其他人借走了
二、關閉連接配接
HouseKeeper
The house keeping task to retire and maintain minimum idle connections.
HouseKeeper
實作Runnable接口,負責執行連接配接過期并維持最小空閑連接配接。HikariCP啟動的過程中,HouseKeeper是在HikariPool構造時送出到線程池定期執行的。預設30秒執行一次,可以通過配置
com.zaxxer.hikari.housekeeping.periodMs
系統參數修改。
HouseKeeper
一個定時任務執行了很多事情。
1. JMX運作時修改配置,更新HikariPool的一些成員變量
2. 檢測時鐘倒退
previous
代表上次執行的時間戳,
housekeepingPeriodMs
預設30秒。正常情況下
now = previous + housekeepingPeriodMs
,如果
previous + housekeepingPeriodMs - now > 128
發生時鐘倒退超過128ms,則執行
softEvictConnections
軟驅逐所有連接配接,并直接傳回。
HikariPool#softEvictConnections
首先
poolEntry.markEvicted()
将PoolEntry标記為被驅逐,如果之後的操作沒成功,這個連接配接将在下次getConnection的時候被檢測到驅逐,進而關閉。
判斷調用者是否是
owner
,即是否是使用者。如果是使用者自己操作
HikariPool#evictConnection
方法驅逐連接配接,關閉連接配接(見3)。
執行
connectionBag.reserve(poolEntry)
,将PoolEntry的狀态通過CAS從
STATE_NOT_IN_USE
修改為
STATE_RESERVED
狀态,如果執行成功,關閉連接配接(見3)。
3. 關閉空閑連接配接
@Override
public void run() {
// ...
if (idleTimeout > 0L && config.getMinimumIdle() final List 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--;
}
}
}// ...
}
首先判斷目前配置
idleTimeout > 0L && config.getMinimumIdle() < config.getMaximumPoolSize()
是否支援空閑連接配接逾時關閉。要設定
idleTimeout
并且
MinimumIdle
要小于
MaximumPoolSize
。
接下來計算總共有多少連接配接需要關閉。待關閉數量(
toRemove
)=
STATE_NOT_IN_USE
的PoolEntry總數 - 配置的
MinimumIdle
數量。
循環所有未使用的PoolEntry,判斷 目前時間 - entry的上次使用時間 是否大于
idleTimeout
,并且執行
reserve
修改PoolEntry狀态成功。如果修改成功,執行關閉連接配接。
HikariPool#closeConnection
是關閉連接配接的公共入口。
connectionBag.remove(poolEntry)
修改PoolEntry狀态,從shareList和threadList中移除這個元素。
poolEntry.close()
關閉MaxLifeTime逾時檢測任務,一些變量賦空值幫助GC。
準備工作都做好以後,把關閉任務送出到線程池,執行關閉連接配接。如果連接配接池仍然是正常狀态(
POOL_NORMAL
),嘗試維持最小空閑連接配接(
fillPool()
見4)。
4. 維持最小空閑連接配接
業務上往往我們稱Hikari連接配接池的
MinimumIdle
為最小連接配接數,其實這是一個簡稱,全稱是
最小空閑連接配接數
。它是保證連接配接池中至少有x個連接配接是空閑的,拿來即可用,但是需要保證總連接配接數不會超過
MaximumPoolSize
。
首先計算需要添加多少連接配接。缺少最大連接配接和缺少空閑連接配接取小,扣除目前正在準備建立的連接配接(
addConnectionQueueReadOnlyView.size()
是線程池
addConnectionExecutor
的等待隊列視圖),就是
connectionsToAdd
需要建立的連接配接。
例如:配置MaximumPoolSize=10,MinimumIdle=5;此時總連接配接數=7,空閑連接配接數=4,等待建立連接配接線程數=0。根據公式計算:
Math.min(10 - 7, 5 - 4) - 0 = 1
,還需要建立一個連接配接,因為空閑連接配接數不夠5。如果按照最小連接配接數去了解,這裡應該不需要建立連接配接了,因為連接配接數已經有7個了。
最後把建立連接配接的任務送出到
addConnectionExecutor
執行。
HikariPool#getConnection
getConnection
方法在第四章介紹過,主要是擷取PoolEntry,并建立ProxyConnection給使用者使用。
注意當
poolEntry.isMarkedEvicted()
PoolEntry被标記未驅逐,或
!isConnectionAlive(poolEntry.connection)
連接配接存活檢測不通過時,會執行
closeConnection
關閉連接配接,關閉連接配接方法在上面講過。
MaxLifetime到期軟驅逐
當一個連接配接被建立之後,就會開啟一個延遲任務,檢測連接配接如果超過MaxLifetime則進行軟驅逐(
softEvictConnection
)。代碼見
HikariPool#createPoolEntry
方法。
連接配接池關閉
HikariDataSource#close
修改isShutdown狀态。
HikariPool#shutdown
關閉所有線程池,取消所有任務,關閉所有資料庫連接配接。
abortActiveConnections
中斷所有正在使用的連接配接,并将PoolEntry從connectionBag移除。
為什麼需要多次執行
abortActiveConnections
和
softEvictConnections
,直到
getTotalConnections<0
?
因為PoolEntry的狀态時刻發生變化,不多次嘗試可能導緻少關閉連接配接。
abortActiveConnections
隻負責
STATE_IN_USE
的PoolEntry;
softEvictConnections
隻負責
STATE_NOT_IN_USE
的PoolEntry。
總結
關于歸還連接配接,對于HikariCP如何設計連接配接借走和歸還有一個整體的了解,主要利用了代理和ConcurrentBag。
對于關閉連接配接,觸發點很多,一般都是通過軟驅逐或強制關閉兩種方式關閉。重點關注HouseKeeper這個30秒定時任務的作用。
原創不易,歡迎評論、點贊和關注。歡迎關注公衆号:程式猿阿越。