http://www.cnblogs.com/baibaluo/archive/2012/12/24/2748468.html#2590289
公司有一个应用,多个线程从activeMQ中取消息,随着业务的扩大,该机器占用的网络带宽越来越高。 仔细分析发现,mq入队时并没有异常高的网络流量,仅仅在出队时会产生很高的网络流量。 最终发现是spring的jmsTemplate与activemq的prefetch机制配合导致的问题。 研究源码发现jmsTemplate实现机制是:每次调用receive()时都会创建一个新的consumer对象,用完即销毁。 正常情况下仅仅会浪费重复创建consumer的资源代价,并不至于产生正常情况十倍百倍的网络流量。 但是activeMQ有一个提高性能的机制prefetch,此时就会有严重的问题。 prefetch机制: 此机制与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预读取的消息很可能无法被及时处理。
|
|