RabbitMQ入門知識整理

微信公衆號:跟着老萬學java
關注可瞭解更多的開發技巧。問題或建議,請公衆號留言;

什麼叫消息隊列

消息(Message)是指在應用間傳送的數據。消息可以非常簡單,比如只包含文本字符串,也可以更復雜,可能包含嵌入對象。
消息隊列(Message Queue)是一種應用間的通信方式,消息發送後可以立即返回,由消息系統來確保消息的可靠傳遞。消息發佈者只管把消息發佈到 MQ 中而不用管誰來取,消息使用者只管從 MQ 中取消息而不管是誰發佈的。這樣發佈者和使用者都不用知道對方的存在,解除了上下游調用的依賴關係,實現異步和解耦。

消息隊列的作用

1、異步
2、解耦
3、削峯
4、廣播訂閱
5、RPC調用
6、順序消費
7、分佈式事務支持

RabbitMQ說明

RabbitMQ的官網是http://www.rabbitmq.com

RabbitMQ 是一個由 Erlang 語言開發的 AMQP 的開源實現。
AMQP :Advanced Message Queue,高級消息隊列協議。它是應用層協議的一個開放標準,爲面向消息的中間件設計,基於此協議的客戶端與消息中間件可傳遞消息,並不受產品、開發語言等條件的限制。

RabbitMQ 即一個消息隊列,主要是用來實現應用程序的異步和解耦,同時也能起到消息緩衝,消息分發的作用。

中間件最標準的用法是生產者生產消息傳送到隊列,消費者從隊列中拿取消息並處理,生產者不用關心是誰來消費,消費者不用關心誰在生產消息,從而達到解耦的目的。在分佈式的系統中,消息隊列也會被用在很多其它的方面,比如:分佈式事務的支持,RPC的調用等等。

RabbitMQ 特點

RabbitMQ 最初起源於金融系統,用於在分佈式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。具體特點包括:
可靠性(Reliability)
RabbitMQ 使用一些機制來保證可靠性,如持久化、傳輸確認、發佈確認。
靈活的路由(Flexible Routing)
在消息進入隊列之前,通過 Exchange 來路由消息的。對於典型的路由功能,RabbitMQ 已經提供了一些內置的 Exchange 來實現。針對更復雜的路由功能,可以將多個 Exchange 綁定在一起,也通過插件機制實現自己的 Exchange 。
消息集羣(Clustering)
多個 RabbitMQ 服務器可以組成一個集羣,形成一個邏輯 Broker 。
雖然支持集羣,但是並不支持消息分片,可通過插件將消息通過hash計算分發到多個隊列。而kafka是通過對單個topic進行分區來實現的。
高可用(Highly Available Queues)
隊列可以在集羣中的機器上進行鏡像,使得在部分節點出問題的情況下隊列仍然可用。
多種協議(Multi-protocol)
RabbitMQ 支持多種消息隊列協議,比如 STOMP、MQTT 等等。
多語言客戶端(Many Clients)
RabbitMQ 幾乎支持所有常用語言,比如 Java、.NET、Ruby 等等。
管理界面(Management UI)
RabbitMQ 提供了一個易用的用戶界面,使得用戶可以監控和管理消息 Broker 的許多方面。
跟蹤機制(Tracing)
如果消息異常,RabbitMQ 提供了消息跟蹤機制,使用者可以找出發生了什麼。
插件機制(Plugin System)
RabbitMQ 提供了許多插件,來從多方面進行擴展,也可以編寫自己的插件。

RabbitMQ 基本概念

上面只是最簡單抽象的描述,具體到 RabbitMQ 則有更詳細的概念需要解釋。上面介紹過 RabbitMQ 是 AMQP 協議的一個開源實現,所以其內部實際上也是 AMQP 中的基本概念:

 


通常我們談到隊列服務, 會有三個概念: 發消息者、隊列、收消息者,RabbitMQ 在這個基本概念之上, 多做了一層抽象, 在發消息者和 隊列之間, 加入了交換器 (Exchange). 這樣發消息者和隊列就沒有直接聯繫, 轉而變成發消息者把消息給交換器, 交換器根據調度策略再把消息發給隊列。

 

Message
消息,由消息頭和消息體組成。消息體是不透明的,而消息頭則由一系列的可選屬性組成,這些屬性包括routing-key(路由鍵)、priority(相對於其他消息的優先權)、delivery-mode(指出該消息可能需要持久性存儲)等。

