RabbitMQ,RocketMQ,Kafka 訊息模型對比分析

Rick.lz發表於2021-12-18

訊息模型

訊息佇列的演進

訊息佇列模型

早起的訊息佇列是按照"佇列"的資料結構來設計的。

生產者(Producer)產生訊息,進行入隊操作,消費者(Consumer)接收訊息,就是出隊操作,存在於服務端的訊息容器就稱為訊息佇列。

mq

當然消費者也可能不止一個,存在的多個消費者是競爭的關係,訊息被其中的一個消費者消費了,其它的消費者就拿不到訊息了。

釋出訂閱模型

如果一個人訊息想要同時被多個消費者消費,那麼上面的佇列模式就不適用了,於是又引出了一種新的模式,釋出訂閱模型。

mq

在釋出-訂閱模型中,訊息的傳送方稱為釋出者(Publisher),訊息的接收方稱為訂閱者(Subscriber),服務端存放訊息的容器稱為主題(Topic)。

釋出者傳送訊息到主題中,然後訂閱者需要先訂閱主題。訂閱主題的訂閱者之後就可以收到傳送者傳送的訊息了。

釋出訂閱也是相容訊息佇列模型的,如果只有一個訂閱者,就是訊息佇列模型了。

RabbitMQ的訊息模型

RabbitMQ 使用的還是訊息佇列這種訊息模型,不過它引入了一個 exchange 的概念。

exchange 也就是交換器,位於生產者和佇列之間,生產者產生的資料是直接傳送到 exchange 中,然後 exchange 根據配置的策略將訊息傳送到對應的佇列中。

mq

RabbitMQ 中通過繫結將交換器和佇列關聯起來,繫結的時候一般會指定一個繫結鍵(BindingKey)。

生產者傳送訊息的時候會指定一個 RoutingKey ,當 RoutingKey 和 BindingKey,一樣的時候就會被髮送的對應的佇列中去。

交換器的型別

RabbitMQ 中腸常用的交換器有 fanout、direct、topic、headers 四種,這裡來一一分析下

direct

direct 會根據傳送訊息的 RoutingKey ,然後傳送到和 RoutingKey 匹配的 BindingKey 對應的佇列中去。

mq

如果傳送訊息的路由鍵也就是 RoutingKey,為 log 的時候,兩個訊息佇列都會收到訊息,如果路由鍵為 debug ,exchange 只會把訊息傳送到訊息佇列1中。

topic

direct 中的 RoutingKey 和 BindingKey 是完全匹配才能傳送訊息,topic 中在此基礎之上做了擴充套件,也就是引入了模糊匹配機制。

  • RoutingKey 和 BindingKey 中使用 . ,來分割字串,被 . 分割開的每一段字串就是一個匹配字元;

  • BindingKey 中主要通過 * 和 # ,用於模糊匹配,* 表示一個單詞,# 代表任意0個或多個單詞;

  • BindingKey 中單獨使用 # 時,會接收所有的訊息,這與型別 fanout一致;

mq

栗子:

1、路由鍵為 test.rabbitmq 訊息佇列1和訊息佇列2都會收到訊息;

2、路由鍵為 rabbitmq 沒有佇列能收到訊息;

3、路由鍵為 test 訊息佇列2會收到訊息;

4、路由鍵為 rr.info.ww 訊息佇列2會收到訊息;

5、路由鍵為 info 沒有佇列能收到訊息;

fanout

該交換器收到的資訊會被髮送到所有與改交換器繫結的佇列中。

headers

headers 型別的交換器不依賴於路由鍵的匹配規則來路由訊息,而是根據傳送的訊息內容中 headers 屬性進行匹配。在繫結佇列和交換器時制定一組鍵值對當傳送訊息到交換器時,RabbitMQ 會獲取到該訊息的 headers (也是一個鍵值對的形式) ,對比其中的鍵值對是否完全匹配佇列和交換器繫結時指定的鍵值對,如果完全匹配則訊息會路由到該佇列,否則不會路由到該佇列 headers 型別的交換器效能會很差,而且也不實用,基本上不會看到它的存在。

Kafka的訊息模型

mq

Kafaka 中引入了一個 broker。broker 接收生產者的資訊,為訊息設定偏移量,並且儲存的磁碟中。broker 為消費者提供服務,對讀取分割槽的請求作出響應,返回已經提交到磁碟上的訊息。

同時 broker 也會對生產者和消費者進行訊息的確認。

生產者傳送訊息到 broker,如果沒有收到 broker 的確認就可以選擇繼續傳送;

消費者同理,在消費端,消費者在收到訊息並完成自己的消費業務邏輯(比如,將資料儲存到資料庫中)後,也會給服務端傳送消費成功的確認,broker 只有收到消費確認後,才認為一條訊息被成功消費,否則它會給消費者重新傳送這條訊息,直到收到對應的消費成功確認。

如果一個主題中,每次只有一個消費例項在處理,同時我們也要保持訊息的有序性,當前訊息沒有被消費掉就不能接著消費下一個訊息。那麼,消費的效能將是極低的,這時候引入了一個分割槽的概念。

主題可以被分為若干個分割槽,一個分割槽就是一個提交日誌。訊息以追加的方式寫入分割槽,然後以先入先出的順序讀取。要注意,由於一個主題一般包含幾個分割槽,因此無法在整個主題範圍內保證訊息的順序,但可以保證訊息在單個分割槽內的順序。

同時引入了消費者組,消費者是消費者組中的一部分,這樣會有一個或者多個消費者讀一個分支,不過群組會保證一個分割槽只能被一個消費者消費,通過多消費者,這樣消費的效能就提高了。

每個消費組都消費主題中一份完整的訊息,不同消費組之間消費進度彼此不受影響,也就是說,一條訊息被Consumer Group1消費過,也會再給Consumer Group2消費。不過同組內是競爭關係,同組內一個訊息只能被同組內的一個訊息消費。

消費者通過偏移量來確認讀過的資料,他是個不斷累加的資料,每次成功消費一個資料這個偏移量就加一。在給定的分割槽中,每個訊息的偏移量都是唯一的。消費者會把每個分割槽讀取的訊息偏移量儲存在 Zookeeper 或 Kafka 上,如果消費者關閉或重啟,它的讀取狀態不會丟失。

RocketMQ的訊息模型

mq

RocketMQ 中的訊息模型和 Kafaka 類似,把 Kafaka 中的分割槽換成佇列,就是 RocketMQ 的訊息模型了。

不過雖然訊息模型類似,但是實現方式還是有很大的差別的。

參考

【訊息佇列高手課】https://time.geekbang.org/column/intro/100032301
【訊息佇列設計精要】https://tech.meituan.com/2016/07/01/mq-design.html
【RabbitMQ實戰指南】https://book.douban.com/subject/27591386/
【Kafka權威指南】https://book.douban.com/subject/27665114/
【RabbitMQ,RocketMQ,Kafka 訊息模型對比分析】https://boilingfrog.github.io/2021/12/18/幾種訊息佇列的訊息模型/

相關文章