DBCP程式碼研讀以及就資料庫連線失效的解決
阿新 • • 發佈:2019-02-09
問題
網上很多評論說DBCP有很多BUG,但是都沒有指明是什麼BUG,只有一部分人說資料庫如果因為某種原因斷掉後再DBCP取道的連線都是失效的連線,而沒有重新取。就此研讀了一下DBCP的程式碼,共享之。
分析
DBCP使用apache的物件池ObjectPool作為連線池的實現,有以下主要的方法
Object borrowObject() throws Exception;從物件池取得一個有效物件
void returnObject(Object obj) throws Exception;使用完的物件放回物件池
void invalidateObject(Object obj) throws Exception;使物件失效
void addObject() throws Exception;生成一個新物件
ObjectPool的一個實現就是GenericObjectPool,這個類使用物件工廠PoolableObjectFactory實現物件的生成,失效檢查等等功能,以其實現資料庫連線工廠PoolableConnectionFactory做以說明,主要方法:
Object makeObject() throws Exception; 使用ConnectionFactory生成新連線
void destroyObject(Object obj) throws Exception;關閉連線
boolean validateObject(Object obj); 驗證連線是否有效,如果_validationQuery不空,則使用該屬性作為驗證連線是否有效的sql語句,查詢資料庫
void activateObject(Object obj) throws Exception;啟用連線物件
void passivateObject(Object obj) throws Exception; 關閉連線生成過的Statement和ResultSet,使連線處於非活動狀態
而GenericObjectPool有幾個主要屬性
_timeBetweenEvictionRunsMillis:失效檢查執行緒執行時間間隔,預設-1
_maxIdle:物件池中物件最大個數
_minIdle:物件池中物件最小個數
_maxActive:可以從物件池中取出的物件最大個數,為0則表示沒有限制,預設為8
在構造GenericObjectPool時,會生成一個內嵌類Evictor,實現自Runnable介面。如果_timeBetweenEvictionRunsMillis大於0,每過_timeBetweenEvictionRunsMillis毫秒Evictor會呼叫evict()方法,檢查物件的閒置時間是否大於 _minEvictableIdleTimeMillis毫秒(_minEvictableIdleTimeMillis小於等於0時則忽略,預設為30分鐘),是則銷燬此物件,否則就啟用並校驗物件,然後呼叫ensureMinIdle方法檢查確保池中物件個數不小於_minIdle。在呼叫returnObject方法把物件放回物件池,首先檢查該物件是否有效,然後呼叫PoolableObjectFactory 的passivateObject方法使物件處於非活動狀態。再檢查物件池中物件個數是否小於_maxIdle,是則可以把此物件放回物件池,否則銷燬此物件
還有幾個很重要的屬性,_testOnBorrow、_testOnReturn、_testWhileIdle ,這些屬性的意義是取得、返回物件和空閒時是否進行驗證,檢查物件是否有效,預設都為false即不驗證。所以當使用DBCP時,資料庫連線因為某種原因斷掉後,再從連線池中取得連線又不進行驗證,這時取得的連線實際已經時無效的資料庫連線了。網上很多說DBCP的bug應該都是如此吧,只有把這些屬性設為true,再提供_validationQuery語句就可以保證資料庫連線始終有效了,oracle資料庫可以使用SELECT COUNT(*) FROM DUAL,不過DBCP要求_validationQuery語句查詢的記錄集必須不為空,可能這也可以算一個小小的BUG,其實只要_validationQuery語句執行通過就可以了。
注意事項
所以使用DBCP連線池放必須注意構造GenericObjectPool物件時
validationQuery:SELECT COUNT(*) FROM DUAL
_testOnBorrow、_testOnReturn、_testWhileIdle:最好都設為true
_minEvictableIdleTimeMillis:大於0 ,進行連線空閒時間判斷,或為0,對空閒的連線不進行驗證
_timeBetweenEvictionRunsMillis:失效檢查執行緒執行時間間隔,如果小於等於0,不會啟動檢查執行緒
網上很多評論說DBCP有很多BUG,但是都沒有指明是什麼BUG,只有一部分人說資料庫如果因為某種原因斷掉後再DBCP取道的連線都是失效的連線,而沒有重新取。就此研讀了一下DBCP的程式碼,共享之。
分析
DBCP使用apache的物件池ObjectPool作為連線池的實現,有以下主要的方法
Object borrowObject() throws Exception;從物件池取得一個有效物件
void returnObject(Object obj) throws Exception;使用完的物件放回物件池
void invalidateObject(Object obj) throws Exception;使物件失效
void addObject() throws Exception;生成一個新物件
ObjectPool的一個實現就是GenericObjectPool,這個類使用物件工廠PoolableObjectFactory實現物件的生成,失效檢查等等功能,以其實現資料庫連線工廠PoolableConnectionFactory做以說明,主要方法:
Object makeObject() throws Exception; 使用ConnectionFactory生成新連線
void destroyObject(Object obj) throws Exception;關閉連線
boolean validateObject(Object obj); 驗證連線是否有效,如果_validationQuery不空,則使用該屬性作為驗證連線是否有效的sql語句,查詢資料庫
void activateObject(Object obj) throws Exception;啟用連線物件
void passivateObject(Object obj) throws Exception; 關閉連線生成過的Statement和ResultSet,使連線處於非活動狀態
而GenericObjectPool有幾個主要屬性
_timeBetweenEvictionRunsMillis:失效檢查執行緒執行時間間隔,預設-1
_maxIdle:物件池中物件最大個數
_minIdle:物件池中物件最小個數
_maxActive:可以從物件池中取出的物件最大個數,為0則表示沒有限制,預設為8
在構造GenericObjectPool時,會生成一個內嵌類Evictor,實現自Runnable介面。如果_timeBetweenEvictionRunsMillis大於0,每過_timeBetweenEvictionRunsMillis毫秒Evictor會呼叫evict()方法,檢查物件的閒置時間是否大於 _minEvictableIdleTimeMillis毫秒(_minEvictableIdleTimeMillis小於等於0時則忽略,預設為30分鐘),是則銷燬此物件,否則就啟用並校驗物件,然後呼叫ensureMinIdle方法檢查確保池中物件個數不小於_minIdle。在呼叫returnObject方法把物件放回物件池,首先檢查該物件是否有效,然後呼叫PoolableObjectFactory 的passivateObject方法使物件處於非活動狀態。再檢查物件池中物件個數是否小於_maxIdle,是則可以把此物件放回物件池,否則銷燬此物件
還有幾個很重要的屬性,_testOnBorrow、_testOnReturn、_testWhileIdle
注意事項
所以使用DBCP連線池放必須注意構造GenericObjectPool物件時
validationQuery:SELECT COUNT(*) FROM DUAL
_testOnBorrow、_testOnReturn、_testWhileIdle:最好都設為true
_minEvictableIdleTimeMillis:大於0 ,進行連線空閒時間判斷,或為0,對空閒的連線不進行驗證
_timeBetweenEvictionRunsMillis:失效檢查執行緒執行時間間隔,如果小於等於0,不會啟動檢查執行緒