RocketMQ 順序消費(fèi)

1酷含、前言

對于所有的 MQ 來說汪茧,必問的一道面試題就是 RocketMQ 順序消息怎樣做?原理是什么什往?

首先我們要明確什么順序消費(fèi)慌闭,順序消費(fèi)的定義是什么躯舔?我所理解的順序消費(fèi),指的針對某一類消息丧失,比如都是訂單A 的消息來說惜互,它的消費(fèi)有先后順序琳拭,類似于 FIFO描验。假設(shè)訂單 A 有創(chuàng)建、付款膘流、完成這幾類消息,我們對于訂單 A 的消息耕魄,必須要滿足先消費(fèi)創(chuàng)建彭谁,其次是付款,最后是完成奄抽。

所以針對整個鏈路來說甩鳄,我們不僅需要塞的時(shí)候是有序的,消費(fèi)的時(shí)候也應(yīng)該做到有序妙啃。就算是以 FIFO 順序塞進(jìn)去,消費(fèi)如果使用多線程同時(shí)消費(fèi)同一個 ConsumerQueue 且同時(shí)能消費(fèi)多個消息馆匿,那必然做不到有序燥滑。接下來,會從 provider铭拧、consumer 兩個方面說明如何做到有序。

首先針對順序消息呕臂,生產(chǎn)者可以是多線程的肪跋,只要保證每個線程發(fā)的是不同類型的消息(如發(fā)生不同訂單的消息),那么在不同的分區(qū)就可以保證有序谜洽;

2、實(shí)現(xiàn)

針對 provider 來說阐虚,RocketMQ 提供了發(fā)送順序消息的方式,即 MessageQueueSelector:

public interface MessageQueueSelector {
    MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);
}

provider 在發(fā)送的時(shí)候贸宏,只要選擇消息發(fā)送到那個 ConsumerQueue 即可磕洪。比如訂單來說,使用訂單 id 作為 key 選擇隊(duì)列鲫咽,那么同一個訂單的消息必定能發(fā)送到同一個隊(duì)列谷异。

SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
               @Override
               public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                   Long id = (Long) arg;  //根據(jù)訂單id選擇發(fā)送queue
                   long index = id % mqs.size();
                   return mqs.get((int) index);
               }
           }, orderList.get(i).getOrderId());//訂單id

所以 provider 的順序發(fā)送異常簡單。


針對 consumer 來說箩绍,需要使用 MessageListenerOrderly 來消費(fèi)消息:

consumer.registerMessageListener(new MessageListenerOrderly() {
           @Override
           public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
               context.setAutoCommit(true);
               for (MessageExt msg : msgs) {
                   // 可以看到每個queue有唯一的consume線程來消費(fèi), 訂單對每個queue(分區(qū))有序
                   System.out.println("consumeThread=" + Thread.currentThread().getName() + "queueId=" + msg.getQueueId() + ", content:" + new String(msg.getBody()));
               }
               return ConsumeOrderlyStatus.SUCCESS;
           }
       });

consumer 順序消費(fèi)的原理也很簡單尺上。消費(fèi)者消費(fèi)消息的時(shí)候,會有一個 PullMessageService 拉取線程(單線程)拉取消息卑吭,然后放入到 processQueue(每個消費(fèi)隊(duì)列對應(yīng)一個 processQueue) 中马绝,因?yàn)槭菃尉€程拉取的,對于同一個隊(duì)列的消息(雖然消費(fèi)者可以訂閱多個隊(duì)列富稻,但是對于同一個隊(duì)列是有序的)是有序的唉窃。在放入 processQueue 之后纹笼,會調(diào)用 ConsumeMessageConcurrentlyService 或 ConsumeMessageOrderlyService 來進(jìn)行消費(fèi),這里是調(diào)用 ConsumeMessageOrderlyService 進(jìn)行消費(fèi)。ConsumeMessageOrderlyService 在消費(fèi)的時(shí)候件已,會先獲取每一個 ConsumerQueue 的鎖元暴,然后從 processQueue 獲取消息消費(fèi),這也就意味著茉盏,對于每一個 ConsumerQueue 的消息來說,消費(fèi)的邏輯也是順序的铜秆。

3讶迁、缺點(diǎn)

  • 發(fā)送順序消息無法利用集群 FailOver 特性(發(fā)送時(shí)已經(jīng)選定發(fā)到哪個隊(duì)列)
  • 消費(fèi)順序消息的并行度依賴于隊(duì)列數(shù)量
  • 隊(duì)列熱點(diǎn)問題,個別隊(duì)列由于哈希不均導(dǎo)致消息過多啸驯,消費(fèi)速度跟不上祟峦,產(chǎn)生消息堆積問題
  • 遇到消息失敗的消息,無法跳過宅楞,當(dāng)前隊(duì)列消費(fèi)暫停

不能更換MessageQueue重試就需要MessageQueue有自己的副本,通過Raft搓幌、Paxos之類的算法保證有可用的副本迅箩,或者通過其他高可用的存儲設(shè)備來存儲MessageQueue。

