http://www.linuxdiyf.com/viewarticle.php?id=89413
最近在一個J2EE項目的開發過程中,遇到了這樣的問題:
在伺服器上部署好這個Web系統後,這時訪問系統是很正常的。當把伺服器的時間(例如:2008-03-31)加一天或更多天(例如:2008-04-01,2008-04-02...),這時再訪問這個Web系統,報出如下的異常:
| com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying exception: ** BEGIN NESTED EXCEPTION ** java.io.EOFException MESSAGE: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost. STACKTRACE: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost. at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1997) at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2411) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2916) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1631) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1723) at com.mysql.jdbc.Connection.execSQL(Connection.java:3250) at com.mysql.jdbc.Connection.setAutoCommit(Connection.java:5395) ... |
我們首先判斷是串連池出了問題,即當系統時間改變後,資料庫連接發現逾時,因此需要重新串連或完全重建串連池。我們換用了一下資料庫,如果使用Oracle資料庫,同樣的修改不會出現問題,系統運行正常,根據不會出現這樣的異常。這就說明了出現這個異常是Mysql相關的,通過查閱資料得知可能和Mysql的運行參數(逾時)有關,順著這個思路我們來分析Mysql的一些運行參數。
在MySQL Command Line Client中執行show variables like '%timeout%';如所示:
在圖中我們可以看到有兩個變數wait_timeout和interactive-timeout,它們的預設值都為28800秒,即為8小時。也就是說預設情況下,Mysql在經過8小時(28800秒)不使用後會自動關閉已開啟的串連。
為瞭解決這個問題,對於MySQL5之前的版本,如Mysql4.x,只需要修改串連池配置中的URL,添加一個參數:autoReconnect=true,如果是MySQL5及以後的版本,則需要修改my.cnf(或者my.ini)檔案,在[mysqld]後面添加上:
wait_timeout = n
interactive-timeout = n
n為伺服器關閉互動式串連前等待活動的秒數。
可是就部署而言每次修改my.ini比較麻煩,而且n等於多少才是合適的呢?所以這個解決辦法不好。
怎麼才能比較好的解決這個問題呢?通過仔細分析,原因大致為:當修改系統日期後Mysql自動關閉已開啟的串連,可以資料庫連接池(DBCP)還認為這些串連是活動的,如果這時有請求(需要執行讀寫資料庫的操作),串連池就用一個串連去操作資料庫,而這個串連在Mysql的串連池中並不存在,所以會出現以上的異常。如果一個串連在和Mysql建立串連時能檢查就不會有這樣的問題了。
這時我們同事想起了他以前用過的一個資料庫連接池proxool,它有兩個屬性:一個是test-before-use,還有一個是test-after-use,這兩個屬性就是在使用前和使用後都要進行對串連的檢查,如果串連無效就扔掉再建立一個新的串連,它們的解釋如下:
test-before-use:
If you set this to true then each connection is tested (with whatever is defined in house-keeping-test-sql) before being served. If a connection fails then it is discarded and another one is picked. If all connections fail a new one is built. If that one fails then you get an SQLException saying so.
test-after-use:
If you set this to true then each connection is tested (with whatever is defined in house-keeping-test-sql) after it is closed (that is, returned to the connection pool). If a connection fails then it is discarded.
於是我們在項目中換上proxool的串連池,再運行並訪問系統,更改系統時間,再訪問,一切OK,問題就這樣解決了!
不但如此,proxool的串連池中的串連的數量,活動數以及每個串連的的具體情況都可以一目瞭然的看到,如所示:
通過上面的測試和分析,針對於Mysql5資料庫,選擇proxool資料庫連接池是比較合適的。
c3p0的串連池有 idleConnectionTestPeriod 屬性,可以設定一段時間後串連池自動化的測試執行,保持串連開放,效果也是一樣的。