RocketMQ 主題擴分片後遇到的坑

消息組接到某項目組反饋,topic 在擴容後出現部分隊列無法被消費者,導致消息積壓,影響線上業務?

考慮到該問題是發送在真實的線上環境,爲了避免泄密,本文先在筆者的虛擬機中來重現問題。
@(本節目錄)

1、案情回顧

1.1 集羣現狀

集羣信息如下:
在這裏插入圖片描述
例如業務主體名 topic_dw_test_by_order_01 的路由信息如圖所示:
在這裏插入圖片描述
當前的消費者信息:
在這裏插入圖片描述
broker 的配置信息如下:

brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
brokerIP1=192.168.0.220
brokerIP2-192.168.0.220
namesrvAddr=192.168.0.221:9876;192.168.0.220:9876
storePathRootDir=/opt/application/rocketmq-all-4.5.2-bin-release/store
storePathCommitLog=/opt/application/rocketmq-all-4.5.2-bin-release/store/commitlog
autoCreateTopicEnable=false
autoCreateSubscriptionGroup=false

備註:公司對 topic、消費組進行了嚴格的管控,項目組需要使用時需要向運維人員申請,故 broker 集羣不允許自動創建主題與自動創建消費組。

由於該業務量穩步提升,項目組覺得該主題的隊列數太少,不利於增加消費者來提高其消費能力,故向運維人員提出增加隊列的需求。

1.2、RocketMQ 在線擴容隊列

運維通過公司自研的消息運維平臺,直接以指定集羣的方式爲 topic 擴容,該運維平臺底層其實使用了RocketMQ 提供的 updateTopic 命令,其命令說明如下:
圖片來源於《》RocketMQ技術內幕》
從上圖可以得知可以通過 -c 命令來指定在集羣中所有的 broker 上創建隊列,在本例中,將隊列數從 4 設置爲 8,具體命令如下:

sh ./mqadmin upateTopic -n 192.168.0.220:9876 -c DefaultCluster -t topic_dw_test_by_order_01 -r 8 -w 8

執行效果如圖所示,表示更新成功。
在這裏插入圖片描述
我們再來從 rocketmq-console 中來看命令執行後的效果:
在這裏插入圖片描述
從上圖可以得知,主題的隊列數已經擴容到了8個,並且在集羣的兩臺broker上都創建了隊列。

1.3 消息發送

從 RocketMQ 系列可知,RocketMQ 是支持在線 topic 在線擴容機制的,故無需重啓 消息發送者、消息消費者,隨着時間的推移,我們可以查看topic的所有隊列都參與到了消息的負載中,如圖所示:
在這裏插入圖片描述
我們可以清晰的看到,所有的16個隊列(每個 broker 8個隊列)都參與到了消息發送的,運維小哥愉快的完成了topic的擴容。

2、問題暴露

該 topic 被 5個消費組所訂閱,突然接到通知,其中有兩個消費組反饋,部分隊列的消息沒有被消費,導致下游系統並沒有及時處理。

3、問題分析

當時到項目組提交到消息組時,我第一反應是先看消費者的隊列,打開該主題的消費情況,如圖所示:
在這裏插入圖片描述
發現隊列數並沒有積壓,備註(由於生產是4主4從,每一個 broker上8個隊列,故總共32個隊列),當時由於比較急,並沒有第一時間發現這個界面,竟然只包含一個消費者,覺得並沒有消息積壓,又由於同一個集羣,其他消費組沒有問題,只有兩個消費組有問題,懷疑是應用的問題,就採取了重啓,打印線程棧等方法。

事後諸葛亮:其實這完成是錯誤的,爲什麼這樣說呢?因爲項目組(業務方)已經告知一部分業務未處理,說明肯定有隊列的消息積壓,當根據自己的知識,結合看到的監控頁面做出的判斷與業務方反饋的出現衝突時,一定是自己的判斷出了問題。

正在我們“如火如荼”的認定是項目有問題時,團隊的另一成員提出了自己的觀點,原來在得到業務方反饋時,他得知同一個主題,被5個消費組訂閱,只有其中兩個有問題,那他通過rocketmq-console來找兩者的區別,找到區別,找到規律,就離解決問題的路近了。

他通過對比發現,出問題的消費組只有兩個客戶端在消費(通常生產環境是4節點消費),而沒有出現問題的發現有4個進程都在處理,即發現現象:出錯的消費組,並沒有全員參與到消費。正如上面的圖所示:只有其中一個進程在處理8個隊列,另外8個隊列並沒有在消費。

那現在就是要分析爲啥topic共有16個隊列,但這裏只有1個消費者隊列在消費,另外一個消費者不作爲?

首先根據RocketMQ 消息隊列負載機制,2個消費者,只有1個消費者在消費,並且一個有一個明顯的特點是,只有broker-a上的隊列在消費,broker-b上的隊列一個也沒消費。

正在思考爲啥會出現這種現象時,他又在思考是不是集羣是不是broker-b(對應我們生產環境是broker-c、broker-d上的隊列都未消費)是新擴容的機器?擴容的時候是不是沒有把訂閱關係在新的集羣上創建?提出了疑問,接下來肖工就開始驗證猜想,通過查閱broker-c、broker-d在我們系統中創建的時間是2018-4月的時候,就基本得出結論,擴容時並沒有在新集羣上創建訂閱消息,故無法消費消息。

於是運維小哥使用運維工具創建訂閱組,創建方法如圖所示:
在這裏插入圖片描述
創建好消費組後,再去查看topic的消費情況時,另外一個消費組也開始處理消息了,如下圖所示:
在這裏插入圖片描述

4、問題覆盤

潛在原因:DefaultCluster 集羣進行過一次集羣擴容,從原來的一臺消息服務器( broker-a )額外增加一臺broker服務器( broker-b ),但擴容的時候並沒有把原先的存在於 broker-a 上的主題、消費組擴容到 broker-b 服務器。

觸發原因:接到項目組的擴容需求,將集羣隊列數從4個擴容到8個,這樣該topic就在集羣的a、b都會存在8個隊列,但Broker不允許自動創建消費組(訂閱關係),消費者無法從broker-b上隊列上拉取消息,導致在broker-b隊列上的消息堆積,無法被消費。

解決辦法:運維通過命令,在broker-b上創建對應的訂閱消息,問題解決。

經驗教訓:集羣擴容時,需要同步在集羣上的topic.json、subscriptionGroup.json文件。

RocketMQ 理論基礎,消費者向 Broker 發起消息拉取請求時,如果broker上並沒有存在該消費組的訂閱消息時,如果不允許自動創建(autoCreateSubscriptionGroup 設置爲 false),默認爲true,則不會返回消息給客戶端,其代碼如下:
在這裏插入圖片描述
問題解決後,我們團隊的成員也分享了一下他在本次排查問題的處理方法:尋找出現問題的規律、推斷問題、 然後驗證問題。規律可以是問題本身的規律 也可以是和正常對比的差。

如果覺得文章寫的還不錯的話,麻煩幫忙點個贊,謝謝。


作者介紹:丁威,《RocketMQ技術內幕》作者,RocketMQ 社區佈道師,公衆號:中間件興趣圈 維護者,目前已陸續發表源碼分析Java集合、Java 併發包(JUC)、Netty、Mycat、Dubbo、RocketMQ、Mybatis等源碼專欄。可以點擊鏈接加入中間件知識星球 ,一起探討高併發、分佈式服務架構,交流源碼。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章