消息組接到某項目組反饋,topic 在擴容後出現部分隊列沒法被消費者,致使消息積壓,影響線上業務?json
考慮到該問題是發送在真實的線上環境,爲了不泄密,本文先在的虛擬機中來重現問題。服務器
集羣信息以下:
微信
例如業務主體名 topic_dw_test_by_order_01 的路由信息如圖所示:
當前的消費者信息:
broker 的配置信息以下:app
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 集羣不容許自動建立主題與自動建立消費組。運維
因爲該業務量穩步提高,項目組以爲該主題的隊列數太少,不利於增長消費者來提升其消費能力,故向運維人員提出增長隊列的需求。ide
運維經過公司自研的消息運維平臺,直接以指定集羣的方式爲 topic 擴容,該運維平臺底層其實使用了RocketMQ 提供的 updateTopic 命令,其命令說明以下:
圖片來源於《》RocketMQ技術內幕》線程
從上圖能夠得知能夠經過 -c 命令來指定在集羣中全部的 broker 上建立隊列,在本例中,將隊列數從 4 設置爲 8,具體命令以下:3d
sh ./mqadmin upateTopic -n 192.168.0.220:9876 -c DefaultCluster -t topic_dw_test_by_order_01 -r 8 -w 8
執行效果如圖所示,表示更新成功。code
咱們再來從 rocketmq-console 中來看命令執行後的效果:
從上圖能夠得知,主題的隊列數已經擴容到了8個,而且在集羣的兩臺broker上都建立了隊列。blog
從 RocketMQ 系列可知,RocketMQ 是支持在線 topic 在線擴容機制的,故無需重啓 消息發送者、消息消費者,隨着時間的推移,咱們能夠查看topic的全部隊列都參與到了消息的負載中,如圖所示:
咱們能夠清晰的看到,全部的16個隊列(每一個 broker 8個隊列)都參與到了消息發送的,運維小哥愉快的完成了topic的擴容。
該 topic 被 5個消費組所訂閱,忽然接到通知,其中有兩個消費組反饋,部分隊列的消息沒有被消費,致使下游系統並無及時獲得處理,引發用戶的注意。
當時到項目組提交到消息組時,我第一反應是先看消費者的隊列,打開該主題的消費狀況,如圖所示:
發現隊列數並無積壓,備註(因爲生產是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-7月 的時候,就基本得出結論,是否是擴容時並無在新集羣上建立訂閱消息,故沒法消費消息,後面一查證,果真如此。
而後運維小哥,立馬建立訂閱組,建立方法如圖所示:
建立好消費組後,再去查看topic的消費狀況時,另一個消費組也開始處理消息了,以下圖所示:
潛在緣由: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,則不會返回消息給客戶端,其代碼以下:
問題解決後,團隊內部成員也分享了一下他在本次排查問題的處理方法:尋找出現問題的規律、推斷問題、 而後驗證問題。規律能夠是問題自己的規律 也能夠是和正常對比的差。
更多文章請關注微信公衆號: