boneCP的連接管理


轉載請標明鏈接:http://www.cnblogs.com/wingsless/p/6349434.html

boneCP連接的實現

boneCP自己實現了標准的java.sql.Connection接口,除了會持有Connection對象之外,還會擁有一些屬性用於標記連接的創建時間,空閑時間等。

比較重要的時間概念代碼如下:

if (!recreating){
  //上次使用時間戳
    connectionLastUsedInMs = System.currentTimeMillis();
  //上次重置時間戳
  connectionLastResetInMs = System.currentTimeMillis();
  //連接創建時間
  connectionCreationTimeInMs = System.currentTimeMillis();
}

boneCP對連接的管理

MySQL對連接有最大空閑時間的限制,默認是8小時,因此連接池在將連接分配給客戶端時,應該保證連接的可用性。

一般會有兩種做法:分配時測試和定時測試。

分配時測試:在收到客戶端請求時,連接池首先對向數據庫發送一條簡單的SQL,判斷連接是否可用。

定時測試:啟動一個測試線程(ConnectionTesterThread),定時每隔一段時間向數據庫發送命令,判斷連接是否可用。

boneCP采用定時測試的方式保證連接的可用。為了實現該方式,boneCP規定了兩個重要的參數:idleConnectionTestPeriodInSeconds(默認4小時)和idleMaxAgeInSeconds(默認1小時),分別表示空閑連接探測周期和連接最大可空閑時間。

默認情況下,boneCP啟動的keepalive線程每個1小時會啟動一次,用於檢查連接是否達到了空閑時間的上限:

代碼片段1:

if (connection.isPossiblyBroken() || 
	((this.idleMaxAgeInMs > 0) && ( System.currentTimeMillis()-connection.getConnectionLastUsedInMs() > this.idleMaxAgeInMs))){
	// kill off this connection - it's broken or it has been idle for too long
	closeConnection(connection);
	continue;
}

如果一個線程距離上次使用已經過去了1小時以上,則會在這段邏輯中被close掉,然后繼續循環掃描其他的連接。

一個連接被close掉之后,boneCP會有其他的線程負責新建連接。因此表現在MySQL客戶端上,可以看到每隔1小時,就會關閉一些連接並出現一些新的連接(極端情況下,所有的連接都被關閉,並一次性重建所有連接)。注意新建連接的MySQL分配id和舊連接完全不同。

需要注意的是,在默認情況下,並沒有觀察到邏輯執行到這里的現象:

代碼片段2:

if (this.idleConnectionTestPeriodInMs > 0 && (currentTimeInMs-connection.getConnectionLastUsedInMs() > this.idleConnectionTestPeriodInMs) &&
	(currentTimeInMs-connection.getConnectionLastResetInMs() >= this.idleConnectionTestPeriodInMs)) {
	// send a keep-alive, close off connection if we fail.
	if (!this.pool.isConnectionHandleAlive(connection)){
		closeConnection(connection);
		continue; 
	}
	// calculate the next time to wake up
	tmp = this.idleConnectionTestPeriodInMs;
	if (this.idleMaxAgeInMs > 0){ // wake up earlier for the idleMaxAge test?
		tmp = Math.min(tmp, this.idleMaxAgeInMs);
		}
}

這段邏輯主要判斷是否有連接的上一次重置時間距現在超過4小時,如果有,則向MySQL發一個探測命令,並且將連接的最后一次重置時間設為當前時間,如果連接alive,返回true,不對連接進行close操作。

上一段代碼也是ConnectionTesterThread的邏輯,推斷應該是因為每隔1小時,連接就會被關閉重建一次,因此不會存在滿足這段邏輯條件的連接存在。

如果修改默認值,將idleConnectionTestPeriodInSeconds和idleMaxAgeInSeconds的值對調,那么boneCP仍會每隔1小時(即idleConnectionTestPeriodInSeconds時間)定時調度keepalive線程。

此時可以發現上述兩段邏輯都會被執行,每次執行的時候,都會首先執行代碼片段2中的邏輯,因此每次都會更新ConnectionHandler的最后一次重置時間,但是連接仍然不會生存超過4小時,每4小時,邏輯就會進入代碼片段1中,將連接close掉。

jdbc驅動的NonRegistingDriver分析

現在發現系統運行一段時間以后就會出現fullGC,從內存分析上看,大部分內存都被com.mysql.jdbc.NonRegistingDriver占去。通過跟蹤jdbc代碼發現,當connection建立的時候,jdbc總會將該connection交給NonRegistingDriver,建立一個虛引用,並將該虛引用放在一個ConcurrentHashMap中。

代碼片段3:

protected static void trackConnection(Connection newConn) {
    ConnectionPhantomReference phantomRef = new ConnectionPhantomReference((ConnectionImpl) newConn, refQueue);
    connectionPhantomRefs.put(phantomRef, phantomRef);
}

內存分析中發現很多內存正是被NonRegistingDriver中的ConcurrentHashMap占去,因此可以推斷,應該是新建了大量的Connection導致了大量的NonRegistingDriver對象被新建,從而引發了內存問題。

綜合上面對boneCP的分析,應該是boneCP定時的將連接close掉再重建導致的,如果在不是很繁忙的系統上,該情況應該會比較嚴重。

boneCP探測連接可用的方式

在沒有設置探測SQL的情況下,boneCP利用jdbc的getMetaData方法,獲取connection的元數據,從其Javadoc上看,元數據應該包括了數據庫的表,SQL語法,存儲過程等等信息:

The metadata includes information about the database's tables, its supported SQL grammar, its stored procedures, the capabilities of this connection, and so on.

經過抓包分析,實際上getMetaData方法向MySQL 發送了一條簡單的show tables命令,如果收到response則認為連接是alive的。

轉載請標明鏈接:http://www.cnblogs.com/wingsless/p/6349434.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM