RocketMQ系列1:基礎介紹

Hello-Brand發表於2024-12-01

★訊息佇列16篇

1 認識RocketMQ

RocketMQ是一款基於Java開發的分散式訊息中介軟體,它以其高效能、高可靠性、高實時性以及分散式特性而廣受好評。

它支援事務訊息、順序訊息、批次訊息、定時訊息、訊息回溯等。網際網路場景中經常使用RocketMQ進行訊息路由、訂閱釋出、非同步解耦、流量削減峰等操作,來緩解系統的壓力。

image

2 三種常見的訊息中介軟體比較

特性 RabbitMQ RocketMQ Kafka
開發語言 erlang java scala
單機吞吐量 1w+ 10w+ 10w+
時效性 us級 ms級 ms級以內
可用性 高(主從架構) 非常高(分散式架構) 非常高(分散式架構)
訊息可靠性 基本不丟 引數化配置和持久化:基本不丟 引數化配置和持久化:基本不丟
功能特性 基於erlang開發,所以併發能力很強,效能極其好,延時很低;管理介面較豐富 MQ功能比較完備,擴充套件性佳 只支援主要的MQ功能,像一些訊息查詢,訊息回溯等功能沒有提供,畢竟是為大資料準備的,在大資料領域應用廣。
生態 開源、穩定、社群活躍度高 阿里開源,交給Apache,社群活躍度低 Apache開發,開源、高吞吐量、社群活躍度高

技術選型決策參考:
(1)中小型軟體,建議選RabbitMQ, 中小型軟體資料量沒那麼大,選訊息中介軟體,應首選功能比較完備的,所以kafka排除。
不考慮rocketmq的原因是,rocketmq是阿里出品,如果阿里放棄維護rocketmq,中小型公司一般抽不出人來進行rocketmq的定製化開發,因此不推薦。
(2)大型軟體公司,根據具體使用在rocketMq和kafka之間二選一。一方面,大型軟體公司,具備足夠的資金搭建分散式環境,也具備足夠大的資料量。
針對rocketMQ,大型軟體公司也可以抽出人手對rocketMQ進行定製化開發,畢竟有能力改JAVA原始碼的人,還是相當多的。
至於kafka,根據業務場景選擇,如果有日誌採集功能,肯定是首選kafka了。具體該選哪個,看使用場景。引入MQ之後,必然導致系統可用性降低,複雜性增大。

3 訊息中介軟體使用場景

1. 解耦: 比如說系統A會交給系統B去處理一些事情,但是A不想直接跟B有關聯,避免耦合太強,就可以透過在A,B中間加入訊息佇列,A將要任務的事情交給訊息佇列 ,B訂閱訊息佇列來執行任務。

這種場景很常見,比如A是訂單系統,B是庫存系統,可以透過訊息佇列把削減庫存的工作交予B系統去處理。如果A系統同時想讓B、C、D...多個系統處理問題的時候,這種優勢就更加明顯了。

image

2. 有序性: 先進先出原理,先來先處理,比如一個系統處理某件事需要很長一段時間,但是在處理這件事情時候,有其他人也發出了請求,可以把請求放在訊息隊裡,一個一個來處理。

對資料的順序性和一致性有強需求的業務,比如同一張銀行卡同時被多個入口使用,需要保證入賬出賬的順序性,避免出現資料不一致。

image

3. 訊息路由/資料分發: 按照不同的規則,將佇列中訊息傳送到不同的其他佇列中

透過訊息佇列將不同染色的請求傳送到不同的服務去操作。這樣達成了流量按照業務拆分的目的。

image

4、非同步處理: 處理一項任務的時候,有3個步驟A、B、C,需要先完成A操作, 然後做B、C 操作。任務執行成功與否強依賴A的結果,但不依賴B、C 的結果。
如果我們使用序列的執行方式,那處理任務的週期就會變長,系統的整體吞吐能力也會降低(在同一個系統中做非同步其實也是比較大的開銷),所以使用訊息佇列是比較好的辦法。

登入操作就是典型的場景:A:執行登入並得到結果、B:記錄登入日誌、C:將使用者資訊和Token寫入快取。 執行完A就可以從登入頁跳到首頁了,B、C讓服務慢慢去消化,不阻塞當前操作。

