1、消息中間件
1.1、什么是消息中間件
消息中間件(Message Queue Middleware,簡稱 MQ)是指利用高效可靠的消息傳遞機制進行與平台無關的數據交流,並基於數據通道來進行分布式系統的集成。
1.2、消息中間件的作用
- 解耦:在項目啟動之初來預測將來項目會碰到什么需求,是極其困難的。消息隊列在處理過程中間插入了一個隱含的、基於數據的接口層,兩邊的處理過程都要實現這一接口。這允許你獨立的擴展或修改兩邊的處理過程,只要確保它們遵守同樣的接口約束。
- 冗余(存儲):有時在處理數據的時候處理過程會失敗。除非數據被持久化,否則將永遠丟失。消息隊列把數據進行持久化直到它們已經被完全處理,通過這一方式規避了數據丟失風險。在被許多消息隊列所采用的"插入-獲取-刪除"范式中,在把一個消息從隊列中刪除之前,需要你的處理過程明確的指出該消息已經被處理完畢,確保你的數據被安全的保存直到你使用完畢。
- 擴展性:因為消息中間件解捐了應用的處理過程,所以提高消息入隊和處理的效率是很容 易的,只要另外增加處理過程即可,不需要改變代碼,也不需要調節參數。
- 流量削峰: 在訪問量劇增的情況下,應用仍然需要繼續發揮作用,但是這樣的突發流 量 並不常 見。如果以能處理這類峰值為標准而投入資源,無疑是巨大的浪費 。 使用消息中間件能夠使關 鍵組件支撐突發訪問壓力,不會因為突發的超負荷請求而完全崩慣 。
- 可恢復性: 當系統一部分組件失效時,不會影響到整個系統 。 消息中間件降低了進程間的 稿合度,所以即使一個處理消息的進程掛掉,加入消息中間件中的消息仍然可以在系統恢復后 進行處理 。
- 順序保證: 在大多數使用場景下,數據處理的順序很重要,大部分消息中間件支持一定程 度上的順序性。
- 緩沖: 在任何重要的系統中,都會存在需要不同處理時間的元素。消息中間件通過 一個緩 沖層來幫助任務最高效率地執行,寫入消息中間件的處理會盡可能快速 。 該緩沖層有助於控制 和優化數據流經過系統的速度。
- 異步通信: 在很多時候應用不想也不需要立即處理消息 。消息中間件提供了異步處理機制, 允許應用把一些消息放入消息中間件中,但並不立即處理它,在之后需要的時候再慢慢處理 。
1.3、消息中間件的缺點
- 系統可用性降低: 系統中加入了MQ,如果MQ掛了,整套系統奔潰了
- 系統復雜度提高:引入MQ會產生一些問題,比如怎么保證消息沒有重復消費,怎么處理消息丟失的情況等
2、RabbitMQ 簡介
RabbitMQ 是采用 Erlang 語言實現 AMQP (Advanced Message Queuing Protocol,高級消息 隊列協議)的消息中間件。
2.1、RabbitMQ 特點
- 可靠性:RabbitMQ 使用一些機制來保證可靠性,如持久性、傳輸確認及發布確認等
- 靈活的路由: 在消息進入隊列之前,通過交換器來路由消息。對典型的路由功能,RabbitMQ已經提供了一些內置的交換器來實現。針對更復雜的路由功能,可以將多個交換器綁定在一起,也可以通過插件機制來實現自己的交換器
- 擴展性: 多個 RabbitMQ 節點可以組成一個集群,也可以根據實際業務情況動態地擴展集群中的節點
- 插件機制: RabbitMQ 提供了許多插件,以實現從多方面進行擴展,當然也可以編寫自己的插件
- 高可用性: 隊列可以在集群中的機器上設置鏡像,使得在部分節點出現問題的情況下隊列任然可用
- 多種協議: RabbitMQ 除了原生支持AMQP協議,還支持STOMP、MQTT等多種消息中間件協議
- 多語言客戶端: RabbitMQ 幾乎支持所用常用語言,比如Java、Python、Ruby、PHP等
- 管理界面: RabbitMQ 提供了一個易用的用戶界面,使得用戶可以監控和管理消息、集群中的節點等
2.2、相關概念
RabbitMQ 整體上是一個生產者與消費者模型,主要負責接收、存儲和轉發消息。可以把消 息傳遞的過程想象成:當你將一個包裹送到郵局,郵局會暫存並最終將郵件通過郵遞員送到收 件人的手上, RabbitMQ 就好比由郵局、郵箱和郵遞員組成的一個系統。從計算機術語層面來說, RabbitMQ 模型更像是一種交換機模型。 RabbitMQ 的整體模型架構如圖所示:

- Producer: 生產者,就是投遞消息的 一方。
- Consumer: 消費者,就是接收消息的 一方。
- Broker: 消息中間件的服務節點。如圖展示了生產者將消息存入 RabbitMQ Broker,以及消費者從 Broker中消費數據的整個流程

- Queue: 隊列,是 RabbitMQ 的內部對象,用於存儲消息。
- Exchange: 交換器。生產者將消息發送到 Exchange,由交換器將消息路由到一個或者多個隊列中。如果路由不到,或許會返回給生產者,或許直接丟棄。 交換器的具體示意圖如圖所示

- RoutingKey: 路由鍵 。生產者將消息發給交換器 的時候, 一般會 指定 一個 RoutingKey,用 來指定這個消息的路由規則,而這個 RoutingKey 需要與交換器類型和綁定鍵 (BindingKey) 聯 合使用才能最終生效。
- Binding: 綁定 。 RabbitMQ 中通過綁定將交換器與隊列關聯起來,在綁定的時候一般會指定一個綁定鍵 (BindingKey),這樣 RabbitMQ 就知道如何正確地將消息路由到隊列了,如圖所

2.3、交換器類型
RabbitMQ 常用的交換器類型有 fanout、 direct、 topic、 headers 這四種 。
- fanout: 它會把所有發送到該交換器的消息路由到所有與該交換器綁定的隊列中。
- direct: 它會把消息路由到那些 BindingKey和 RoutingKey 完全匹配的隊列中。
- topic: topic類型的交換器在匹配規則上進行了擴展,它與 direct類型的交換器相似,但是它支持模糊匹配,它有如下約定:
- RoutingKey 為一個點號" "分隔的字符串,比如“com.rabbitmq.client”, "java.util.concurrent", "com.hidden.client"
- BindingKey 和 RoutingKey 一樣也是點號" "分隔的字符串;
- BindingKey 中可以存在兩種特殊 字符串""和"#",用於做模糊匹配,其中""用於匹配一個單詞,"#"用於匹配多規格單詞(可以是零個)。

-
- 如圖
- 路由鍵為" com.rabbitmq.client" 的消息會同時路由到 Queuel 和 Queue2;
- 路由鍵為" com.hidden.client" 的消息只會路由到 Queue2 中:
- 路由鍵為" com.hidden.demo" 的消息只會路由到 Queue2 中:
- 路由鍵為 "java.rabbitmq.demo" 的消息只會路由到 Queuel 中:
- 路由鍵為" java.util.concurrent" 的消息將會被丟棄或者返回給生產者(需要設置 mandatory 參數) ,因為它沒有匹配任何路由鍵。
- 如圖
- headers: headers 類型的交換器不依賴於路由鍵的匹配規則來路由消息,而是根據發送的消息內容中 的 headers 屬性進行匹配。在綁定隊列和交換器時制定一組鍵值對 , 當發送消息到交換器時, RabbitMQ 會獲取到該消息的 headers (也是一個鍵值對的形式) ,對比其中的鍵值對 是否完全 匹配隊列和交換器綁定時指定的鍵值對,如果完全匹配則消息會路由到該隊列,否則不會路由 到該隊列 。 headers 類型的交換器性能會很差,而且也不實用,基本上不會看到它的存在。
