本文從 Commons DBCP testOnBorrow 的作用機(jī)制著手,管中窺豹,從一點(diǎn)去分析數(shù)據(jù)庫連接池獲取的過程以及架構(gòu)分層設(shè)計(jì)。
以下內(nèi)容會(huì)按照每層的作用,貫穿分析整個(gè)調(diào)用流程。
1??框架層 commons-pool
The indication of whether objects will be validated before being borrowed from the pool.
If the object fAIls to validate, it will be dropped from the pool, and we will attempt to borrow another.
testOnBorrow 不是 dbcp 定義的,是 commons-pool 定義的。commons-pool 詳細(xì)的定義了資源池使用的一套規(guī)范和運(yùn)行流程。
/**
* Borrow an object from the pool. get object from 資源池
* @see org.Apache.commons.pool2.impl.GenericObjectPool#borrowObject(long)
*/
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
PooledObject<T> p = null;
// if validation fails, the instance is destroyed and the next available instance is examined.
// This continues until either a valid instance is returned or there are no more idle instances available.
while (p == null) {
// If there is one or more idle instance available in the pool,
// then an idle instance will be selected based on the value of getLifo(), activated and returned.
p = idleObjects.pollFirst();
if (p != null) {
// 設(shè)置 testOnBorrow 就會(huì)進(jìn)行可用性校驗(yàn)
if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
boolean validate = false;
Throwable validationThrowable = null;
try {
// 具體的校驗(yàn)實(shí)現(xiàn)由實(shí)現(xiàn)類完成。
// see org.apache.commons.dbcp2.PoolableConnectionFactory
validate = factory.validateObject(p);
} catch (final Throwable t) {
PoolUtils.checkRethrow(t);
validationThrowable = t;
}
if (!validate) {
try {
// 如果校驗(yàn)異常,會(huì)銷毀該資源。
// obj is not valid and should be dropped from the pool
destroy(p);
destroyedByBorrowValidationCount.incrementAndGet();
} catch (final Exception e) {
// Ignore - validation failure is more important
}
p = null;
}
}
}
}
return p.getObject();
}
2??應(yīng)用層 commons-dbcp
dbcp 是特定于管理數(shù)據(jù)庫連接的資源池。
PoolableConnectionFactory is a PooledObjectFactory
PoolableConnection is a PooledObject
/**
* @see PoolableConnectionFactory#validateObject(PooledObject)
*/
@Override
public boolean validateObject(final PooledObject<PoolableConnection> p) {
try {
/**
* 檢測資源池對象的創(chuàng)建時(shí)間,是否超過生存時(shí)間
* 如果超過 maxConnLifetimeMillis, 不再委托數(shù)據(jù)庫連接進(jìn)行校驗(yàn),直接廢棄改資源
* @see PoolableConnectionFactory#setMaxConnLifetimeMillis(long)
*/
validateLifetime(p);
// 委托數(shù)據(jù)庫連接進(jìn)行自我校驗(yàn)
validateConnection(p.getObject());
return true;
} catch (final Exception e) {
return false;
}
}
/**
* 數(shù)據(jù)庫連接層的校驗(yàn)。具體到是否已關(guān)閉、是否與 server 連接可用
* @see Connection#isValid(int)
*/
public void validateConnection(final PoolableConnection conn) throws SQLException {
if(conn.isClosed()) {
throw new SQLException("validateConnection: connection closed");
}
conn.validate(_validationQuery, _validationQueryTimeout);
}
3??基礎(chǔ)層 MySQL-connector-JAVA
Returns true if the connection has not been closed and is still valid.
這個(gè)是 java.sql.Connection 定義的規(guī)范。具體實(shí)現(xiàn)根據(jù)對應(yīng)數(shù)據(jù)庫的 driver 來完成。使用某種機(jī)制用來探測連接是否可用。
/**
* 調(diào)用 com.mysql.jdbc.MysqlIO, 發(fā)送ping 請求,檢測是否可用
* 對比 H2 數(shù)據(jù)庫,是通過獲取當(dāng)前事務(wù)級(jí)別來檢測連接是否可以。但是忽略了 timeout 配置,畢竟是 demo 數(shù)據(jù)庫