熱點(diǎn)問題好像沒有什么好的解決辦法饲趋,只能通過拆分MessageQueue和優(yōu)化路由方法來盡量均衡的將消息分配到不同的MessageQueue奕塑。

消費(fèi)并行度理論上不會有太大問題,因?yàn)镸essageQueue的數(shù)量可以調(diào)整龄砰。

消費(fèi)失敗的無法跳過是不可避免的讨衣,因?yàn)樘^可能導(dǎo)致后續(xù)的數(shù)據(jù)處理都是錯誤的式镐。不過可以提供一些策略娘汞,由用戶根據(jù)錯誤類型來決定是否跳過,并且提供重試隊(duì)列之類的功能你弦,在跳過之后用戶可以在“其他”地方重新消費(fèi)到這條消息。

4扮叨、感悟

其實(shí)對于所謂的順序消費(fèi)來說领迈,本質(zhì)上是類似于一個狀態(tài)機(jī)的行為,比如一個訂單先創(chuàng)建狸捅,后付款、最后結(jié)束的行為磁浇,完全可以定義一個狀態(tài)朽褪,而且發(fā)生的順序是有先后的。所以完全不必要使用什么順序消費(fèi)衍锚,可以先創(chuàng)建嗤堰,把創(chuàng)建消息塞到 mq,從 mq 獲取到創(chuàng)建消息消費(fèi)踢匣,然后創(chuàng)建一個付款消息,再塞到 mq后专。然后從 mq 消費(fèi)付款消息输莺,然后標(biāo)識訂單結(jié)束裸诽。完全可以用一個狀態(tài)機(jī) + mq + db 來做建瘫,更加穩(wěn)定通用尸折。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末实夹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子亮航,更是在濱河造成了極大的恐慌缴淋,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件露氮,死亡現(xiàn)場離奇詭異钟沛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)恨统,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門畜埋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悠鞍,你說我怎么就攤上這事∪沓郏” “怎么了心肪?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵硬鞍,是天一觀的道長戴已。 經(jīng)常有香客問我锅减,道長,這世上最難降的妖魔是什么怔匣? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任每瞒,我火速辦了婚禮,結(jié)果婚禮上剿骨,老公的妹妹穿的比我還像新娘。我一直安慰自己挤庇,他們只是感情好贷掖,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布嫡秕。 她就那樣靜靜地躺著,像睡著了一般羽资。 火紅的嫁衣襯著肌膚如雪淘菩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天屠升,我揣著相機(jī)與錄音潮改,去河邊找鬼。 笑死腹暖,一個胖子當(dāng)著我的面吹牛汇在,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脏答,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼糕殉,長吁一口氣:“原來是場噩夢啊……” “哼殖告!你這毒婦竟也來了阿蝶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤黄绩,失蹤者是張志新(化名)和其女友劉穎羡洁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爽丹,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筑煮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年辛蚊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片真仲。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡袋马,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秸应,到底是詐尸還是另有隱情虑凛,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布灸眼,位于F島的核電站卧檐,受9級特大地震影響墓懂,放射性物質(zhì)發(fā)生泄漏焰宣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一捕仔、第九天 我趴在偏房一處隱蔽的房頂上張望匕积。 院中可真熱鬧,春花似錦榜跌、人聲如沸闪唆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悄蕾。三九已至,卻和暖如春础浮,著一層夾襖步出監(jiān)牢的瞬間帆调,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工豆同, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留番刊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓影锈,卻偏偏與公主長得像芹务,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鸭廷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1.在進(jìn)行負(fù)載分配時(shí)候枣抱,會把不屬于的隊(duì)列進(jìn)行鎖釋放,如果釋放成功辆床,從待處理隊(duì)列中移除掉時(shí)佳晶, 進(jìn)行新隊(duì)列加入操作,嘗...
    三個字真絕了閱讀 1,252評論 0 0
  • 1 RocketMQ基本理論 1.1 發(fā)展歷史 阿里巴巴消息中間件起源于2001年的五彩石項(xiàng)目佛吓,Notify在這期...
    MiniSoulBigBang閱讀 3,204評論 5 30
  • 一個Topic下對應(yīng)著多個隊(duì)列宵晚。 以官方文檔的例子垂攘,一個訂單的順序流程是:創(chuàng)建、付款淤刃、推送晒他、完成。 這里有三個訂單...
    markeNick閱讀 264評論 0 1
  • 1逸贾、什么是順序消息 順序消息(FIFO 消息)是 MQ 提供的一種嚴(yán)格按照順序進(jìn)行發(fā)布和消費(fèi)的消息類型陨仅。順序消息由...
    冰河winner閱讀 2,958評論 0 2
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒铝侵。表情可以傳達(dá)很多信息灼伤。高興了當(dāng)然就笑了,難過就哭了咪鲜。兩者是相互影響密不可...
    Persistenc_6aea閱讀 125,318評論 2 7