訊息中介軟體之RabbitMQ關鍵知識點總結

xiaosong_2016發表於2020-11-27

目錄

 

一、什麼是RabbitMQ

二、RabbitMQ整體架構

1、Exchange 交換器

三、RabbitMQ資料儲存

四、RabbitMQ安裝

五、RabbitMQ工作流程

1、生產者傳送訊息流程

2、消費者接收訊息的過程

六、RabbitMQ工作模式

1、simple模式(即最簡單的收發模式)

2、Work Queue(work工作模式,資源的競爭)

3、釋出訂閱(publish/fanout資源共享)

4 、路由模式(routing/direct)

5、 主題模式

七、精選面試題

1、RabbitMQ 概念裡的 channel、exchange 和 queue 是什麼?

2、如何確保訊息正確地傳送至 RabbitMQ?

3、如何確保訊息接收方消費了訊息?

4、如何避免訊息重複投遞或重複消費?

5、RabbitMQ 有幾種消費模式?

6、RabbitMQ 如何保證訊息的順序性?


一、什麼是RabbitMQ

      RabbitMQ是一個開源的訊息代理和佇列伺服器,用來通過普通協議在不同的應用之間共享資料(跨平臺跨語言)。RabbitMQ是使用Erlang語言編寫,並且基於AMQP協議實現。

RabbitMQ,俗稱“兔子MQ”(可見其輕巧,敏捷),是目前非常熱門的一款開源訊息中介軟體,不管是網際網路行業還是傳統行業都廣泛使用(最早是為了解決電信行業系統之間的可靠通訊而設計)。

  1. 可靠性(Reliability) RabbitMQ 使用一些機制來保證可靠性,如持久化、傳輸確認、釋出確認。
  2. 靈活的路由(Flexible Routing) 在訊息進入佇列之前,通過 Exchange 來路由訊息的。對於典型的路由功能,RabbitMQ 已經提供了一些內建的 Exchange 來實現。針對更復雜的路由功能,可以將多個 Exchange 繫結在一起,也通過外掛機制實現自己的 Exchange 。
  3. 訊息叢集(Clustering) 多個 RabbitMQ 伺服器可以組成一個叢集,形成一個邏輯 Broker 。
  4. 高可用(Highly Available Queues) 佇列可以在叢集中的機器上進行映象,使得在部分節點出問題的情況下佇列仍然可用。
  5. 多種協議(Multi-protocol) RabbitMQ 支援多種訊息佇列協議,比如 STOMP、MQTT 等等。
  6. 多語言客戶端(Many Clients) RabbitMQ 幾乎支援所有常用語言,比如 Java、.NET、Ruby 等等。
  7. 管理介面(Management UI) RabbitMQ 提供了一個易用的使用者介面,使得使用者可以監控和管理訊息 Broker 的許多方面。
  8. 跟蹤機制(Tracing) 如果訊息異常,RabbitMQ 提供了訊息跟蹤機制,使用者可以找出發生了什麼。
  9. 外掛機制(Plugin System) RabbitMQ 提供了許多外掛,來從多方面進行擴充套件,也可以編寫自己的外掛。

注:AMQP :Advanced Message Queue,高階訊息佇列協議。它是應用層協議的一個開放標準,為面向訊息的中介軟體設計,基於此協議的客戶端與訊息中介軟體可傳遞訊息,並不受產品、開發語言等條件的限制。

RabbitMQ具有很強大的外掛擴充套件能力,官方和社群提供了非常豐富的外掛可供選擇: https://www.rabbitmq.com/community-plugins.html

 

二、RabbitMQ整體架構

1、Exchange 交換器

用來接收生產者傳送的訊息並將這些訊息路由給伺服器中的佇列。

RabbitMQ常用的交換器型別有: fanout direct topic 、 headers 四種。

Fanout

會把所有傳送到該交換器的訊息路由到所有與該交換器繫結的佇列中,如圖:

每個發到 fanout 型別交換器的訊息都會分到所有繫結的佇列上去。fanout 交換器不處理路由鍵,只是簡單的將佇列繫結到交換器上,每個傳送到交換器的訊息都會被轉發到與該交換器繫結的所有佇列上。很像子網廣播,每臺子網內的主機都獲得了一份複製的訊息。fanout 型別轉發訊息是最快的。

Direct

direct型別的交換器路由規則很簡單,它會把訊息路由到那些BindingKey和RoutingKey完全匹配的佇列中,如下圖:

訊息中的路由鍵(routing key)如果和 Binding 中的 binding key 一致, 交換器就將訊息發到對應的佇列中。路由鍵與佇列名完全匹配,如果一個佇列繫結到交換機要求路由鍵為“dog”,則只轉發 routing key 標記為“dog”的訊息,不會轉發“dog.puppy”,也不會轉發“dog.guard”等等。它是完全匹配、單播的模式。

Topic

