線上問題之:RocketMq重複消費

在懷疑是系統(伺服器)問題前,先多考慮是否是程式本身的問題

大過年在家裡,正玩王者,突然收到帶的新人徒弟發來的微信訊息,說線上mq資料統計錯亂了,有的多,有的少,說可能RocketMq叢集有問題。影響到了使用者報表,十分緊急,無奈之下,只能坑隊友一把(掛機),連上easyConnect,幫徒弟定位問題。

剛上線,就看到徒弟已經拉了一個群,裡面拉了運維同事,讓運維緊急看下mq訊息錯亂,出現線上問題了,運維小哥十分委屈的說不太可能,mq跑幾年了也從來沒出現這個問題,一定是用的有問題或者資料本來就是如此也不是不可能。兩個人一直在爭吵,推諉。。。

功能簡介:這個功能是一個訊息量統計功能,類似於:統計每個使用者在我司app上的發言量(視窗統計,每分鐘,每10分鐘等等這樣)。

定位過程:

1、我心想,mq不太可能出現這種低階問題。會不會是app端上報重複(上報漏)了?於是我先自己驗證下,自己登入app發言一次後,發現統計量確實「是2」。然後看nginx日誌,發現app側「只調用了一次」。排除了app的問題(暫時先不管統計少的問題,這時已經發現端倪了)

2、懷疑寫入mq寫了2次?是這樣的,服務收到app請求後,做簡單業務處理後,丟入mq,供下游各種程式進行消費做業務(解耦)。然後問了其他業務,別人資料是正常的,沒有出現翻倍;並且開debug看了下日誌,寫入mq只有一次。

3、極有可能是徒弟的消費統計程式有問題。看了徒弟的統計邏輯程式碼,看了好久沒看出破綻,百思不得其解,無意間看了眼consumer初始化程式碼,終於發現問題了!!!徒弟初始化mq是這樣的:

線上問題之:RocketMq重複消費

徒弟mq消費者初始化部分程式碼

問題就在這裡,由於徒弟的程式是多例項部署在一臺ecs上的,在一臺ecs上啟動了2個消費者(jvm)程序,如果此時設定了固定的instanceName,則會造成「部分訊息重複消費」「部分訊息丟失!」,mq底層關鍵程式碼如下:

線上問題之:RocketMq重複消費

mq內部給消費者分發queue的部分程式碼

可以看到,分發queue的憑據是cid(下面那一坨算數運算先不care),憑直覺,各位是不是已經感覺到了,如果cid相同,那鐵定是消費相同的queue,那不就消費了2次嘛,好了,上證據:

線上問題之:RocketMq重複消費

mq底層生成cid的邏輯

可以看到,cid的邏輯是:ip+${instanceName}+${unitName},而如果不設定instanceName,則底層預設用jvm的pid。unitName一般不會設定。這樣各位就看清楚了吧,就是因為徒弟設定了就造成了固定的instanceName(見圖1,KTJ_user_msg_stats),所以他的兩個消費者的cid是相同的,如:127。0。0。1@KTJ_user_msg_stats,所以在給消費者分派queue時,這2個消費者被分到了相同的queue。造成了訊息重複。

問題到這裡還沒有結束,為什麼會有訊息丟失?順水推舟呀,見圖2,本身這個topic有4個queue,2個消費者消費了相同的佇列,剩餘2個佇列沒有被消費(沒有被分派消費者),所以有2個佇列的訊息丟失!(看mq的lag監控也可以看到,lag一直在上漲!)

解決措施:刪除設定instaceName的程式碼,重啟服務,發現統計正常。當然,也可以設定全域性唯一的instanceName。

覆盤總結:出現問題時,特別是依賴了中介軟體時,不要上去就把“皮球”踢給中介軟體(中介軟體不背鍋)大多數還是要檢查自身的使用姿勢是否正確。不是說中介軟體不會有問題,而是這種低級別的問題,早就被官方fix了。假設您真的發現了中介軟體問題,不要猶豫,去git提issue,說不定還能“出個名”!

思維發散:如果徒弟的程式碼是在不同的機器上啟動,會不會有這個問題?為什麼,評論區見

好了,打王者去了。