Publisher
消息的生產者,也是一個向交換器發佈消息的客戶端應用程序。

Exchange
交換器,用來接收生產者發送的消息並將這些消息路由給服務器中的隊列。

Binding
綁定,用於消息隊列和交換器之間的關聯。一個綁定就是基於路由鍵將交換器和消息隊列連接起來的路由規則,所以可以將交換器理解成一個由綁定構成的路由表。

Queue
消息隊列,用來保存消息直到發送給消費者。它是消息的容器,也是消息的終點。一個消息可投入一個或多個隊列。消息一直在隊列裏面,等待消費者連接到這個隊列將其取走。

Connection
網絡連接,比如一個TCP連接。

Channel
信道,多路複用連接中的一條獨立的雙向數據流通道。信道是建立在真實的TCP連接內地虛擬連接,AMQP 命令都是通過信道發出去的,不管是發佈消息、訂閱隊列還是接收消息,這些動作都是通過信道完成。因爲對於操作系統來說建立和銷燬 TCP 都是非常昂貴的開銷,所以引入了信道的概念,以複用一條 TCP 連接。

Consumer
消息的消費者,表示一個從消息隊列中取得消息的客戶端應用程序。

Virtual Host
虛擬主機,表示一批交換器、消息隊列和相關對象。虛擬主機是共享相同的身份認證和加密環境的獨立服務器域。每個 vhost 本質上就是一個 mini 版的 RabbitMQ 服務器,擁有自己的隊列、交換器、綁定和權限機制。vhost 是 AMQP 概念的基礎,必須在連接時指定,RabbitMQ 默認的 vhost 是 / 。

爲什麼需要多個虛擬主機呢?
很簡單,RabbitMQ當中,用戶只能在虛擬主機的粒度進行權限控制。 因此,如果需要禁止A組訪問B組的交換機/隊列/綁定,必須爲A和B分別創建一個虛擬主機。
簡單的來說,就是一個分組的概念。每個用戶操作自己權限下的虛擬主機,相互之間互不影響。

Broker
表示消息隊列服務器實體。

重點說明一下

  1. RabbitMQ的發送消息不是面向隊列的,而是面向交換器Exchange,交換器根據接收消息時指定的路由鍵Routing Key和交換器自身的類型以及和隊列之間建立綁定關係使用的Binding Key來決定消息發送到哪些具體的隊列中。
  2. 注意區分Routing Key和Binding Key的作用和使用場景的區別,Routing Key是生產者發送消息時指定的,用來確定路由規則;Binding Key是交換器和隊列綁定時用到的。
    3、一條消息具體發送到什麼隊列,是由Routing Key,交換器Exchange,Binding Key共同決定的。

核心流程

生產者發送消息流程

  1. 生產者連接到RabbitMQ Broker,建立一個連接(Connection),開啓一個信道(Channel)
  2. 生產者聲明一個交換器,並設置相關屬性,比如交換器類型,是否持久化等
  3. 生產者聲明一個隊列並設置相關屬性,比如是否排他,是否持久化,是否自動刪除等。
  4. 生產者通過路由鍵將交換器和隊列綁定起來
  5. 生產者發送消息至RabbitMQ Broker,其中包含路由鍵,交換器等信息。
  6. 相應的交換器根據接收到的路由鍵查找相匹配的隊列。
  7. 如果找到,則將從生產者發送過來的消息存入相應的隊列中。
  8. 如果沒有找到,則根據生產者配置的屬性選擇丟棄還是回退給生產者
  9. 關閉信道。
  10. 關閉連接。

簡單代碼

 

消費者消費消息流程

  1. 消費者連接到RabbitMQ Broker,建立一個連接(Connection),開啓一個信道(Channel)。
  2. 消費者向RabbitMQ Broker請求消費相應隊列中的消息,可能會設置相應的回調函數,以及做一些準備工作。
  3. 等待RabbitMQ Broker迴應並投入相應隊列中的消息,消費者接收消息。
  4. 消費者確認(ack)接收到消息
  5. RabbitMQ從隊列中刪除相應已經被確認的消息
  6. 關閉信道。
  7. 關閉連接

簡單代碼

 

四種路由

Exchange分發消息時根據類型的不同分發策略有區別,目前常用的四種類型:
direct、fanout、topic、headers 。
headers 匹配 AMQP 消息的 header 而不是路由鍵,此外 headers 交換器和 direct 交換器完全一致,但性能差很多,目前幾乎用不到了,所以直接看另外三種類型:

