概述
? ? 這篇文章是以同事在實(shí)際工作中遇到的問(wèn)題作為分析的切入點(diǎn),加深自己對(duì)mq的掌握,踐行“干中學(xué)”的團(tuán)隊(duì)理念。
? ? 當(dāng)自己差不多把基本概念都掌握的差不多的時(shí)候,必須需要實(shí)際的案例或者實(shí)踐來(lái)提深自己的深度,這個(gè)時(shí)候just do it 變得很重要,所以我喜歡不停的被人挑戰(zhàn),截止目前幫人解答的問(wèn)題包括:client端消息堆積問(wèn)題、批量消息拉取問(wèn)題中遇到的神奇的數(shù)字32、以及本篇的tag不一致造成的假象,也就說(shuō)會(huì)有3篇文章輸出。
? ? 整個(gè)mq的問(wèn)題解決文章會(huì)收錄在mq的另外一個(gè)《rocketMq干中學(xué)》專題當(dāng)中,歡迎訂閱,歡迎挑戰(zhàn)。
背景
????某次線上發(fā)布升級(jí)mq的消費(fèi)端修改訂閱的topic對(duì)應(yīng)的tags,為了保證穩(wěn)定性,采取了灰度發(fā)布策略,也就說(shuō)發(fā)布一臺(tái)服務(wù)后觀察一段時(shí)間看是否正常再全量發(fā)布。
? ? 升級(jí)的內(nèi)容為consumer訂閱的tags信息,灰度一臺(tái)之后存在同一個(gè)consumeGroup下有多個(gè)consumer,且其中一個(gè)consumer的topic的tags信息和其他consumer不一致。
? ? 如我們?cè)赾onsumeGroupA下有3個(gè)consumer,一開始3個(gè)consumer訂閱了topicA + tagA||tagB,然后我們升級(jí)一個(gè)consumer訂閱topicA+tagC,這個(gè)時(shí)候在同一個(gè)consumeGroup下針對(duì)同一個(gè)topic會(huì)有兩個(gè)不同的訂閱信息。
? ? 升級(jí)以后的現(xiàn)象是什么呢,重要的事情說(shuō)3遍,說(shuō)3遍,說(shuō)3遍。
? ? 升級(jí)后我們發(fā)現(xiàn)所有的consumer都沒(méi)有消費(fèi)數(shù)據(jù)的記錄!
?????升級(jí)后我們發(fā)現(xiàn)所有的consumer都沒(méi)有消費(fèi)數(shù)據(jù)的記錄!?
????升級(jí)后我們發(fā)現(xiàn)所有的consumer都沒(méi)有消費(fèi)數(shù)據(jù)的記錄!
復(fù)現(xiàn)
????請(qǐng)按照以下順序進(jìn)行復(fù)現(xiàn)操作
????????1、啟動(dòng)consumeA,負(fù)責(zé)訂閱orderTopic,tags為A||D;
? ? ? ? 2、等待一段時(shí)間,待consumeA啟動(dòng)完成;
? ? ? ? 3、producer發(fā)送消息,發(fā)現(xiàn)consumeA正常消費(fèi)消息;
? ? ? ? 4、啟動(dòng)consumeB,負(fù)責(zé)訂閱orderTopic,tags為F;
? ? ? ? 5、producet發(fā)送消息,發(fā)現(xiàn)consumeA和consumerB都沒(méi)有消費(fèi)記錄;
? ? ? ? 6、理論上這個(gè)時(shí)候consumeA應(yīng)該能夠消費(fèi)(tags一致),但是事實(shí)上卻沒(méi)有。