topic型別的交換器在direct匹配規則上進行了擴充套件,也是將訊息路由到BindingKey和RoutingKey 相匹配的佇列中,這裡的匹配規則稍微不同,它約定:BindingKey和RoutingKey一樣都是由"."分隔的字串;BindingKey中可以存在兩種特殊字元“*”和 “#”,用於模糊匹配,其中"*"用於匹配一個單詞,"#"用於匹配多個單詞(可以是0個)。

topic 交換器通過模式匹配分配訊息的路由鍵屬性,將路由鍵和某個模式進行匹配,此時佇列需要繫結到一個模式上。它將路由鍵和繫結鍵的字串切分成單詞,這些單詞之間用點(".")隔開。它同樣也會識別兩個萬用字元:符號“#”和“*”。#匹配0個或多個單詞,"*"匹配不多不少一個單詞。

Headers

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

三、RabbitMQ資料儲存

儲存機制

   RabbitMQ訊息有兩種型別:

  • 1. 持久化訊息和非持久化訊息。
  • 2. 這兩種訊息都會被寫入磁碟。

持久化訊息在到達佇列時寫入磁碟,同時會記憶體中儲存一份備份,當記憶體吃緊時,訊息從記憶體中清 除。這會提高一定的效能。

非持久化訊息一般只存於記憶體中,當記憶體壓力大時資料刷盤處理,以節省記憶體空間。

RabbitMQ儲存層包含兩個部分:佇列索引和訊息儲存。

四、RabbitMQ安裝

《windows rabbitmq 安裝》

《Linux環境RabbitMQ安裝與常用操作命令》

五、RabbitMQ工作流程

1、生產者傳送訊息流程

  • 1. 生產者連線RabbitMQ,建立TCP連線( Connection),開啟通道(Channel)
  • 2. 生產者宣告一個Exchange(交換器),並設定相關屬性,比如交換器型別、是否持久化等
  • 3. 生產者宣告一個佇列井設定相關屬性,比如是否排他、是否持久化、是否自動刪除等
  • 4. 生產者通過 routingKey (路由Key)將交換器和佇列繫結( binding )起來
  • 5. 生產者傳送訊息至RabbitMQ Broker,其中包含 routingKey (路由鍵)、交換器等資訊
  • 6. 相應的交換器根據接收到的 routingKey 查詢相匹配的佇列。
  • 7. 如果找到,則將從生產者傳送過來的訊息存入相應的佇列中。
  • 8. 如果沒有找到,則根據生產者配置的屬性選擇丟棄還是回退給生產者
  • 9. 關閉通道。
  • 10. 關閉連線。

2、消費者接收訊息的過程

  • 1. 消費者連線到RabbitMQ Broker ,建立一個連線(Connection ) ,開啟一個通道(Channel) 。
  • 2. 消費者向RabbitMQ Broker 請求消費相應佇列中的訊息,可能會設定相應的回撥函式, 以及 做一些準備工作
  • 3. 等待RabbitMQ Broker 回應並投遞相應佇列中的訊息, 消費者接收訊息。
  • 4. 消費者確認( ack) 接收到的訊息。
  • 5. RabbitMQ 從佇列中刪除相應己經被確認的訊息。
  • 6. 關閉通道。
  • 7. 關閉連線。

六、RabbitMQ工作模式

簡單的實現可參考《RabbitMQ的五種工作模式的簡單實現》

1、simple模式(即最簡單的收發模式)

  • 1個生產者將訊息交給預設的交換機(AMQP default)
  • 2 交換機獲取訊息後交給繫結這個生產者的佇列(關係是通過佇列名稱完成)
  • 3 監聽當前佇列的消費者獲取訊息,執行消費邏輯
  • 應用場景:簡訊,聊天

2、Work Queue(work工作模式,資源的競爭)

生產者發訊息,啟動多個消費者例項來消費訊息,每個消費者僅消費部分資訊,可達到負載均衡的效果。

  • 1 生產者將訊息交個交換機
  • 2 交換機交給繫結的佇列
  • 3 佇列由多個消費者同時監聽,只有其中一個能夠獲取這一條訊息,形成了資源的爭搶,誰的資源空閒大,爭搶到的可能越大;
  • 應用場景:搶紅包,大型系統的資源排程

3、釋出訂閱(publish/fanout資源共享)

  • 1 生產者扔給交換機訊息
  • 2 交換機根據自身的型別(fanout)將會把所有訊息複製同步到所有與其繫結的佇列
  • 3 每個佇列可以有一個消費者,接收訊息進行消費邏輯
  • 應用場景:郵件群發,廣告

4 、路由模式(routing/direct)

  • 1 生產者還是將訊息傳送給交換機,訊息攜帶具體的路由key(routingKey)
  • 2 交換機型別direct,將接收到的訊息中的routingKey,比對與之繫結的佇列的routingKey
  • 3 消費者監聽一個佇列,獲取訊息,執行消費邏輯
  • 應用場景:根據生產者的要求傳送給特定的一個或者一批佇列;錯誤的通報;

