spring+activemq中多個consumer同時處理消息時遇到的性能問題
最近在做數據對接的工作,用到了activemq,我需要從activemq中接收消息並處理,但是我處理數據的步驟稍微復雜,漸漸的消息隊列中堆的數據越來越多,就想到了我這邊多開幾個線程來處理消息。
可是會發現,服務器占用的網絡帶寬變的異常的高,仔細分析發現,mq入隊時並沒有異常高的網絡流量,僅僅在出隊時會產生很高的網絡流量。最終發現是spring的jmsTemplate與activemq的prefetch機制配合導致的問題。研究源碼發現jmsTemplate實現機制是:每次調用receive()時都會創建一個新的consumer對象,用完即銷毀。正常情況下僅僅會浪費重復創建consumer的資源代價,並不至於產生正常情況十倍百倍的網絡流量。但是activeMQ有一個提高性能的機制prefetch,此時就會有嚴重的問題。
ActiveMq的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,此時可以充分利用prefetch機制。配合多線程的方式每個線程擁有自己的一個consumer,此時能夠充分發揮MQ在大吞吐量時的速度優勢。
ps:如何看到activeMQ的prefetch機制?
prefetchsize值默認是1000,那麽只要隊列裏的消息小於1000,消費端無論開幾個線程,我們會發現真正處理消息的線程永遠只有一個,其它的線程其實都在做無用功,幫不上忙。
spring+activemq中多個consumer同時處理消息時遇到的性能問題