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


消息模型

消息隊列的演進

消息隊列模型

早先的消息隊列是按照"隊列"的數據結構來設計的。

生產者(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中。

Default Exchange

Default Exchange 是一種特殊的 Direct Exchange

如果不指定 Exchange ,當你手動創建一個隊列時,后台會自動將這個隊列綁定到一個名稱為空的 Direct Exchange 上,綁定 RoutingKey 與隊列名稱相同。通過使用這個默認的交換器,可以省略掉 RoutingKey 的綁定,直接使用隊列即可,在某些場景可以簡化我們的代碼。

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/幾種消息隊列的消息模型/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM