activeMQ消费消息时网络流量异常大的问题

     公司有一个应用,多个线程从activeMQ中取消息,随着业务的扩大,该机器占用的网络带宽越来越高。

仔细分析发现,mq入队时并没有异常高的网络流量,仅仅在出队时会产生很高的网络流量。

最终发现是spring的jmsTemplate与activemq的prefetch机制配合导致的问题。

研究源码发现jmsTemplate实现机制是:每次调用receive()时都会创建一个新的consumer对象,用完即销毁。

正常情况下仅仅会浪费重复创建consumer的资源代价,并不至于产生正常情况十倍百倍的网络流量。

但是activeMQ有一个提高性能的机制prefetch,此时就会有严重的问题。

prefetch机制:
每次consumer连接至MQ时,MQ预先存放许多message到消费者(前提是MQ中存在大量消息),预先存放message的数量取决于prefetchSize(默认为1000)。此机制的目的很显然,是想让客户端代码用一个consumer反复进行receive操作,这样能够大量提高出队性能。

此机制与jmsTemplate配合时就会产生严重的问题,每次jmsTemplate.receive(),都会产生1000个消息的网络流量,但是因为jmsTemplae并不会重用consumer,导致后面999个消息都被废弃。反复jmsTemplate.receive()时,表面上看不出任何问题,其实网络带宽会造成大量的浪费。


解决方案:

1、若坚持使用jmsTemplate,需要设置prefetch值为1,相当于禁用了activeMQ的prefetch机制,此时感觉最健壮,就算多线程,反复调用jmsTemplate.receive()也不会有任何问题。但是会有资源浪费,因为要反复创建consumer并频繁与服务器进行数据通信,但在性能要求不高的应用中也不算什么问题。

2、不使用jmsTemplate,手工创建一个consumer,并单线程反复使用它来receive(),此时可以充分利用prefetch机制。配合多线程的方式每个线程拥有自己的一个consumer,此时能够充分发挥MQ在大吞吐量时的速度优势。

切记避免多线程使用一个consumer造成的消息混乱。大吞吐量的应用推荐使用方案2,能够充分利用prefetch机制提高系MQ的吞吐性能。

参考文档:

介绍了acrivemq的prefetch机制,每次消费者读取消息时,mq会预先塞给消费者prefetchSize个消息
而jmsTemplate取消息的实现方式为:创建consumer -> 取一个消息 -> 销毁consumer,并没有为consumer做池。
缺省设置时, prefetchSize为1000,prefetch机制取得的后999个消息都白取了。所以必须设置prefetch值为1。
 
activemq的spring支持,文章后半段Working with Spring's JmsTemplate 部分介绍了jmsTemplate
jms连接池只对connection、session、producer组池,而不对consumer做池。
因为prefetch机制的存在,如果对consumer做池,会造成池内的consumer预读取的消息很可能无法被及时处理。
posted @ 2012-12-24 14:23  baibaluo  阅读(5269)  评论(2编辑  收藏  举报