我有一個問題,當我通過tomcat Web應用程序的用戶請求重置我的MySQL數據庫後,我收到了tomcat異常。我試圖將此分解爲設置,問題和我的分析,以幫助任何嘗試閱讀此內容的人。如何重置JDBC連接池
設置
復位基本上由從Java代碼中調用一個bash腳本的:
- 刪除MySQL的root用戶的密碼在舊版本的數據庫
- 加載
- 在其上運行一些腳本
- 恢復所有密碼
這是用戶啓動的過程,通常會將數據庫恢復到以前的狀態,但它也可用於從另一個系統導入數據庫。一旦所有事情都完成了,用戶就會嘗試訪問Web應用程序的不同部分(即使用同一個會話,而不註銷/重新登錄),它會執行DB查詢以獲取某些數據。
問題
一旦數據庫被Tomcat應用程序查詢,有一個例外:
Dec 29, 2014 3:49:50 PM ERROR BasicSecurityRealm:216 -
ERROR: ----- SQLException -----
Dec 29, 2014 3:49:50 PM INFO BasicSecurityRealm:218 - Exceptioncom.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 234,810 milliseconds ago. The last packet sent successfully to the server was 12 milliseconds ago.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
...
Caused by: 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:2540)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2990)
即使用戶在註銷和背部,我會看到這個異常。如果我刷新頁面四次,頁面會每次加載一些不同的異常(上述所有變體 - 由於「EOFException:無法讀取服務器響應」導致的CommunicationsException)。最後一次,一切似乎都正常運行。
爲避免這些異常,我唯一能做的就是重新啓動tomcat。我想避免這種情況,因爲這意味着登錄的當前用戶將會丟失會話,並且必須等待tomcat重新啓動才能重新登錄。強制他們註銷/返回可能是一個可以接受的折中方案,但這並不能解決問題。
分析
從我可以告訴,我認爲這個問題與JDBC連接池做。我使用JNDI數據源如下訪問我的數據庫:
的server.xml:
<GlobalNamingResources>
<Resource name="jdbc/mydb"
auth="Container"
type="javax.sql.DataSource"
maxActive="30" maxIdle="30" maxWait="2147483647"
username="x" password="x"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb?autoReconnect=true"/>
的web.xml:
<!-- Data source definitions -->
<resource-ref>
<res-ref-name>jdbc/mydb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
的Java:
// Get connection to specified database
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource) envCtx.lookup("jdbc/mydb");
con = ds.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery("...");
我相信連接池包含陳舊/死亡的連接。每當我與ds.getConnection
建立連接時,就會得到這些舊連接中的一個。嘗試使用它將在第一次失敗,連接重置(請注意,我正在使用autoReconnect=true
,因此第二次應該(並且確實)有效)。然而,池包含很多(在我的情況下,經驗上4或5)陳舊的連接,所以它們都需要一段時間才能正確重置。一旦連接重置,所有事情都會正常進行。
解決方案?
因爲我使用autoReconnect=true
我可以重新構造我的代碼,這樣如果在嘗試查詢時遇到異常,我可以重試該查詢一次。如果它再次失敗,那麼我會知道真的有問題。如果通過,則連接成功重新建立。
問題在於代碼中存在查詢無處不在。對它們進行重新分解需要花費很多時間和測試,如果有必要,我會這樣做,但是希望避免。另外,如果查詢由於其他原因而失敗,則在報告之前將嘗試兩次。對於長時間查詢,這可能會導致嚴重的用戶體驗延遲,但只能在錯誤情況下進行。
另一種解決方案是強制重置/重新連接連接池中的所有連接。我可以以編程方式(即在調用bash腳本完成時從我的java代碼)或bash腳本(例如使用某種類型的命令行實用程序)執行此操作。問題是,我不知道該怎麼做,或者甚至有可能。
我發現了一些有關攔截器的文檔,但我不確定這是否適用於重置連接。我會繼續調查。
感謝大家的時間和幫助!
解決方案:不要刪除您在數據庫連接池中配置的用戶的憑據。實際上,您不應該篡改mysql用戶數據來改變數據庫的狀態。您應該只更改您正在使用的架構。 –
@LuiggiMendoza謝謝。我曾想到這一點,但長話短說,這可能不是一種選擇。運行的腳本沒有修改數據庫的憑據,所以我需要刪除root用戶的密碼。另外,加載的新數據庫可能有不同的密碼。 – Trenin
*我想到了這一點,但長話短說,這可能不是一個選項*然後在沉默u_u受苦。笑話,你不能*重置*數據庫連接池。避免這樣做,或者讓有足夠能力的用戶來執行腳本。您可以創建另一個用戶來執行此操作,或者關閉您的應用程序並重新啓動它們併爲此創建策略。 –