穩,從資料庫連線池 testOnBorrow 看架構設計

2023-06-21 18:01:16

本文從 Commons DBCP testOnBorrow 的作用機制著手,管中窺豹,從一點去分析資料庫連線池獲取的過程以及架構分層設計。

以下內容會按照每層的作用,貫穿分析整個呼叫流程。

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 詳細的定義了資源池使用的一套規範和執行流程。

/**
 * 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) {
            // 設定 testOnBorrow 就會進行可用性校驗
			if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
				boolean validate = false;
				Throwable validationThrowable = null;
				try {
                    // 具體的校驗實現由實現類完成。
                    // see org.apache.commons.dbcp2.PoolableConnectionFactory
					validate = factory.validateObject(p);
				} catch (final Throwable t) {
					PoolUtils.checkRethrow(t);
					validationThrowable = t;
				}
				if (!validate) {
					try {
                        // 如果校驗異常,會銷燬該資源。
                        // 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️⃣應用層 commons-dbcp

dbcp 是特定於管理資料庫連線的資源池。

PoolableConnectionFactory is a PooledObjectFactory

PoolableConnection is a PooledObject

/**
 * @see PoolableConnectionFactory#validateObject(PooledObject)
 */
@Override
public boolean validateObject(final PooledObject<PoolableConnection> p) {
	try {
		/**
		 * 檢測資源池物件的建立時間,是否超過生存時間
		 * 如果超過 maxConnLifetimeMillis, 不再委託資料庫連線進行校驗,直接廢棄改資源
		 * @see PoolableConnectionFactory#setMaxConnLifetimeMillis(long)
		 */
		validateLifetime(p);
		// 委託資料庫連線進行自我校驗
		validateConnection(p.getObject());
		return true;
	} catch (final Exception e) {
		return false;
	}
}

/**
 * 資料庫連線層的校驗。具體到是否已關閉、是否與 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️⃣基礎層 mysql-connector-java

Returns true if the connection has not been closed and is still valid.

這個是 java.sql.Connection 定義的規範。具體實現根據對應資料庫的driver 來完成。使用某種機制用來探測連線是否可用。

/**
 * 呼叫 com.mysql.jdbc.MysqlIO, 傳送ping 請求,檢測是否可用
 * 對比 H2 資料庫,是通過獲取當前事務級別來檢測連線是否可以。但是忽略了 timeout 設定,畢竟是 demo 資料庫