前不久踩了個坑,而這個坑跟 RocketMQ 推薦的一個最佳實踐有關。
看下我從官網的截圖,官方推薦一個應用盡可能只用一個 topic,然后用 tags 來標識子類型。
從訂單角度來看,可以用一個 Topic-Order,然后再用不同的 tag 來區分這是 3C 類的訂單,還是母嬰類的訂單等,然后下游應用根據不同的需求過濾不同的 tag。
這樣的實現方式從業務上來看關系更清晰(有點樹狀的感覺),但是在實踐上有點問題。
問題和起因
一般而言,生產上同一個服務至少會部署兩臺機器,不僅僅是為了負載均衡,也是為了系統的可靠性,當一臺機器意外掛了,另一臺可以扛起大旗。
我們在發服務的時候,都是分批發布。
這是為了驗證新功能的正確性,不讓其一次性影響所有實例,我們會先發布一批,然后觀察下日志,確保無誤后繼續發布后續幾臺機器。
而這個操作再結合 RocketMQ 一個 Topic 多 tag 就會出現訂閱消息不一致的情況,導致丟消息。
原理分析
我們借用官網的圖來分析一下。
一般我們都用集群模式,以下描述默認使用集群模式
從使用層來看,發送和消費消息給我們最直觀的感受如下:
生產者往一個 Topic 發送消息,消費者訂閱了這個 Topic 就能消費到這個消息。
而實際上在 RocketMQ 中有隊列的概念:
也就是生產者往一個 Topic 發送消息時,消息會被分到不同的隊列中。
而屬于同一個消費組的消費者們會平分消費這些隊列,從上圖可以看到 Topic A 分了三個隊列,分別是 MessageQueue 0、1、2。
而消費組 ConsumerGroupA 中的 Consumer1 僅消費 MessageQueue0 和 MessageQueue1 這兩個隊列中的消息,而 Consumer2 僅消費 MessageQueue2。
這樣劃分后,Consumer1 是無法消費到 MessageQueue2 中的消息的。
看到可能有人會說,這跟 tag 有什么關系嗎?沒錯,問題就在這個分割跟 tag 沒關系!
在默認情況下生產者發送消息是以輪詢隊列的方式發送的。
比如現在 Producer A 要發送 TopicA-tag1、TopicA-tag2、TopicA-tag3 這三條數據,輪詢發送后,MessageQueue 0、1、2 分別存儲了這 3 條消息。
假設同樣訂閱了 TopicA,但是 Consumer 1訂閱的 tag 是 tag1和 tag3,而 Consumer 2 訂閱的是 tag1、tag2,那么問題就來了。
按輪詢的順序 Consumer 1 要消費的 tag3 被投遞到 MessageQueue2 這個隊列中,而 Consumer 1 又無法消費 MessageQueue2 中的消息,Consumer 2 能消費 MessageQueue2 中的消息,但偏偏它又不要 tag3 的消息。這樣一來 tag3 的這條消息就丟了,問題就出現了。
所以,在實踐中,我們要求同一個消費組的消費者的訂閱關系要保持一致。
也就是 Conusmer1 和 Conusmer2 需要訂閱一樣的 Topic、一樣的 tag,這樣消息才不會丟失。
再回到問題
現在我們已經知道訂閱關系一致的重要性,但是有時候不得已就會“明知故犯”。
假設我們訂單服務線上一共部署了 5 臺,這 5 臺機器屬于同一個消費組,因此它們負載均衡消費有關訂單的消息,如 Topic-Order。
這 5 臺機器部署的都是同一套代碼,它們都訂閱了 Topic-Order,且 tag 是 A、B、C 三個。
這次發版需要訂單服務新增消費 Topic-Order 下的 tag D 消息,由于分批部署,所以先部署了 1 臺機器觀察。
而此時線上就出現了訂閱關系不一致的情況!5臺機器,有 1 臺訂閱了 Topic-Order tag A、B、C、D,而其他 4 臺訂閱了 Topic-Order tag A、B、C。
這段時間內就出現了上述所說的丟消息的情況,如果有 Topic-Order tagD 的消息產生,那么就有可能會丟了。
明知有錯,不想犯,卻犯了!
針對這個場景,我暫時沒啥思路,不知道業界是否有什么方式可以優雅的處理這個問題?歡迎各位留言指導或探討!
然后留個坑,如果一臺機器訂閱的是 tagA||tagB,而另一臺訂閱的是 tagB||tagA,這樣算訂閱消息一致嗎?
審核編輯:劉清
-
機器人
+關注
關注
211文章
28468瀏覽量
207359 -
過濾器
+關注
關注
1文章
430瀏覽量
19630
原文標題:RocketMQ 最佳實踐之坑
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論