ActiveMQ:No operations allowed after statement closed问题及解决办法

标签: activemq no operations | 发表时间:2012-11-10 00:30 | 作者:旁观者
出处:http://www.cnblogs.com/zhengyun_ustc/
ActiveMQ版本:5.5.1
记录人:@郑昀

现象:

系统现象:部分消息发送失败,失败频率不正常。
日志信息:activemq.log 中一直有这样的错误日志:

JDBC Failure: No operations allowed after statement closed.

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

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

        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)

…………

        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)

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

        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 导致数据库连接因为不活动而被主动关闭。

 


参考资料:
 

本文链接

相关 [activemq no operations] 推荐:

【ActiveMQ Tuning】Prefetch Limit

- - 博客园_首页
   摘要:ActiveMQ优化 客户端优化 预取限制. 原文: http://fusesource.com/docs/broker/5.4/tuning/GenTuning-Consumer-Prefetch.html. Overview:图列4.1阐明了Broker在等待之前发送给客户端消息的反馈的行为.

【ActiveMQ Tuning】Serializing to Disk

- - 博客园_首页
     翻译自: http://fusesource.com/docs/broker/5.4/tuning/PersTuning-SerialToDisk.html.      KahaDB message store:KahaDB 是ActiveMQ Broker 为了高性能而推荐使用的消息存储机制.

ActiveMQ 桥接

- - CSDN博客互联网推荐文章
使用目的:将本地产生的消息转发到远程,通过远程服务器来处理消息,处理完成后,再启动消费者处理本地服务器消息(验证消息是否被转走,本地无消息可处理为正常). 消息在下面的地址被消费,无需任何特别配置,采用默认的配置即可. 生产消息地址为localhost:7001,需要做如下配置. 注意: 表示只有这个队列的会进行桥接转发.

ActiveMQ学习小结

- - CSDN博客架构设计推荐文章
   Activemq是众多开源消息中间件的一种,支持集群,同等网络,自动检测,TCP,SSL,广播,持久化,和J2EE1.4容器无缝结合. 它是apache基金会的一个项目,而且经过多年发展,有了很高的稳定性. 目前被很多知名项目使用,比如Apache serviceMix、FuseESB.  消息中间件一般被用在异步消息通信、整合多个系统的场景,比如你注册CSDN论坛,你填写完注册信息点提交时,它会发一份验证邮箱的验证邮件给到你,这封邮件就可以通过消息中间异步发送给你.

ActiveMQ与Spring整合

- - 博客园_首页
ActiveMQ 是Apache出品, 是最流行​​和最强大的开源消息总线. 同时完全支持 JMS 1.1和J2EE 1.4规范. 支持多种编程语言和协议编写客户端. 在JMS客户端和消息代理完全支持企业集成模式. 完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务). 对Spring的支持, ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring2.0的特性.

ActiveMQ高级特性

- - zzm
消息生产者使用持久(persistent)传递模式发送消息的时候,Producer.send() 方法会被阻塞,直到 broker 发送一个确认消息给生产者,这个确认消息暗示生产者 broker 已经成功地将它发送的消息路由到目标目的并把消息保存到二级存储中. 但有一个例外,当发送方法在一个事物上下文中时,被阻塞的是commit 方法而不是 send 方法.

ActiveMQ:No operations allowed after statement closed问题及解决办法

- - 博客园_旁观者
ActiveMQ版本:5.5.1. 系统现象:部分消息发送失败,失败频率不正常. 日志信息:activemq.log 中一直有这样的错误日志:. 看上去又是 mq broker 失去了数据库连接,但代码仍尝试在此连接上执行操作,所以 jdbc 直接抛异常. 原因: ActiveMQ 持久化方案我们选的是 MySQL.

ActiveMQ持久化方式

- - CSDN博客架构设计推荐文章
消息持久性对于可靠消息传递来说应该是一种比较好的方法,有了消息持久化,即使发送者和接受者不是同时在线或者消息中心在发送者发送消息后宕机了,在消息中心重新启动后仍然可以将消息发送出去,如果把这种持久化和ReliableMessaging结合起来应该是很好的保证了消息的可靠传送. 消息持久性的原理很简单,就是在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等,然后试图将消息发送给接收者,发送成功则将消息从存储中删除,失败则继续尝试.

[MQ]关于ActiveMQ的配置

- - 企业架构 - ITeye博客
  目前常用的消息队列组建无非就是MSMQ和ActiveMQ,至于他们的异同,这里不想做过多的比较. 简单来说,MSMQ内置于微软操作系统之中,在部署上包含一个隐性条件:Server需要是微软操作系统. (对于这点我并去调研过MSMQ是否可以部署在非微软系统,比如:Linux,只是拍脑袋想了想,感觉上是不可以).

优化ActiveMQ性能(zhuan)

- - zzm
1.  优化ActiveMQ性能. 1.PERSISTENT(持久性消息). 这是 ActiveMQ 的默认传送模式,此模式保证这些消息只被传送一次和成功使用一次. 对于这些消息,可靠性是优先考虑的因素. 可靠性的另一个重要方面是确保持久性消息传送至目标后,消息服务在向消费者传送它们之前不会丢失这些消息.