原因分析
? ? 在復(fù)現(xiàn)問(wèn)題以后,基本上你知道離定位問(wèn)題就不遠(yuǎn)了,其實(shí)對(duì)于經(jīng)常出現(xiàn)的問(wèn)題你只要靜下心去排查問(wèn)題就不大了,
我的問(wèn)題排查理念:
? ? 1、對(duì)mq在訂閱topic的過(guò)程和消息拉取的過(guò)程在心里要有一個(gè)宏觀的理解,說(shuō)白了在心里面要清楚整個(gè)交互過(guò)程,看整個(gè)交互過(guò)程中是不是可能本身就存在這個(gè)邏輯缺陷。
? ? 2、在原來(lái)日志不能夠幫助定位你的問(wèn)題的時(shí)候,在可以獲取源碼的時(shí)候增加日志,增加在懷疑的執(zhí)行路徑上。
我的問(wèn)題排查過(guò)程:
? ? 1、排查rocketMq訂閱消息的邏輯
? ? 2、排查rocketMq訂閱關(guān)系同步的邏輯
訂閱過(guò)程-client端:

說(shuō)明:
? ? 1、在consumer端訂閱的時(shí)候我們會(huì)在本地保存一個(gè)訂閱數(shù)據(jù),在這個(gè)訂閱數(shù)據(jù)里面有一個(gè)字段非常重要,就是用時(shí)間戳來(lái)代表的訂閱消息版本信息。

說(shuō)明:
? ? 1、定時(shí)通過(guò)心跳信息發(fā)送訂閱數(shù)據(jù)到broker,也就是說(shuō)我們會(huì)把訂閱信息多次發(fā)送。
? ? 2、定時(shí)同步broker的訂閱信息到client端,也就是最終都會(huì)拷貝到一份最新的訂閱信息。

說(shuō)明:
? ? 1、在我們創(chuàng)建SubscriptionData的時(shí)候我們其實(shí)用時(shí)間戳代表了版本號(hào),這個(gè)東西非常重要,因?yàn)樵赽roker端我們會(huì)通過(guò)版本號(hào)來(lái)區(qū)分最新數(shù)據(jù)。
訂閱過(guò)程-broker端

說(shuō)明
? ? broker端處理的入口函數(shù),相當(dāng)于接收consumer的心跳數(shù)據(jù)的處理函數(shù)。

說(shuō)明:
? ? 核心關(guān)鍵點(diǎn),我們每次只會(huì)用最新版本號(hào)的訂閱數(shù)據(jù)。
消息拉取-server端

說(shuō)明
? ? 在broker端進(jìn)行消費(fèi)的時(shí)候我們會(huì)根據(jù)subscriptionData來(lái)判斷這個(gè)消息是否屬于tag內(nèi)的消息,如果不是指定tag的消息,就返回false直接過(guò)濾消息。
消息拉取-client端

說(shuō)明
? ? client端也做了類似的過(guò)濾,不知道是處于什么考慮,但是broker端已經(jīng)對(duì)消息進(jìn)行了過(guò)濾。
結(jié)論
? ? 1、同一個(gè)consumeGroup下面的多個(gè)client定時(shí)向broker發(fā)送心跳信息,匯報(bào)自己最新的subscription信息,broker端在收到消息后以最新版本的訂閱消息為準(zhǔn)。??
? ? 2、broker端在收到client拉取消息的請(qǐng)求后,會(huì)從broker的store中獲取消息數(shù)據(jù)并以subscription信息去進(jìn)行過(guò)濾,這個(gè)是關(guān)鍵的地方,broker在獲取數(shù)據(jù)的時(shí)候會(huì)用最新的subscription去進(jìn)行過(guò)濾。
? ? 3、我們這個(gè)現(xiàn)象原因就是舊的subscription(tag為A||D)信息和新的subscription(tag為F)信息不一致,我們以最新的subscription(tag為F)為準(zhǔn),這個(gè)時(shí)候即便你發(fā)送的消息tag為A||D,在消息消費(fèi)的會(huì)因?yàn)樽钚碌膕ubscription(tag為F)被過(guò)濾掉。
其他輔助信息
在消費(fèi)數(shù)據(jù)的時(shí)候會(huì)不停的打印錯(cuò)誤日志:NO_MATCHED_MSG
