- 前言
- 一、初識 RocketMQ
- 1.1基本模型
- 二、基本概念
- 2.1Producer
- 2.2Consumer
- 2.3Topic
- 2.4Tag
- 2.5Message
- 2.6Broker
- 2.7Pull Consumer
- 2.8Producer Group
- 2.9Consumer Group
- 2.10Ordered Message
- 三、高階特性
- 3.1訊息順序
- 3.2訊息可靠性
- 3.3延時佇列
- 3.4訊息重試
- 3.5死信佇列
- 四、文章小結
前言
RocketMQ 是阿里巴巴在 2012 年開源的分散式訊息中介軟體,目前已經捐贈給 Apache 軟體基金會,並於 2017 年 9 月 25 日成為 Apache 的頂級專案。
作為經歷過多次阿里巴巴雙十一這種“超級工程”的洗禮並有穩定出色表現的國產中介軟體,以其高效能、低延時和高可靠等特性近年來已經也被越來越多的國內企業使用。
一、初識 RocketMQ
2011 年初,Linkin 開源了 Kafka 這個優秀的訊息中介軟體,淘寶中介軟體團隊在對 Kafka 做過充分 Review 之後,被 Kafka 無限訊息堆積、高效的持久化速度等優點吸引了。
美中不足的的是,Kafka 主要定位於日誌傳輸,對於使用在淘寶交易、訂單、充值等場景下還有諸多特性不滿足。所以,阿里的中介軟體團隊重新用 Java 語言編寫了 RocketMQ ,定位於全場景的可靠訊息傳輸。
目前 RocketMQ 在阿里集團的應用生態裡被廣泛應用於訂單、交易、充值、物流、訊息推送、日誌處理, binglog 分發等場景。
RocketMQ 對比 Kafka,雖然設計的思想上有借鑑,但在架構上做了減法,在功能上做了加法:
- 去掉 Zookeeper,使用 NameServer 來管理 Broker 叢集,通訊更方便;
- 有延時佇列和死信佇列,開箱即用,減化程式碼邏輯實現。
1.1基本模型
RocketMQ 主要由 Producer、Broker、Consumer 三部分組成。其中,Producer 負責生產訊息,Consumer 負責消費訊息,Broker 負責儲存訊息。
而 Broker 在實際部署過程中對應的是一臺伺服器,每個 Broker 可以儲存多個 Topic 的訊息,每個Topic 的訊息也可以分片儲存於不同的 Broker。Message Queue 用於儲存訊息的實體地址,每個 Topic 中的訊息地址都會儲存在多個 Message Queue 中。
ConsumerGroup 由多個Consumer 例項構成。
二、基本概念
以下的基本概念是理解和掌握 RocketMQ 最基礎的概念,也是最重要的概念。現在不理解沒有關係,先記住有個印象,後續使用的時候,興許能幫助你豁然開朗。
2.1Producer
訊息生產者(Producer)負責生產訊息,一般由業務系統負責生產訊息。
一個訊息生產者會把業務應用系統裡產生的訊息(封裝好的訊息體)傳送到 Broker 伺服器。RocketMQ 提供多種傳送方式,同步傳送、非同步傳送、順序傳送、單向傳送。其中,同步和非同步方式均需要 Broker 返回確認資訊(即Ack),單向傳送不需要。
2.2Consumer
訊息消費者(Consumer)負責消費訊息,一般是由下游的系統負責非同步消費。
一個訊息消費者會從 Broker 伺服器預設主動拉取(Pull 模式)訊息,並將其提供給應用程式。從使用者應用的角度而言提供了兩種消費形式:拉取式消費(預設 Pull 模式)、推動式消費。
2.3Topic
主題(Topic)表示一類訊息的集合,每個主題包含若干條訊息,每條訊息只能屬於一個主題,是 RocketMQ 進行訊息訂閱的基本單位。
2.4Tag
標籤(Tag)為訊息設定的標誌,用於同一 Topic 下區分不同型別的訊息。來自同一業務單元的訊息,可以根據不同業務的功能模組在同一主題下設定不同的標籤。標籤能夠有效地保持程式碼的清晰度和連貫性,並最佳化 RocketMQ 提供的查詢系統。消費者可以根據 Tag 實現對不同子主題的不同消費邏輯,實現更好的擴充套件性。
2.5Message
訊息(Message)是系統所傳輸資訊的物理載體、生產和消費資料的最小單位,每條訊息必須屬於某一個主題。RocketMQ 中的每條訊息都擁有唯一的 Message ID 作為標識,且可以攜帶具有業務標識的 Key。系統提供了透過 Message ID 和 Key 查詢訊息的功能。
2.6Broker
代理伺服器(Broker)是訊息中轉角色,負責儲存訊息、轉發訊息。代理伺服器在 RocketMQ 系統中負責接收從生產者傳送來的訊息並儲存、同時為消費者的拉取請求作準備。代理伺服器也儲存訊息相關的後設資料,包括消費者組、消費進度偏移和主題和佇列訊息等。
2.7Pull Consumer
拉取式消費(Pull Consumer)是 Consumer 消費的一種型別,也是預設的型別。下游應用系統通常主動呼叫 Consumer 的拉訊息方法從 Broke r伺服器拉訊息,即主動權由下游應用控制。一旦獲取了批次訊息,應用就會啟動消費過程。
2.8Producer Group
生產者組(Producer Group)是同一類 Producer 的集合,這類 Producer 傳送同一類訊息且傳送邏輯一致。如果傳送的是事務訊息且原始生產者在傳送之後崩潰,則 Broker 伺服器會聯絡同一生產者組的其他生產者例項重試提交或回溯消費。
2.9Consumer Group
消費者組(Consumer Group)是同一類 Consumer 的集合,這類 Consumer 通常消費同一類訊息且消費邏輯一致。消費者組使得在訊息過程中實現負載均衡和提高容錯變得非常容易。要注意的是,消費者組的每個消費者例項必須訂閱完全相同的 Topic。RocketMQ 支援兩種訊息模式:叢集消費(Clustering)和廣播消費(Broadcasting)。
2.10Ordered Message
順序訊息分為普通順序消費(Normal Ordered Message)和嚴格順序訊息(Strictly Ordered Message)。
普通順序消費(Normal Ordered Message)下的消費者透過同一個訊息佇列(Message Queue) 收到的訊息是有順序的,不同訊息佇列收到的訊息則可能是無順序的。
而在嚴格順序訊息(Strictly Ordered Message)模式下,消費者收到的所有訊息均是嚴格有序的。
三、高階特性
3.1訊息順序
訊息有序指的是一類訊息消費時,能按照傳送的順序來消費,RocketMQ 可以嚴格的保證訊息有序。
例如:一個訂單產生了三條訊息分別是訂單建立、訂單付款、訂單完成。消費時要按照這個順序消費才能有意義,但是同時訂單之間是可以並行消費的。
順序訊息分為全域性順序訊息與分割槽順序訊息,全域性順序是指某個 Topic 下的所有訊息都要保證順序,部分順序訊息只要保證每一組訊息被順序消費即可。
-
全域性順序
-
對於指定的一個 Topic,所有訊息按照嚴格的先進先出(FIFO)的順序進行釋出和消費。
-
適用場景:效能要求不高,所有的訊息嚴格按照 FIFO 原則進行訊息釋出和消費的場景。
-
-
分割槽順序
-
對於指定的一個 Topic,所有訊息根據 sharding key 進行區塊分割槽。 同一個分割槽內的訊息按照嚴格的 FIFO 順序進行釋出和消費。 Sharding key 是順序訊息中用來區分不同分割槽的關鍵欄位,和普通訊息的 Key 是完全不同的概念。
-
適用場景:效能要求高,以 sharding key 作為分割槽欄位,在同一個區塊中嚴格的按照 FIFO 原則進行訊息釋出和消費的場景。
-
3.2訊息可靠性
RocketMQ 支援訊息的高可靠,以下是影響訊息可靠性的 6 種情況:
- Broker 非正常關閉
- Broker 異常 Crash
- OS Crash
- 機器掉電,但是能立即恢復供電情況
- 機器無法開機(可能是 cpu、主機板、記憶體等關鍵裝置損壞)
- 磁碟裝置損壞
其中上述的1、2、3、4 這四種情況都屬於硬體資源可立即恢復的情況,RocketMQ 在這四種情況下能保證訊息不丟失,或者丟失少量資料(取決於刷盤方式是同步還是非同步)。
而5、6這兩點屬於單點故障,無法恢復,一旦發生,在此單點上的訊息會全部丟失。
RocketMQ 在這兩種情況下,透過非同步複製可保證 99% 的訊息不丟失,但是仍然會有極少量的訊息可能丟失。透過同步雙寫技術可以完全避免單點,同步雙寫勢必會影響效能,適合對訊息可靠性要求極高的場合,例如與訂單、支付等相關的應用。注:RocketMQ 從 3.0 版本開始支援同步雙寫。
3.3延時佇列
延遲佇列是指訊息傳送到 Broker 後,不會立即被消費,等待特定時間後才會投遞給真正的 Topic。
Broker 有配置項 messageDelayLevel,預設值為:1s、5s、10s、30s、1min、2min、3min、4min、5min、6min、7min、8min、9min、10min、20min、30min、1h、2h 這 18 個 level。
也可以配置自定義 messageDelayLevel ,注意:messageDelayLevel 是 Broker 的屬性,不屬於某個 Topic。
發訊息時,設定 delayLevel 等級即可:msg.setDelayLevel(level)。level 有以下 3 種情況:
- level == 0,訊息為非延遲訊息
- 1<=level<=maxLevel,訊息延遲特定時間,例如 level==1,延遲1s
- level > maxLevel,則 level== maxLevel,例如 level==20,延遲 2h
定時訊息會暫存在名為 SCHEDULE_TOPIC_XXXX 的 Topic 中,並根據 delayTimeLevel 存入特定的 queue,queueId = delayTimeLevel – 1,即一個 queue 只存相同延遲的訊息,保證具有相同傳送延遲的訊息能夠順序消費。Broker 會排程地消費 SCHEDULE_TOPIC_XXXX,將訊息寫入真實的 Topic。
需要注意的是,定時訊息會在第一次寫入和排程寫入真實 Topic 時都會計數,因此傳送數量、TPS 都會變高,對效能可能會有一定影響。
3.4訊息重試
Consumer 消費訊息失敗後,可以提供一種重試機制,令訊息再消費一次。Consumer 消費訊息失敗通常可以認為有以下幾種情況:
- 由於訊息本身的原因
- 例如反序列化失敗,訊息資料本身無法處理(例如話費充值,當前訊息的手機號被登出,無法充值)等。
- 這種錯誤通常需要跳過這條訊息,再消費其它訊息,而這條失敗的訊息即使立刻重試消費,99% 也不成功,所以最好提供一種定時重試機制,即過 10 秒後再重試。
- 由於依賴的下游應用服務不可用
- 例如資料庫連線不可用,系統網路故障等。
- 遇到這種錯誤,即使跳過當前失敗的訊息,消費其他訊息同樣也會報錯。這種情況建議應用 sleep 30s,再消費下一條訊息,這樣可以減輕 Broker 重試訊息的壓力。
RocketMQ 會為每個消費組都設定一個 Topic 名稱為:%RETRY%+consumerGroup 的重試佇列。這裡需要注意的是:這個 Topic 的重試佇列是針對消費組,而不是針對每個 Topic 設定的,用於暫時儲存因為各種異常而導致 Consumer 端無法消費的訊息。
考慮到異常恢復起來需要一些時間,會為重試佇列設定多個重試級別,每個重試級別都有與之對應的重新投遞延時,重試次數越多投遞延時就越大。
RocketMQ 對於重試訊息的處理是先儲存至 Topic 名稱為:SCHEDULE_TOPIC_XXXX 的延遲佇列中,後臺定時任務按照對應的時間進行 Delay 後重新儲存至 %RETRY%+consumerGroup 的重試佇列中。
3.5死信佇列
死信佇列用於處理無法被正常消費的訊息。
當一條訊息初次消費失敗,訊息佇列會自動進行訊息重試。達到最大重試次數後,若消費依然失敗,則表明消費者在正常情況下無法正確地消費該訊息,此時,訊息佇列不會立刻將訊息丟棄,而是將其傳送到該消費者對應的特殊佇列中。
RocketMQ 將這種正常情況下無法被消費的訊息稱為死信訊息(Dead-Letter Message),將儲存死信訊息的特殊佇列稱為死信佇列(Dead-Letter Queue)。
在 RocketMQ 中,可以透過使用 consol e控制檯對死信佇列中的訊息進行重發來使得消費者例項再次進行消費。
四、文章小結
到這裡關於訊息佇列 RocketMQ 的基本結構就分享完了,其實本文主要還是介紹一些基本的概念,後續筆者還會分享一些在正真專案中的實踐,盡請期待。
最後,如果文章有不足和錯誤,還請大家指正。或者你有其它想說的,也歡迎大家在評論區裡交流!
參考文件:
https://github.com/apache/rocketmq/blob/master/docs/cn/concept.md
https://github.com/apache/rocketmq/blob/master/docs/cn/features.md