image

5、削峰: 將峰值期間的操作削減,比如A同學的整個操作流程包含12個步驟,後續的11個步驟是不需要強關注結果的資料,可以放在訊息佇列中。

4 MQ的概念、結構和原理

4.1 構成說明

image

RocketMQ主要有四大核心組成部分:NameServer、Broker、Producer以及Consumer四部分。這些角色通常以叢集的方式存在,RocketMQ 基於純Java開發,具有高吞吐量、高可用性、適合大規模分散式系統應用的特點。

  • RockerMQ想要啟動,首先需要啟動 NameServer,再啟動 Brober 主機
  • Broker 會向 NameServer 註冊對應的路由和服務
  • Producer會進行路的發現,向NameServer請求Broker路由資訊,進行訊息的傳送
  • Consumer要連通NameServer,獲取到相關的路由資訊,方便我們進行訊息的訂閱
  • Broker 主要負責訊息的儲存,不管是生產訊息還是訂閱訊息,訊息的來源都是 Broker,
  • 訊息的傳送(Producer)只會發到主節點,然後Broker會進行訊息的同步,同步到從節點,作為消費者(Consumer)也只會優先從Master節點,獲取訊息,進行消費
  • Broker主節點不可用或者非常繁忙,會選擇從節點進行消費

1. Producer:
負責生產訊息,一般由業務系統負責。生產者透過呼叫API將訊息傳送到指定的Topic(主題)中。

2. Broker:
訊息儲存中心,負責接收來自Producer的訊息並儲存,同時Consumer也從這裡取得訊息。Broker還儲存與訊息相關的後設資料,包括消費者組、消費進度偏移量、佇列資訊等。每個Broker可以儲存多個Topic的訊息,每個Topic的訊息也可以分片儲存於不同的Broker。在實際部署中,Broker對應一臺伺服器,並分為Master與Slave兩種型別,Master負責讀寫,Slave只負責讀,以此實現資料的備份和負載均衡。

3. Consumer:
負責消費訊息,一般由後臺系統負責。消費者透過訂閱Topic來獲取訊息,並根據業務邏輯進行處理。消費者可以以叢集消費或廣播消費的方式消費訊息。

4. NameServer:
充當路由訊息的提供者,負責儲存Broker的後設資料資訊,並供Producer和Consumer查詢。NameServer被設計成幾乎無狀態的,可以橫向擴充套件,節點之間無通訊。

4.2 基礎概念

image

4.2.1 Group(分組)

在RocketMQ中,Group功能是其核心特性之一。Group主要分為傳送端Group和消費端Group。

  • 傳送端Group:允許將多個傳送者(Producer)組織成一個傳送者組。透過傳送端Group,可以實現對訊息的批次處理和負載均衡,提高系統效能和可靠性。
  • 消費端Group:RocketMQ允許消費者同時訂閱多個主題(Topic),這些消費者可以被組織成一個或多個消費組。消費組內的消費者可以共同分擔訊息的消費任務,實現負載均衡和容錯。當某個消費者出現故障時,其他消費者可以接管其消費任務,確保訊息被及時處理。同時,消費組還支援訊息過濾和順序消費等高階特性。

4.2.2 Topic(主題)

用來區分訊息的種類,表示一類訊息的邏輯名字,訊息的邏輯管理單位,無論生產還是消費訊息,都需要執行Topic。比如一個Topic專門用於使用者訂單訊息傳送,一個Topic專門用於扣減或增加積分的。

  • 一個傳送者可以傳送訊息給一個或者多個Topic
  • 一個訊息接受者可以訂閱一個或多個Topic訊息

4.2.3 Message Queue(訊息佇列)

MessageQueue是RocketMQ中用於儲存和傳輸訊息的資料結構。每個MessageQueue都有一個唯一識別符號,由Topic名稱和佇列編號組成。MessageQueue具有以下特點:

  • 唯一標識:確保每個MessageQueue都可以被唯一地識別和定位。
  • 訊息順序性:對於同一個MessageQueue中的訊息,RocketMQ保證其消費的順序性,即先進先出(FIFO)。
  • 負載均衡:RocketMQ透過動態調整訊息分配策略,將訊息均勻地分佈到所有的MessageQueue中,實現負載均衡,避免某個MessageQueue過載而其他MessageQueue空閒的情況。
  • 高可用性:RocketMQ支援將多個Broker節點組成叢集,每個MessageQueue可以在不同的Broker節點上進行主從複製,提供高可用性和資料冗餘。即使某個Broker節點出現故障,其他節點也可以繼續提供服務,確保訊息的可靠傳輸。