5、 主題模式

  • 1 生產端傳送訊息,訊息攜帶具體的路由key
  • 2 交換機的型別topic
  • 3 佇列繫結交換機不在使用具體的路由key而是一個範圍值
  • .orange. : haha.orange.haha,haha.haha.orange.haha
  • lazy.# : haha.lazy.haha.haha,layz.alsdhfsh(sh9ou)N0
  • *表示一個字串(不能攜帶特殊符號) 例如 *表示 haha,item,update
  • #表示任意字串

topic主題模式和路由模式區別:

路由模式中的queue繫結攜帶的是具體的key值,路由細化劃分
topic主題模式queue攜帶的是範圍的匹配,某一類的訊息獲取

七、精選面試題

1、RabbitMQ 概念裡的 channel、exchange 和 queue 是什麼?

  • queue 具有自己的 erlang 程式;
  • exchange 內部實現為儲存 binding 關係的查詢表;
  • channel 是實際進行路由工作的實體,即負責按照 routing_key 將 message 投遞給 queue 。

2、如何確保訊息正確地傳送至 RabbitMQ?

RabbitMQ 使用傳送方確認模式,確保訊息正確地傳送到 RabbitMQ。

  • 傳送方確認模式:將通道設定成 confirm 模式(傳送方確認模式),則所有在通道上釋出的訊息都會被指派一個唯一的 ID 。一旦訊息被投遞到目的佇列後,或者訊息被寫入磁碟後(可持久化的訊息),通道會傳送一個確認給生產者(包含訊息唯一ID)。如果 RabbitMQ 發生內部錯誤從而導致訊息丟失,會傳送一條 nack(not acknowledged,未確認)訊息。
  • 傳送方確認模式是非同步的,生產者應用程式在等待確認的同時,可以繼續傳送訊息。當確認訊息到達生產者應用程式,生產者應用程式的回撥方法就會被觸發來處理確認訊息。

3、如何確保訊息接收方消費了訊息?

RabbitMQ 使用接收方訊息確認機制,確保訊息接收方消費了訊息。

  • 接收方訊息確認機制:消費者接收每一條訊息後都必須進行確認(訊息接收和訊息確認是兩個不同操作)。只有消費者確認了訊息,RabbitMQ 才能安全地把訊息從佇列中刪除。

這裡並沒有用到超時機制,RabbitMQ 僅通過 Consumer 的連線中斷來確認是否需要重新傳送訊息。也就是說,只要連線不中斷,RabbitMQ 給了 Consumer 足夠長的時間來處理訊息。

下面羅列幾種特殊情況:

  • 如果消費者接收到訊息,在確認之前斷開了連線或取消訂閱,RabbitMQ 會認為訊息沒有被分發,然後重新分發給下一個訂閱的消費者。(可能存在訊息重複消費的隱患,需要根據 bizId 去重)
  • 如果消費者接收到訊息卻沒有確認訊息,連線也未斷開,則 RabbitMQ 認為該消費者繁忙,將不會給該消費者分發更多的訊息。

4、如何避免訊息重複投遞或重複消費?

在訊息生產時,MQ 內部針對每條生產者傳送的訊息生成一個 inner-msg-id ,作為去重和冪等的依據(訊息投遞失敗並重傳),避免重複的訊息進入佇列

在訊息消費時,要求訊息體中必須要有一個 bizId(對於同一業務全域性唯一,如支付 ID、訂單 ID、帖子 ID 等)作為去重和冪等的依據,避免同一條訊息被重複消費。

5、RabbitMQ 有幾種消費模式?

RabbitMQ 有 pull 和 push 兩種消費模式

6、RabbitMQ 如何保證訊息的順序性?

和 Kafka 與 RocketMQ 不同,Kafka 不存在類似類似 Topic 的概念,而是真正的一條一條佇列,並且每個佇列可以被多個 Consumer 拉取訊息。這個,是非常大的一個差異。

先看看 RabbitMQ 順序錯亂的場景

一個 queue,多個 consumer。比如,生產者向 RabbitMQ 裡傳送了三條資料,順序依次是 data1/data2/data3,壓入的是 RabbitMQ 的一個記憶體佇列。有三個消費者分別從 MQ 中消費這三條資料中的一條,結果消費者 2 先執行完操作,把 data2 存入資料庫,然後是 data1/data3。這不明顯亂了。 也就是說,亂序消費的問題。

解決方案

方案一,拆分多個 queue,每個 queue 一個 consumer,就是多一些 queue 而已,確實是麻煩點。

方案二,或者就一個 queue 但是對應一個 consumer,然後這個 consumer 內部用記憶體佇列做排隊,然後分發給底層不同的 worker 來處理。

實際上,我們會發現上述的兩個方案,前提都是一個 queue 只能啟動一個 consumer 對應

 

參考文章

拉勾教育講義

《訊息佇列之 RabbitMQ》

 

相關文章