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 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 试图在已关闭的数据库链接上继续执行操做。
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 异常,手动从新链接便可:
- } catch (SQLException sqlEx) {
- String sqlState = sqlEx.getSQLState();
-
- if ("08S01".equals(sqlState) || "40001".equals(sqlState))
为何 Connector/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