訊息模型
訊息佇列的演進
訊息佇列模型
早先的訊息佇列是按照"佇列"的資料結構來設計的。
生產者(Producer)產生訊息,進行入隊操作,消費者(Consumer)接收訊息,就是出隊操作,存在於服務端的訊息容器就稱為訊息佇列。
當然消費者也可能不止一個,存在的多個消費者是競爭的關係,訊息被其中的一個消費者消費了,其它的消費者就拿不到訊息了。
釋出訂閱模型
如果一個人訊息想要同時被多個消費者消費,那麼上面的佇列模式就不適用了,於是又引出了一種新的模式,釋出訂閱模型。
在釋出-訂閱模型中,訊息的傳送方稱為釋出者(Publisher),訊息的接收方稱為訂閱者(Subscriber),服務端存放訊息的容器稱為主題(Topic)。
釋出者傳送訊息到主題中,然後訂閱者需要先訂閱主題。訂閱主題的訂閱者之後就可以收到傳送者傳送的訊息了。
釋出訂閱也是相容訊息佇列模型的,如果只有一個訂閱者,就是訊息佇列模型了。
RabbitMQ的訊息模型
RabbitMQ 使用的還是訊息佇列這種訊息模型,不過它引入了一個 exchange 的概念。
exchange 也就是交換器,位於生產者和佇列之間,生產者產生的資料是直接傳送到 exchange 中,然後 exchange 根據配置的策略將訊息傳送到對應的佇列中。
RabbitMQ 中通過繫結將交換器和佇列關聯起來,繫結的時候一般會指定一個繫結鍵(BindingKey)。
生產者傳送訊息的時候會指定一個 RoutingKey ,當 RoutingKey 和 BindingKey,一樣的時候就會被髮送的對應的佇列中去。
交換器的型別
RabbitMQ 中腸常用的交換器有 fanout、direct、topic、headers
四種,這裡來一一分析下
direct
direct 會根據傳送訊息的 RoutingKey ,然後傳送到和 RoutingKey 匹配的 BindingKey 對應的佇列中去。
如果傳送訊息的路由鍵也就是 RoutingKey,為 log 的時候,兩個訊息佇列都會收到訊息,如果路由鍵為 debug ,exchange 只會把訊息傳送到訊息佇列1中。
topic
direct 中的 RoutingKey 和 BindingKey 是完全匹配才能傳送訊息,topic 中在此基礎之上做了擴充套件,也就是引入了模糊匹配機制。
-
RoutingKey 和 BindingKey 中使用 . ,來分割字串,被 . 分割開的每一段字串就是一個匹配字元;
-
BindingKey 中主要通過 * 和 # ,用於模糊匹配,* 表示一個單詞,# 代表任意0個或多個單詞;
-
BindingKey 中單獨使用 # 時,會接收所有的訊息,這與型別 fanout一致;
栗子:
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的訊息模型
Kafaka 中引入了一個 broker。broker 接收生產者的資訊,為訊息設定偏移量,並且儲存的磁碟中。broker 為消費者提供服務,對讀取分割槽的請求作出響應,返回已經提交到磁碟上的訊息。
同時 broker 也會對生產者和消費者進行訊息的確認。
生產者傳送訊息到 broker,如果沒有收到 broker 的確認就可以選擇繼續傳送;
消費者同理,在消費端,消費者在收到訊息並完成自己的消費業務邏輯(比如,將資料儲存到資料庫中)後,也會給服務端傳送消費成功的確認,broker 只有收到消費確認後,才認為一條訊息被成功消費,否則它會給消費者重新傳送這條訊息,直到收到對應的消費成功確認。
如果一個主題中,每次只有一個消費例項在處理,同時我們也要保持訊息的有序性,當前訊息沒有被消費掉就不能接著消費下一個訊息。那麼,消費的效能將是極低的,這時候引入了一個分割槽的概念。
主題可以被分為若干個分割槽,一個分割槽就是一個提交日誌。訊息以追加的方式寫入分割槽,然後以先入先出的順序讀取。要注意,由於一個主題一般包含幾個分割槽,因此無法在整個主題範圍內保證訊息的順序,但可以保證訊息在單個分割槽內的順序。
同時引入了消費者組,消費者是消費者組中的一部分,這樣會有一個或者多個消費者讀一個分支,不過群組會保證一個分割槽只能被一個消費者消費,通過多消費者,這樣消費的效能就提高了。
每個消費組都消費主題中一份完整的訊息,不同消費組之間消費進度彼此不受影響,也就是說,一條訊息被Consumer Group1
消費過,也會再給Consumer Group2
消費。不過同組內是競爭關係,同組內一個訊息只能被同組內的一個訊息消費。
消費者通過偏移量來確認讀過的資料,他是個不斷累加的資料,每次成功消費一個資料這個偏移量就加一。在給定的分割槽中,每個訊息的偏移量都是唯一的。消費者會把每個分割槽讀取的訊息偏移量儲存在 Zookeeper 或 Kafka 上,如果消費者關閉或重啟,它的讀取狀態不會丟失。
RocketMQ的訊息模型
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/幾種訊息佇列的訊息模型/