最近在看一些dbcp的相關(guān)內(nèi)容,順便做一下記錄,免得自己給忘記了。
1. 引入dbcp (選擇1.4)
<dependency> <groupId>com.alibaba.external</groupId> <artifactId>jakarta.commons.dbcp</artifactId> <version>1.4</version> </dependency>
?
2. dbcp的基本配置
相關(guān)配置說明:
?
-
initialSize :連接池啟動(dòng)時(shí)創(chuàng)建的初始化連接數(shù)量(默認(rèn)值為0)
-
maxActive
:連接池中可同時(shí)連接的最大的連接數(shù)(默認(rèn)值為8,調(diào)整為20,高峰單機(jī)器在20并發(fā)左右,自己根據(jù)應(yīng)用場(chǎng)景定)
-
maxIdle:連接池中最大的空閑的連接數(shù),超過的空閑連接將被釋放,如果設(shè)置為負(fù)數(shù)表示不限制(默認(rèn)為8個(gè),maxIdle不能設(shè)置太小,因?yàn)榧偃缭诟哓?fù)載的情況下,連接的打開時(shí)間比關(guān)閉的時(shí)間快,會(huì)引起連接池中idle的個(gè)數(shù) 上升超過maxIdle,而造成頻繁的連接銷毀和創(chuàng)建,類似于jvm參數(shù)中的Xmx設(shè)置)
-
minIdle:連接池中最小的空閑的連接數(shù),低于這個(gè)數(shù)量會(huì)被創(chuàng)建新的連接(默認(rèn)為0,調(diào)整為5,該參數(shù)越接近maxIdle,性能越好,因?yàn)檫B接的創(chuàng)建和銷毀,都是需要消耗資源的;但是不能太大,因?yàn)樵跈C(jī)器很空閑的時(shí)候,也會(huì)創(chuàng)建低于minidle個(gè)數(shù)的連接,類似于jvm參數(shù)中的Xmn設(shè)置)
-
maxWait
?:最大等待時(shí)間,當(dāng)沒有可用連接時(shí),連接池等待連接釋放的最大時(shí)間,超過該時(shí)間限制會(huì)拋出異常,如果設(shè)置-1表示無限等待(默認(rèn)為無限,調(diào)整為60000ms,避免因線程池不夠用,而導(dǎo)致請(qǐng)求被無限制掛起)
-
poolPreparedStatements:開啟池的prepared(默認(rèn)是false,未調(diào)整,經(jīng)過測(cè)試,開啟后的性能沒有關(guān)閉的好。)
-
maxOpenPreparedStatements:開啟池的prepared 后的同時(shí)最大連接數(shù)(默認(rèn)無限制,同上,未配置)
-
minEvictableIdleTimeMillis
?:連接池中連接,在時(shí)間段內(nèi)一直空閑, 被逐出連接池的時(shí)間
-
(默認(rèn)為30分鐘,可以適當(dāng)做調(diào)整,需要和后端服務(wù)端的策略配置相關(guān))
-
removeAbandonedTimeout
?:超過時(shí)間限制,回收沒有用(廢棄)的連接(默認(rèn)為 300秒,調(diào)整為180)
-
removeAbandoned
?:超過removeAbandonedTimeout時(shí)間后,是否進(jìn) 行沒用連接(廢棄)的回收(默認(rèn)為false,調(diào)整為true)
removeAbandoned參數(shù)解釋:
-
如果開啟了removeAbandoned,當(dāng)getNumIdle() < 2) and (getNumActive() > getMaxActive() - 3)時(shí)被觸發(fā).
-
舉例當(dāng)maxActive=20, 活動(dòng)連接為18,空閑連接為1時(shí)可以觸發(fā)"removeAbandoned".但是活動(dòng)連接只有在沒有被使用的時(shí)間超 過"removeAbandonedTimeout"時(shí)才被回收
-
logAbandoned: 標(biāo)記當(dāng)連接被回收時(shí)是否打印程序的stack traces日志(默認(rèn)為false,未調(diào)整)
一般會(huì)是幾種情況出現(xiàn)需要
removeAbandoned
:
-
代碼未在
finally
釋放
connection
,
不過我們都用
sqlmapClientTemplate
,底層都有鏈接釋放的過程
-
遇到數(shù)據(jù)庫死鎖
。以前遇到過后端存儲(chǔ)過程做了鎖表操作,導(dǎo)致前臺(tái)集群中連接池全都被
block
住,后續(xù)的業(yè)務(wù)處理因?yàn)槟貌坏芥溄铀卸继幚硎×恕?
一份優(yōu)化過的配置:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="xxxx" />
<property name="username"><value>xxxx</value></property>
<property name="password"><value>xxxxx</value></property>
<property name="maxActive"><value>20</value></property>
<property name="initialSize"><value>1</value></property>
<property name="maxWait"><value>60000</value></property>
<property name="maxIdle"><value>20</value></property>
<property name="minIdle"><value>3</value></property>
<property name="removeAbandoned"><value>true</value></property>
<property name="removeAbandonedTimeout"><value>180</value></property>
<property name="connectionProperties"><value>clientEncoding=GBK</value></property>
</bean>
?
?
2. dbcp的鏈接validate配置
-
dbcp是采用了commons-pool做為其連接池管理,testOnBorrow,testOnReturn, testWhileIdle是pool是提供的幾種校驗(yàn)機(jī)制,通過外部鉤子的方式回調(diào)dbcp的相關(guān)數(shù)據(jù)庫鏈接(validationQuery)校驗(yàn)
-
dbcp相關(guān)外部鉤子類:PoolableConnectionFactory,繼承于common-pool PoolableObjectFactory
-
dbcp通過GenericObjectPool這一入口,進(jìn)行連接池的borrow,return處理
-
testOnBorrow : 顧明思義,就是在進(jìn)行borrowObject進(jìn)行處理時(shí),對(duì)拿到的connection進(jìn)行validateObject校驗(yàn)
-
testOnReturn : 顧明思義,就是在進(jìn)行returnObject對(duì)返回的connection進(jìn)行validateObject校驗(yàn),個(gè)人覺得對(duì)數(shù)據(jù)庫連接池的管理意義不大
-
testWhileIdle
: 關(guān)注的重點(diǎn),GenericObjectPool中針對(duì)pool管理,起了一個(gè)Evict的TimerTask定時(shí)線程進(jìn)行控制(可通過設(shè)置參數(shù)timeBetweenEvictionRunsMillis>0),定時(shí)對(duì)線程池中的鏈接進(jìn)行validateObject校驗(yàn),對(duì)無效的鏈接進(jìn)行關(guān)閉后,會(huì)調(diào)用ensureMinIdle,適當(dāng)建立鏈接保證最小的minIdle連接數(shù)。
-
timeBetweenEvictionRunsMillis,設(shè)置的Evict線程的時(shí)間,單位ms,大于0才會(huì)開啟evict檢查線程
-
validateQuery, 代表檢查的sql
-
validateQueryTimeout, 代表在執(zhí)行檢查時(shí),通過statement設(shè)置,statement.setQueryTimeout(validationQueryTimeout)
-
numTestsPerEvictionRun,代表每次檢查鏈接的數(shù)量,建議設(shè)置和maxActive一樣大,這樣每次可以有效檢查所有的鏈接.
?
- initialSize :連接池啟動(dòng)時(shí)創(chuàng)建的初始化連接數(shù)量(默認(rèn)值為0)
- maxActive :連接池中可同時(shí)連接的最大的連接數(shù)(默認(rèn)值為8,調(diào)整為20,高峰單機(jī)器在20并發(fā)左右,自己根據(jù)應(yīng)用場(chǎng)景定)
- maxIdle:連接池中最大的空閑的連接數(shù),超過的空閑連接將被釋放,如果設(shè)置為負(fù)數(shù)表示不限制(默認(rèn)為8個(gè),maxIdle不能設(shè)置太小,因?yàn)榧偃缭诟哓?fù)載的情況下,連接的打開時(shí)間比關(guān)閉的時(shí)間快,會(huì)引起連接池中idle的個(gè)數(shù) 上升超過maxIdle,而造成頻繁的連接銷毀和創(chuàng)建,類似于jvm參數(shù)中的Xmx設(shè)置)
- minIdle:連接池中最小的空閑的連接數(shù),低于這個(gè)數(shù)量會(huì)被創(chuàng)建新的連接(默認(rèn)為0,調(diào)整為5,該參數(shù)越接近maxIdle,性能越好,因?yàn)檫B接的創(chuàng)建和銷毀,都是需要消耗資源的;但是不能太大,因?yàn)樵跈C(jī)器很空閑的時(shí)候,也會(huì)創(chuàng)建低于minidle個(gè)數(shù)的連接,類似于jvm參數(shù)中的Xmn設(shè)置)
- maxWait ?:最大等待時(shí)間,當(dāng)沒有可用連接時(shí),連接池等待連接釋放的最大時(shí)間,超過該時(shí)間限制會(huì)拋出異常,如果設(shè)置-1表示無限等待(默認(rèn)為無限,調(diào)整為60000ms,避免因線程池不夠用,而導(dǎo)致請(qǐng)求被無限制掛起)
- poolPreparedStatements:開啟池的prepared(默認(rèn)是false,未調(diào)整,經(jīng)過測(cè)試,開啟后的性能沒有關(guān)閉的好。)
- maxOpenPreparedStatements:開啟池的prepared 后的同時(shí)最大連接數(shù)(默認(rèn)無限制,同上,未配置)
- minEvictableIdleTimeMillis ?:連接池中連接,在時(shí)間段內(nèi)一直空閑, 被逐出連接池的時(shí)間
- (默認(rèn)為30分鐘,可以適當(dāng)做調(diào)整,需要和后端服務(wù)端的策略配置相關(guān))
- removeAbandonedTimeout ?:超過時(shí)間限制,回收沒有用(廢棄)的連接(默認(rèn)為 300秒,調(diào)整為180)
- removeAbandoned ?:超過removeAbandonedTimeout時(shí)間后,是否進(jìn) 行沒用連接(廢棄)的回收(默認(rèn)為false,調(diào)整為true)
- 如果開啟了removeAbandoned,當(dāng)getNumIdle() < 2) and (getNumActive() > getMaxActive() - 3)時(shí)被觸發(fā).
- 舉例當(dāng)maxActive=20, 活動(dòng)連接為18,空閑連接為1時(shí)可以觸發(fā)"removeAbandoned".但是活動(dòng)連接只有在沒有被使用的時(shí)間超 過"removeAbandonedTimeout"時(shí)才被回收
- logAbandoned: 標(biāo)記當(dāng)連接被回收時(shí)是否打印程序的stack traces日志(默認(rèn)為false,未調(diào)整)
- 代碼未在 finally 釋放 connection , 不過我們都用 sqlmapClientTemplate ,底層都有鏈接釋放的過程
- 遇到數(shù)據(jù)庫死鎖 。以前遇到過后端存儲(chǔ)過程做了鎖表操作,導(dǎo)致前臺(tái)集群中連接池全都被 block 住,后續(xù)的業(yè)務(wù)處理因?yàn)槟貌坏芥溄铀卸继幚硎×恕?
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="xxxx" /> <property name="username"><value>xxxx</value></property> <property name="password"><value>xxxxx</value></property> <property name="maxActive"><value>20</value></property> <property name="initialSize"><value>1</value></property> <property name="maxWait"><value>60000</value></property> <property name="maxIdle"><value>20</value></property> <property name="minIdle"><value>3</value></property> <property name="removeAbandoned"><value>true</value></property> <property name="removeAbandonedTimeout"><value>180</value></property> <property name="connectionProperties"><value>clientEncoding=GBK</value></property> </bean>?
- dbcp是采用了commons-pool做為其連接池管理,testOnBorrow,testOnReturn, testWhileIdle是pool是提供的幾種校驗(yàn)機(jī)制,通過外部鉤子的方式回調(diào)dbcp的相關(guān)數(shù)據(jù)庫鏈接(validationQuery)校驗(yàn)
- dbcp相關(guān)外部鉤子類:PoolableConnectionFactory,繼承于common-pool PoolableObjectFactory
- dbcp通過GenericObjectPool這一入口,進(jìn)行連接池的borrow,return處理
- testOnBorrow : 顧明思義,就是在進(jìn)行borrowObject進(jìn)行處理時(shí),對(duì)拿到的connection進(jìn)行validateObject校驗(yàn)
- testOnReturn : 顧明思義,就是在進(jìn)行returnObject對(duì)返回的connection進(jìn)行validateObject校驗(yàn),個(gè)人覺得對(duì)數(shù)據(jù)庫連接池的管理意義不大
- testWhileIdle : 關(guān)注的重點(diǎn),GenericObjectPool中針對(duì)pool管理,起了一個(gè)Evict的TimerTask定時(shí)線程進(jìn)行控制(可通過設(shè)置參數(shù)timeBetweenEvictionRunsMillis>0),定時(shí)對(duì)線程池中的鏈接進(jìn)行validateObject校驗(yàn),對(duì)無效的鏈接進(jìn)行關(guān)閉后,會(huì)調(diào)用ensureMinIdle,適當(dāng)建立鏈接保證最小的minIdle連接數(shù)。
-
timeBetweenEvictionRunsMillis,設(shè)置的Evict線程的時(shí)間,單位ms,大于0才會(huì)開啟evict檢查線程
- validateQuery, 代表檢查的sql
- validateQueryTimeout, 代表在執(zhí)行檢查時(shí),通過statement設(shè)置,statement.setQueryTimeout(validationQueryTimeout)
- numTestsPerEvictionRun,代表每次檢查鏈接的數(shù)量,建議設(shè)置和maxActive一樣大,這樣每次可以有效檢查所有的鏈接.
<property name="testWhileIdle"><value>true</value></property> <!-- 打開檢查,用異步線程evict進(jìn)行檢查 --> <property name="testOnBorrow"><value>false</value></property> <property name="testOnReturn"><value>false</value></property> <property name="validationQuery"><value>select sysdate from dual</value></property> <property name="validationQueryTimeout"><value>1</value></property> <property name="timeBetweenEvictionRunsMillis"><value>30000</value></property> <property name="numTestsPerEvictionRun"><value>20</value></property>
?相關(guān)配置需求:
?
- 目前網(wǎng)站的應(yīng)用大部分的瓶頸還是在I/O這一塊,大部分的I/O還是在數(shù)據(jù)庫的這一層面上,每一個(gè)請(qǐng)求可能會(huì)調(diào)用10來次SQL查詢,如果不走事務(wù),一個(gè)請(qǐng)求會(huì)重復(fù)獲取鏈接,如果每次獲取鏈接都進(jìn)行validateObject,性能開銷不是很能接受,可以假定一次SQL操作消毫0.5~1ms(一般走了網(wǎng)絡(luò)請(qǐng)求基本就這數(shù))
- 網(wǎng)站異常數(shù)據(jù)庫重啟,網(wǎng)絡(luò)異常斷開的頻率是非常低的,一般也就在數(shù)據(jù)庫升級(jí),演習(xí)維護(hù)時(shí)才會(huì)進(jìn)行,而且一般也是選在晚上,訪問量相對(duì)比較低的請(qǐng)求,而且一般會(huì)有人員值班關(guān)注,所以異步的validateObject是可以接受,但一個(gè)前提需要確保能保證在一個(gè)合理的時(shí)間段內(nèi),數(shù)據(jù)庫能完成自動(dòng)重聯(lián)。
?
public interface PoolableObjectFactory { Object makeObject() throws Exception; void destroyObject(Object obj) throws Exception; boolean validateObject(Object obj); void activateObject(Object obj) throws Exception; void passivateObject(Object obj) throws Exception; }
?
?
2. dbcp實(shí)現(xiàn)的pool從池管理操作
?
這里貼了一個(gè)相關(guān)validate代碼,具體類可見:PoolableConnectionFactory.validateConnection()
?
public class PoolableConnectionFactory implements PoolableObjectFactory { ...... public boolean validateObject(Object obj) { //驗(yàn)證validateObject if(obj instanceof Connection) { try { validateConnection((Connection) obj); return true; } catch(Exception e) { return false; } } else { return false; } } public void validateConnection(Connection conn) throws SQLException { String query = _validationQuery; if(conn.isClosed()) { throw new SQLException("validateConnection: connection closed"); } if(null != query) { Statement stmt = null; ResultSet rset = null; try { stmt = conn.createStatement(); if (_validationQueryTimeout > 0) { stmt.setQueryTimeout(_validationQueryTimeout); } rset = stmt.executeQuery(query); if(!rset.next()) { throw new SQLException("validationQuery didn't return a row"); } } finally { if (rset != null) { try { rset.close(); } catch(Exception t) { // ignored } } if (stmt != null) { try { stmt.close(); } catch(Exception t) { // ignored } } } } } .... }
?
3. pool池的evict調(diào)用代碼:GenericObjectPool (apache commons pool version 1.5.4)
protected synchronized void startEvictor(long delay) { //啟動(dòng)Evictor為TimerTask if(null != _evictor) { EvictionTimer.cancel(_evictor); _evictor = null; } if(delay > 0) { _evictor = new Evictor(); EvictionTimer.schedule(_evictor, delay, delay); } } for (int i=0,m=getNumTests();i<m;i++) { final ObjectTimestampPair pair; ....... boolean removeObject = false; // 空閑鏈接處理 final long idleTimeMilis = System.currentTimeMillis() - pair.tstamp; if ((getMinEvictableIdleTimeMillis() > 0) && (idleTimeMilis > getMinEvictableIdleTimeMillis())) { removeObject = true; } else if ((getSoftMinEvictableIdleTimeMillis() > 0) && (idleTimeMilis > getSoftMinEvictableIdleTimeMillis()) && ((getNumIdle() + 1)> getMinIdle())) { removeObject = true; } // testWhileIdle sql 檢查處理 if(getTestWhileIdle() && !removeObject) { boolean active = false; try { _factory.activateObject(pair.value); active = true; } catch(Exception e) { removeObject=true; } if(active) { if(!_factory.validateObject(pair.value)) { removeObject=true; } else { try { _factory.passivateObject(pair.value); } catch(Exception e) { removeObject=true; } } } } // 真正關(guān)閉 if (removeObject) { try { _factory.destroyObject(pair.value); } catch(Exception e) { // ignored } } ........?
注意: 目前dbcp的pool的實(shí)現(xiàn)是使用了公用的apache common pools進(jìn)行擴(kuò)展處理,所以和原生的連接池處理,代碼看上去有點(diǎn)別扭,感覺自動(dòng)重連這塊異常處理不怎么好,我也就只重點(diǎn)關(guān)注了這部分代碼而已?
?.
?
3. dbcp的鏈接自動(dòng)重鏈相關(guān)測(cè)試
相關(guān)場(chǎng)景:
- 數(shù)據(jù)庫意外重啟后,原先的數(shù)據(jù)庫連接池能自動(dòng)廢棄老的無用的鏈接,建立新的數(shù)據(jù)庫鏈接
- 網(wǎng)絡(luò)異常中斷后,原先的建立的tcp鏈接,應(yīng)該能進(jìn)行自動(dòng)切換
測(cè)試需求1步驟
- 建立一testCase代碼
- 配置mysql數(shù)據(jù)庫
- 循環(huán)執(zhí)行在SQL查詢過程
- 異常重啟mysql數(shù)據(jù)庫
測(cè)試需求2步驟
- 建立一testCase代碼
- 配置mysql數(shù)據(jù)庫
- 循環(huán)執(zhí)行在SQL查詢過程
-
通過iptables禁用網(wǎng)絡(luò)鏈接
/sbin/iptables -A INPUT -s 10.16.2.69 -j REJECT
/sbin/iptables -A FORWARD -p tcp -s 10.16.2.69 --dport 3306 -m state --state NEW,ESTABLISHED -j DROP
???? 5. iptables -F 清空規(guī)則,恢復(fù)鏈接通道。
?
測(cè)試需求問題記錄
?
分別測(cè)試了兩種配置,有validateObject的配置和沒有validateObject的相關(guān)配置。
1. 沒有validate配置
問題一: 異常重啟mysql數(shù)據(jù)庫后,居然也可以自動(dòng)恢復(fù)鏈接,sql查詢正常
跟蹤了一下代碼,發(fā)現(xiàn)這么一個(gè)問題:
- 在數(shù)據(jù)庫關(guān)閉的時(shí)候,client中pool通過borrowObject獲取一個(gè)異常鏈接返回給client
- client在使用具體的異常鏈接進(jìn)行sql調(diào)用出錯(cuò)了,拋了異常
- 在finally,調(diào)用connection.close(),本意是應(yīng)該調(diào)用pool通過returnObject返回到的池中,但在跟蹤代碼時(shí),未見調(diào)用GenericObjectPool的returnObject
- 繼續(xù)查,發(fā)現(xiàn)在dbcp在中PoolingDataSource(實(shí)現(xiàn)DataSource接口)調(diào)用PoolableConnection(dbcp pool相關(guān)的delegate操作)進(jìn)行相應(yīng)關(guān)閉時(shí),會(huì)檢查_conn.isClosed(),針對(duì)DataSource如果isClosed返回為true的則不調(diào)用returnObject,直接丟棄了鏈接??
解釋:
- 正因?yàn)樵讷@取異常鏈接后,因?yàn)樽隽薩conn.isClosed()判斷,所以異常鏈接并沒有返回到連接池中,所以到數(shù)據(jù)庫重啟恢復(fù)后,每次都是調(diào)用pool重新構(gòu)造一個(gè)新的connection,所以后面就正常了
- _conn.isClosed()是否保險(xiǎn),從jdk的api描述中: A connection is closed if the method close has been called on it or if certain fatal errors have occurred. 里面提供兩種情況,一種就是被調(diào)用了closed方法,另一種就是出現(xiàn)一些異常也說的比較含糊。
問題二:validateObject調(diào)用時(shí),dbcp設(shè)置的validationQueryTimeout居然沒效果
看了mysql statement代碼實(shí)現(xiàn),找到了答案。?
mysql com.mysql.jdbc.statemen 部分代碼
?
timeout時(shí)間處理:
timeoutTask = new CancelTask(); //通過TimerTask啟動(dòng)一定時(shí)任務(wù) Connection.getCancelTimer().schedule(timeoutTask, this.timeoutInMillis);
?
對(duì)應(yīng)的CancelTask的代碼:?
?
class CancelTask extends TimerTask { long connectionId = 0; CancelTask() throws SQLException { connectionId = connection.getIO().getThreadId(); } public void run() { Thread cancelThread = new Thread() { public void run() { Connection cancelConn = null; java.sql.Statement cancelStmt = null; try { cancelConn = connection.duplicate(); cancelStmt = cancelConn.createStatement(); // 簡(jiǎn)單暴力,再發(fā)起一條KILL SQL,關(guān)閉先前的sql thread id cancelStmt.execute("KILL QUERY " + connectionId); wasCancelled = true; } catch (SQLException sqlEx) { throw new RuntimeException(sqlEx.toString()); } finally { if (cancelStmt != null) { try { cancelStmt.close(); } catch (SQLException sqlEx) { throw new RuntimeException(sqlEx.toString()); } } if (cancelConn != null) { try { cancelConn.close(); } catch (SQLException sqlEx) { throw new RuntimeException(sqlEx.toString()); } } } } }; cancelThread.start(); } }
?
?
原因總結(jié)一句話: queryTimeout的實(shí)現(xiàn)是通過底層數(shù)據(jù)庫提供的機(jī)制,比如KILL QUERY pid. ?如果此時(shí)的網(wǎng)絡(luò)不通,出現(xiàn)阻塞現(xiàn)象,對(duì)應(yīng)的kill命令也發(fā)不出去,所以timeout設(shè)置的超時(shí)沒效果。
4.最后
最后還是決定配置testWhileIdle掃描,主要考慮:
- pool池中的鏈接如果未被使用,可以通過testWhileIdle進(jìn)行鏈接檢查,避免在使用時(shí)后總要失敗那么一次,可以及時(shí)預(yù)防
- 配合連接池的minEvictableIdleTimeMillis(空閑鏈接),removeAbandoned(未釋放的鏈接),可以更好的去避免因?yàn)橐恍┊惓G闆r引起的問題,防范于未然。比如使用一些分布式數(shù)據(jù)庫的中間件,會(huì)有空閑鏈接關(guān)閉的動(dòng)作,動(dòng)態(tài)伸縮連接池,這時(shí)候需要能及時(shí)的發(fā)現(xiàn),避免請(qǐng)求失敗。
- testOnBorrow個(gè)人不太建議使用,存在性能問題,試想一下連接一般會(huì)在什么情況出問題,網(wǎng)絡(luò)或者服務(wù)端異常終端空閑鏈接,網(wǎng)絡(luò)中斷你testOnBorrow檢查發(fā)現(xiàn)不對(duì)再取一個(gè)鏈接還是不對(duì),針對(duì)空閑鏈接處理異常關(guān)閉,可以從好業(yè)務(wù)端的重試策略進(jìn)行考慮,同時(shí)配置客戶端的空閑鏈接超時(shí)時(shí)間,maxIdle,minIdle等。
?
--------------------------------------------
新加的內(nèi)容:
5.dbcp密碼加密處理
以前使用jboss的jndi數(shù)據(jù)源的方式,是通過配置oracle-ds.xml,可以設(shè)置<security-domain>EncryptDBPassword</security-domain>,引用jboss login-config.xml配置的加密配置。
?
?
<application-policy name="EncryptDBPassword"> <authentication> <login-module code="org.jboss.resource.security.SecureIdentityLoginModule" flag="required"> <module-option name="username">${username}</module-option> <module-option name="password">${password_encrypt}</module-option> <module-option name="managedConnectionFactoryName">jboss.jca:service=LocalTxCM,name=${jndiName}</module-option> </login-module> </authentication> </application-policy>?
?
為了能達(dá)到同樣的效果,切換為spring dbcp配置時(shí),也有類似密碼加密的功能,運(yùn)行期進(jìn)行密碼decode,最后進(jìn)行數(shù)據(jù)鏈接。
?
?
實(shí)現(xiàn)方式很簡(jiǎn)單,分析jboss的對(duì)應(yīng) SecureIdentityLoginModule 的實(shí)現(xiàn),無非就是走了Blowfish加密算法,自己拷貝實(shí)現(xiàn)一份。
?
?
private static String encode(String secret) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { byte[] kbytes = "jaas is the way".getBytes(); SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish"); Cipher cipher = Cipher.getInstance("Blowfish"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encoding = cipher.doFinal(secret.getBytes()); BigInteger n = new BigInteger(encoding); return n.toString(16); } private static char[] decode(String secret) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { byte[] kbytes = "jaas is the way".getBytes(); SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish"); BigInteger n = new BigInteger(secret, 16); byte[] encoding = n.toByteArray(); Cipher cipher = Cipher.getInstance("Blowfish"); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decode = cipher.doFinal(encoding); return new String(decode).toCharArray(); }?
最后的配置替換為:
?
?
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> ...... <property name="password"><!-- 注意多了一層轉(zhuǎn)化,將密碼串調(diào)用decode解密為最初的數(shù)據(jù)庫密碼 --> <bean class="com.xxxxx.EncryptDBPasswordFactory"> <property name="password" value="${xxxx.password.encrypted}" /> </bean> </property> ........ </bean>
?
--------------------------------------------
新加的內(nèi)容:
6.數(shù)據(jù)庫重連機(jī)制
常見的問題:
1. 數(shù)據(jù)庫意外重啟后,原先的數(shù)據(jù)庫連接池能自動(dòng)廢棄老的無用的鏈接,建立新的數(shù)據(jù)庫鏈接
2. 網(wǎng)絡(luò)異常中斷后,原先的建立的tcp 鏈接,應(yīng)該能進(jìn)行自動(dòng)切換。比如網(wǎng)站演習(xí)中的交換機(jī)重啟會(huì)導(dǎo)致網(wǎng)絡(luò)瞬斷
3. 分布式數(shù)據(jù)庫中間件,比如amoeba 會(huì)定時(shí)的將空閑鏈接異常關(guān)閉,客戶端會(huì)出現(xiàn)半開的空閑鏈接。
?
大致的解決思路:?
1. sql 心跳檢查
? 主動(dòng)式 ,即我前面提到的sql validate相關(guān)配置
2. 請(qǐng)求探雷
??? 犧牲小我,完成大我的精神。 拿鏈接嘗試一下,發(fā)現(xiàn)處理失敗丟棄鏈接,探雷的請(qǐng)求總會(huì)失敗幾個(gè),就是前面遇到的問題一,dbcp已經(jīng)支持該功能,不需要額外置。
3. 設(shè)置合理的超時(shí)時(shí)間,
????? 解決半開鏈接. 一般數(shù)據(jù)庫mysql,oracle都有一定的鏈接空閑斷開的機(jī)制,而且當(dāng)你使用一些分布式中間件(軟件一類的),空閑鏈接控制會(huì)更加嚴(yán)格,這時(shí)候設(shè)置合理的超時(shí)時(shí)間可以有效 避免半開鏈接。
???? 一般超時(shí)時(shí)間,dbcp主要是minEvictableIdleTimeMillis(空閑鏈接) , removeAbandonedTimeout(鏈接泄漏)。可以見前面的參數(shù)解釋。
?
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