4.2.4 Tag(標籤)

Tag是RocketMQ中用於對訊息進行分類和過濾的標記。生產者可以在傳送訊息時指定Tag,消費者可以根據Tag來過濾和訂閱訊息。Tag的使用可以使得訊息的管理和消費更加靈活和高效。例如,一個電商系統可能會根據商品的類別(如服裝、電子產品等)來設定不同的Tag,消費者可以根據這些Tag來訂閱和處理特定類別的訊息。

4.2.5 Offset(偏移量)

Offset在RocketMQ中用於標識消費者在訊息佇列中的位置。每個消費者都會維護一個Offset,以便知道下一次從哪裡開始消費。Offset的使用可以確保訊息不丟失、避免訊息重複消費以及支援訊息的順序消費。

  • Message queue 是無限長的陣列。一條訊息進來下標就會漲 1,而這個陣列的下標就是 offset。
  • 確保訊息不丟失:透過持久化Offset,即使在消費者當機後重啟,也能從上次消費的位置繼續消費,保證訊息至少被消費一次。
  • 避免訊息重複消費:消費者在成功處理訊息後更新Offset,確保每條訊息只被消費一次。
  • 支援訊息順序消費:對於順序訊息,透過維護Offset可以保證訊息的順序消費。

5 程式實現

5.1 Java中引入和實現RocketMQ

  1. 引入依賴

    在Java專案中,通常透過Maven或Gradle等構建工具來引入RocketMQ的客戶端依賴。以Maven為例,可以在pom.xml檔案中新增以下依賴:

    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-client</artifactId>
        <version>最新版本號(5.3.1)</version>
    </dependency>
    
  2. 實現生產者

    在Java中,透過建立DefaultMQProducer物件來實現訊息的生產者。生產者需要設定NameServer地址,並呼叫start()方法初始化。然後,可以建立Message物件並設定主題、標籤和訊息內容,最後呼叫send()方法傳送訊息。

    
    public class Producer {
        public static void main(String[] args) throws Exception {
            // 建立一個訊息生產者,並設定訊息生產者組
            DefaultMQProducer producer = new DefaultMQProducer("your_producer_group");
            // 指定NameServer地址
            producer.setNamesrvAddr("your_nameserver_address");
            // 初始化Producer
            producer.start();
    
            // 建立訊息物件,並設定主題、標籤和訊息內容
            Message msg = new Message("your_topic", "your_tag", "Hello RocketMQ".getBytes());
            // 傳送訊息並獲取傳送結果
            SendResult sendResult = producer.send(msg);
            System.out.printf("%s%n", sendResult);
    
            // 關閉生產者例項
            producer.shutdown();
        }
    }
    
  3. 實現消費者

    在Java中,透過建立DefaultMQPushConsumer物件來實現訊息的消費者。消費者需要設定NameServer地址和消費組名稱,並呼叫subscribe()方法訂閱主題和標籤。然後,註冊訊息監聽器來處理接收到的訊息。

    
    public class Consumer {
        public static void main(String[] args) throws Exception {
            // 建立一個訊息消費者,並設定訊息消費者組
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("your_consumer_group");
            // 指定NameServer地址
            consumer.setNamesrvAddr("your_nameserver_address");
            // 訂閱指定Topic下的所有訊息
            consumer.subscribe("your_topic", "*");
    
            // 註冊訊息監聽器
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    for (MessageExt msg : msgs) {
                        System.out.printf("接收到訊息: %s%n", new String(msg.getBody()));
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
    
            // 啟動消費者例項
            consumer.start();
        }
    }
    

6 總結

本文只是瞭解下RocketMQ的基本原理和實現,在後面的章節中,我們會帶來RocketMQ的完整解讀。

相關文章