direct
direct類型的Exchange路由規則也很簡單,它會把消息路由到那些binding key與routing key完全匹配的Queue中。

 


以上圖的配置爲例,我們以routingKey=”error”發送消息到Exchange,則消息會路由到Queue1(amqp.gen-S9b…,這是由RabbitMQ自動生成的Queue名稱)和Queue2(amqp.gen-Agl…);如果我們以routingKey=”info”或routingKey=”warning”來發送消息,則消息只會路由到Queue2。如果我們以其他routingKey發送消息,則消息不會路由到這兩個Queue中。

 

topic
前面講到direct類型的Exchange路由規則是完全匹配binding key與routing key,但這種嚴格的匹配方式在很多情況下不能滿足實際業務需求。topic類型的Exchange在匹配規則上進行了擴展,它與direct類型的Exchage相似,也是將消息路由到binding key與routing key相匹配的Queue中,但這裏的匹配規則有些不同,它約定:
routing key爲一個句點號“.”分隔的字符串(我們將被句點號“. ”分隔開的每一段獨立的字符串稱爲一個單詞),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit” binding key與routing key一樣也是句點號“. ”分隔的字符串
binding key中可以存在兩種特殊字符“*”與“#”,用於做模糊匹配,其中“*”用於匹配一個單詞,“#”用於匹配多個單詞(可以是零個)

 

以上圖中的配置爲例,routingKey=”quick.orange.rabbit”的消息會同時路由到Q1與Q2,routingKey=”lazy.orange.fox”的消息會路由到Q1,routingKey=”lazy.brown.fox”的消息會路由到Q2,routingKey=”lazy.pink.rabbit”的消息會路由到Q2(只會投遞給Q2一次,雖然這個routingKey與Q2的兩個bindingKey都匹配);routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit”的消息將會被丟棄,因爲它們沒有匹配任何bindingKey

fanout
fanout類型的Exchange路由規則非常簡單,它會把所有發送到該Exchange的消息路由到所有與它綁定的Queue中。

 


上圖中,生產者(P)發送到Exchange(X)的所有消息都會路由到圖中的兩個Queue,並最終被兩個消費者(C1與C2)消費。
fanout路由下會忽略binding key與routing key的匹配關係。

 

headers
headers類型的Exchange不依賴於routing key與binding key的匹配規則來路由消息,而是根據發送的消息內容中的headers屬性進行匹配。
在綁定Queue與Exchange時指定一組鍵值對;當消息發送到Exchange時,RabbitMQ會取到該消息的headers(也是一個鍵值對的形式),對比其中的鍵值對是否完全匹配Queue與Exchange綁定時指定的鍵值對;如果完全匹配則消息會路由到該Queue,否則不會路由到該Queue。
該類型的Exchange沒有用到過(不過也應該很有用武之地),所以不做介紹。

RPC調用

MQ本身是基於異步的消息處理,前面的示例中所有的生產者(P)將消息發送到RabbitMQ後不會知道消費者(C)處理成功或者失敗(甚至連有沒有消費者來處理這條消息都不知道)。
但實際的應用場景中,我們很可能需要一些同步處理,需要同步等待服務端將我的消息處理完成後再進行下一步處理。這相當於RPC(Remote Procedure Call,遠程過程調用)。
在RabbitMQ中也支持RPC。

 


RabbitMQ中實現RPC的機制是:
客戶端發送請求(消息)時,在消息的屬性(MessageProperties,在AMQP協議中定義了14中properties,這些屬性會隨着消息一起發送)中設置兩個值replyTo(一個Queue名稱,用於告訴服務器處理完成後將通知我的消息發送到這個Queue中)和correlationId(此次請求的標識號,服務器處理完成後需要將此屬性返還,客戶端將根據這個id瞭解哪條請求被成功執行了或執行失敗)。
服務器端收到消息並處理服務器端處理完消息後,將生成一條應答消息到replyTo指定的Queue,同時帶上correlationId屬性客戶端之前已訂閱replyTo指定的Queue,從中收到服務器的應答消息後,根據其中的correlationId屬性分析哪條請求被執行了,根據執行結果進行後續業務處理。

 

總結

1、主要介紹消息隊列的概念
2、消息隊列的作用和使用場景
3、rabbitmq的特點
4、rabbitmq基本概念和內部結構
5、rabbitmq經典的四種路由機制
6、rabbitmq的RPC調用實現原理

  更多精彩,歡迎關注。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章