MQ異常斷開

ActiveMQ:No operations allowed after statement closed問題及解決辦法

 
ActiveMQ版本:5.5.1

現象:html

系統現象:部分消息發送失敗,失敗頻率不正常。
日誌信息:activemq.log 中一直有這樣的錯誤日誌:

JDBC Failure: No operations allowed after statement closed.java

com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after statement closed.mysql

        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)sql

        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)數據庫

…………apache

        at org.apache.activemq.transport.InactivityMonitor.onCommand(InactivityMonitor.java:227)安全

        at org.apache.activemq.transport.TransportSupport.doConsume(TransportSupport.java:83)服務器

        at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:220)tcp

        at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:202)ide

        at java.lang.Thread.run(Thread.java:662)

Close failed: Already closed.

看上去又是 mq broker 失去了數據庫鏈接,但代碼仍嘗試在此鏈接上執行操做,因此 jdbc 直接拋異常。


緣由:
ActiveMQ 持久化方案咱們選的是 MySQL 。
若是 mq 與 db 之間的數據庫鏈接,由於 數小時的不活動(inactivity),那麼 MySQL 就會根據自身的 wait_timeout 參數設置 主動斷開鏈接。這一點在以前的「 ActiveMQ:Communications link failure問題以及解決辦法」文章中講過。
只不過,遇到此問題時,
mq client 端報告「com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure」錯誤,
而 mq server 端則報告「 com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after statement closed. 」錯誤。
即緣由是, mq broker service 試圖在已關閉的數據庫鏈接上繼續執行操做
缺陷單  AMQ-2534 甚至報告「

Broker gets stuck with an error about using a closed JDBC statement

」,咱們很難說此時 mq broker service 會不會真的「卡住」。
 
解決辦法:
跟「ActiveMQ:Communications link failure問題以及解決辦法」文章講的同樣,
最簡單辦法,仍是根據業務特色,調整 ActiveMQ 所使用的 MySQL 的全局變量 wait_timeout 值,
儘可能減小數據庫鏈接由於 inactivity 而被關閉的概率 。
 
或者在 mq client 裏主動捕獲 com.mysql.jdbc.exceptions.jdbc4.CommunicationsException 異常,手動從新鏈接便可:
  1.         } catch (SQLException sqlEx) {  
  2.             String sqlState = sqlEx.getSQLState();  
  3.            // 08S01就是這個異常的sql狀態。單獨處理手動從新鏈接便可。  
  4.             if ("08S01".equals(sqlState) || "40001".equals(sqlState))   
 

爲何 Connector/J 不自動重連而非要拋出異常:
下面的文字翻譯自 MySQL 5.5  Connect/J 疑難問題文檔
23.3.15.13: 爲何遇到  communication failure 以後,  Connector/J 不能本身從新連上數據庫,而後從新提交事務呢,而是非要拋出一個異常,即便我用了 autoReconnect 鏈接字符串屬性?
答:有幾個緣由。
第一個,保證事務完整性。MySQL 的幫助文檔上曾說過:「there is no safe method of reconnecting to the MySQL server without risking some corruption of the connection state or database state information」。
能夠看一下這個案例:
01.conn.createStatement().execute(
  "UPDATE checking_account SET balance = balance - 1000.00 WHERE customer='Smith'");
02.conn.createStatement().execute(
  "UPDATE savings_account SET balance = balance + 1000.00 WHERE customer='Smith'");
03.conn.commit();
考慮一下這個場景:執行完01後,數據庫鏈接斷了。
若是沒有異常拋出的話,應用程序永遠不知道這個問題,仍繼續執行。
可是第一個事務並無提交,因此它回滾了。
若是沒拋異常,數據庫鏈接自動重連,因而第二個事務被提交了。
從而破壞了事務完整性(transactional integrity)。
 
記住,此時 auto-commit 於事無補。當 Connector/J 遇到 communication problem ,你不知道服務器端是否處理了這個事務,有幾種可能:
  • 服務器端沒有接到這個事務,所以什麼也沒發生;
  • 服務器端接到了且執行了,但客戶端沒有收到 Response 。
 
第二個緣由是,事務裏上下文相關數據有可能很是脆弱,如:
  • 臨時表;
  • 用戶自定義變量;
  • 服務器端預處理語句(Server-side prepared statements);
若是數據庫鏈接斷開了,極可能這些數據也消失了;若是此時不拋出異常而是自動重連,你的應用程序極可能跑飛了。
 
總結:
1) silently reconnecting 」可能很是不安全,將衍生出不少不可控問題。因此最佳策略是,通知應用程序到底發生了什麼,而後由應用開發者決定如何處理
2) mq的生產者頻繁報「com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure」和mq server本身頻繁報「com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after statement closed. 」,都是由於 mysql 的 wait_timeout 致使數據庫鏈接由於不活動而被主動關閉。
 
本文引自:http://www.cnblogs.com/zhengyun_ustc/archive/2012/11/10/activemq_inactivity.html
相關文章
相關標籤/搜索