在我之前發(fā)的兩篇問題整理中,梳理了一下ActiveMQ集群轉(zhuǎn)發(fā)問題的分析過程:
ActiveMQ集群消息轉(zhuǎn)發(fā)問題整理(一)
ActiveMQ集群消息轉(zhuǎn)發(fā)問題整理(二)
這里簡要介紹一下問題的發(fā)現(xiàn)和分析過程:
問題發(fā)現(xiàn)
環(huán)境中 8000+ 個 Client 連接8臺MQ甸昏,使用 non-durable 的方式訂閱各自C類網(wǎng)段的TOPIC顽分,例如 192.168.0.1 訂閱 TOPIC:VLAN.192.168.0 。環(huán)境中大約有160+個C類網(wǎng)段施蜜,所以大約有160個TOPIC卒蘸。
下發(fā)針對全量 Client 的任務的時候,常常發(fā)現(xiàn)有個別 Client 收不到任務。由于我之前做了個任務流插件缸沃,可以跟蹤到任務是否到達MQ恰起,見(ActiveMQ插件開發(fā)實例-任務日志)表現(xiàn)為任務到達了生產(chǎn)者所在的MQ,但是沒有被轉(zhuǎn)發(fā)到Client所在的MQ趾牧。
問題分析
一检盼、進一步現(xiàn)象
根據(jù)現(xiàn)象進一步測試發(fā)現(xiàn)如下的現(xiàn)象:
- 假設 Client_A連接MQ_A,消費TOPIC_A翘单。消息下發(fā)時吨枉,出現(xiàn)Client_A接收不到消息的情況。
- Client_A連接的MQ_A上只有這個Client_A一個消費者消費TOPIC_A上的消息哄芜。
- 查看TOPIC的訂閱者信息貌亭,除了Client_A外,還可能出現(xiàn)該MQ轉(zhuǎn)發(fā)到其他MQ上的虛擬消費者认臊,表示發(fā)到這個TOPIC的消息需要被轉(zhuǎn)發(fā)給其他MQ
- 集群內(nèi)其他所有MQ上的TOPIC_A上查看訂閱者圃庭,均未出現(xiàn)類似的虛擬消費者,表示消息不會被轉(zhuǎn)發(fā)到MQ_A美尸,所以Client_A無法收到消息冤议。
- 重啟Client,Client會自動飄到集群內(nèi)其他的MQ上师坎,此時可以正常消費恕酸。
- 指定MQ_A要求Client_A重啟后連接到MQ_A,也可以正常消費胯陋。
這些現(xiàn)象表明這是一個偶發(fā)的問題蕊温,所以可能需要進一步深入到源碼級別查找一下問題原因。
二遏乔、源碼分析
首先要明確一下MQ集群中的轉(zhuǎn)發(fā)機制是怎樣的义矛。由于是 non-durable 的topic,使用的應該是 DemandForwardBridge 的方式盟萨。該方式的原理根據(jù)AMQ官網(wǎng)上的介紹凉翻,是通過集群中的 MQ 訂閱集群中其他 MQ 的 advisory topic 實現(xiàn)的。舉個例子:
- Broker A和Broker B為集群中兩臺MQ捻激。
- A,B啟動時制轰,便通過靜態(tài)配置對方IP的方式得知集群中有另一個MQ存在,所以建立了連接對方的通道胞谭,并訂閱了對方的 ActiveMQ.Advisory.Consumer.> 的topic垃杖。
- 當 A 上連接了一個消費者,訂閱topic1時丈屹,A便會往自己的 ActiveMQ.Advisory.Consumer.Topic.topic1 發(fā)送一條帶上了消費者信息(ConsumerInfo) 的消息调俘。
- B接收到這條 Advisory 消息以后,知道了A有一個消費者要消費topic1,就會建立一個專門的訂閱通道(DemandSubscription)彩库。
- 當 B 上收到生產(chǎn)者往 topic1 發(fā)送的消息時肤无,會同時往 A 上轉(zhuǎn)發(fā)一份。A收到以后再發(fā)送給自己的消費者侧巨。
從訂閱 Advisory Topic 到消息轉(zhuǎn)發(fā)舅锄,所有的動作都在 activemq-broker 的 org.apache.activemq.network.DemandForwardBridgeSupport 類中實現(xiàn)。其中 serviceRemoteConsumerAdvisory() 方法用于處理接收到的Advisory 消息司忱,該方法調(diào)用了 addConsumerInfo() 方法皇忿,用于建立 DemanSubscription 。
于是我修改了 DemandForwardBridgeSupport 和 AdvisoryBroker(用于發(fā)送 Advisory 消息)這兩個類坦仍,增加了一些日志來分析異常的場景下到底是哪一環(huán)節(jié)出了問題鳍烁。
##########正常的日志##########
1. Client-1 連接到 Broker A,Broker A 添加消費者繁扎,消費者ID為 Client-1
2. Broker A 發(fā)送 Advisory 消息幔荒,廣播Client-1的ConsumerInfo
3. Broker B 收到 Client-1的 Advisory 消息,添加消費者梳玫,消費者ID為 Broker B->Broker A
4. Broker B 發(fā)送 Advisory 消息爹梁,廣播 Broker B->Broker A 的ConsumerInfo
5. Broker C 收到 Client-1的 Advisory 消息,添加消費者提澎,消費者ID為 Broker C->Broker A
6. Broker C 發(fā)送 Advisory 消息姚垃,廣播 Broker C->Broker A 的ConsumerInfo
7. Broker C 收到 Broker B->Broker A 的 Advisory 消息,由于 networkTTL=1 的設置盼忌,不添加消費者积糯。
8. Broker B 收到 Broker C->Broker A 的 Advisory 消息,由于 networkTTL=1 的設置谦纱,不添加消費者嘉栓。
9. Broker A 收到 Broker B->Broker A 的 Advisory 消息缭付,由于 networkTTL=1 的設置究履,不添加消費者亡笑。
10. Broker A 收到 Broker C->Broker A 的 Advisory 消息,由于 networkTTL=1 的設置祠乃,不添加消費者窘游。
##########異常的日志##########
1. Client-1 連接到 Broker A,Broker A 添加消費者跳纳,消費者ID為 Client-1
2. Broker A 發(fā)送 Advisory 消息,廣播Client-1的ConsumerInfo
3. Broker B 收到 Client-1的 Advisory 消息贪嫂,添加消費者寺庄,消費者ID為 Broker B->Broker A
4. Broker B 發(fā)送 Advisory 消息,廣播 Broker B->Broker A 的ConsumerInfo
5. Broker C 收到 Broker B->Broker A 的 Advisory 消息,由于 networkTTL=1 的設置斗塘,不添加消費者赢织。
6. Broker A 收到 Broker B->Broker A 的 Advisory 消息,由于 networkTTL=1 的設置馍盟,不添加消費者于置。
可以看到,Broker C 異常場景下沒有接收到 Broker A 的消息贞岭。也就是說八毯,消息從 Broker A 中發(fā)出,但是在 Broker C 消費時丟失了瞄桨。
異常場景只在大量 Client 同時嘗試連接话速,且出現(xiàn)問題的 Topic 網(wǎng)段中 Client 數(shù)量較少。
問題解決
我嘗試了多種方法芯侥,始終無法解決Advisory消息沒收到的問題泊交,于是我在默認 Advisory 消息會丟的情況下設計了幾種解決方法:
- 設置 networkTTL=2
- 嘗試本地接收 Advisory 消息并存在內(nèi)存中,等到服務器不忙的時候直接再發(fā)一次柱查。
前一種方法對一個互相連接的集群來說廓俭,有極大的網(wǎng)絡負擔;而后一個方法邏輯比較復雜唉工,比較難判斷 Advisory 消息什么時候該發(fā)研乒。一怒之下我提了個 AMQ 嚴重BUG到JIRA上。一開始有人建議我升級到5.15以上的版本再試試酵紫,于是我試了下告嘲,問題仍然存在。
在我把我分析問題的流程寫到JIRA上以后奖地,有人回復我是我需要去掉AMQ的一個默認配置橄唬。
<policyEntry topic=">" >
<pendingMessageLimitStrategy>
<constantPendingMessageLimitStrategy limit="1000"/>
</pendingMessageLimitStrategy>
</policyEntry>
pendingMessageLimitStrategy這個配置項用于處理 Slow Consumer,AMQ官方的解釋是這樣的(Slow Consumers)
Slow Consumers can cause problems on non-durable topics since they can force the broker to keep old messages in RAM which once it fills up, forces the broker to slow down producers, causing the fast consumers to be slowed down. One option we could implement in the future is spooling to disk - but then spooling to disk could slow down the fast consumers too.
Currently we have a strategy that lets you configure the maximum number of matched messages the broker will keep around for a consumer in addition to its prefetch buffer. Once this maximum is reached, as new messages come in, older messages are discarded. This allows you to keep the RAM for current messages and keep sending messages to a slow consumer but to discard old messages.
也就是是說参歹,可配置一個 non-durable topic 的 consumer 可以讓MQ為其保存多少消息仰楚,由于 topic=">" 的寫法 ,Advisory的Topic也被包含在這個配置的使用范圍內(nèi)犬庇。所以僧界,當有大量 Client 連接上來時,MQ會發(fā)送大量的 Advisory Messages臭挽,如果集群中其他MQ的對 Advisory Messages沒有及時處理完捂襟,就會導致觸發(fā)這個機制,新的 Advisory 消息會持續(xù)把舊的 Advisory 消息頂出隊列(as new messages come in, older messages are discarded)欢峰。
綜上所述葬荷,如果場景中有多個MQ組成集群涨共,有大量的Client嘗試連接,就需要調(diào)整這個配置宠漩,避免出現(xiàn) advisory 消息被丟棄的情況举反。
Problem Solved.
山窮水復疑無路,柳暗花明又一村