瞭解AMQP,RabbitMQ使用的協議

工程 | Peter Ledbrook | 2010年6月14日 | ...

更新 我修改了第一段,以澄清RabbitMQ與JMS之間的關係。

RabbitMQ是一個輕量級、可靠、可擴充套件且可移植的訊息代理。但與許多Java開發人員熟悉的的訊息代理不同,它不基於JMS。取而代之的是,您的應用程式透過一種平臺中立的、線級別的協議(高階訊息佇列協議,AMQP)與其進行通訊。幸運的是,已經有一個Java客戶端庫,並且SpringSource正在致力於一流的Spring和Grails整合,所以不必擔心使用RabbitMQ時需要進行低階操作。您甚至可以找到提供JMS介面的AMQP客戶端庫。但AMQP在操作上與JMS有足夠大的差異,這可能會讓習慣於JMS模型的Java開發人員感到困惑。

為了緩解過渡的痛苦,本文將探討AMQP的基礎概念以及三個常見的用例。到那時,您應該能充分理解如何配置RabbitMQ並透過Spring和Grails提供的API使用它。

交換器、佇列和繫結

像任何訊息系統一樣,AMQP是一個涉及釋出者和消費者的訊息協議。釋出者產生訊息,消費者接收並處理它們。訊息代理(如RabbitMQ)的職責是確保來自發布者的訊息能夠傳送到正確的消費者。為此,代理使用了兩個關鍵元件:交換器和佇列。下圖展示了它們如何連線釋出者和消費者。

rabbit-basics

正如您所見,設定非常簡單。釋出者將訊息傳送到命名交換器,而消費者從佇列中拉取訊息(或者根據配置,佇列將訊息推送到消費者)。當然,首先需要建立連線,那麼釋出者和消費者如何互相發現呢?透過交換器的名稱。通常,釋出者或消費者會建立一個具有給定名稱的交換器,然後公開該名稱。如何公開取決於具體情況,例如可以將其包含在公共API文件中,或者將其傳送給已知的客戶端。

訊息如何從交換器路由到佇列?這是一個好問題。首先,佇列必須附加到給定的交換器。通常,消費者會建立佇列並同時將其附加到交換器。其次,交換器接收到的訊息必須與佇列進行匹配,這個過程稱為“繫結”。

為了理解繫結,瞭解AMQP訊息的結構很有幫助。

rabbit-message

訊息的頭和屬性本質上是鍵值對。它們之間的區別在於,頭由AMQP規範定義,而屬性可以包含任意的、特定於應用程式的資訊。實際的訊息內容只是一個位元組序列,所以如果您想在訊息中傳遞文字,應該統一使用一種編碼。UTF-8是一個不錯的選擇。如果您願意,可以在訊息頭中指定內容型別和編碼,但這似乎並不特別常見。

這與繫結有什麼關係?一個標準頭稱為routing-key,代理就是利用它來匹配訊息到佇列。每個佇列都指定一個“繫結鍵”,如果該鍵與routing-key頭的值匹配,則該佇列將接收到訊息。

交換器型別的概念使情況稍微複雜化。AMQP規範定義了以下四種類型:

交換器型別 行為
Direct 繫結鍵必須與路由鍵完全匹配,不支援萬用字元。
Topic 與Direct相同,但允許在繫結鍵中使用萬用字元。'#'匹配零個或多個點分隔的單詞,'*'匹配恰好一個這樣的單詞。
Fanout 路由鍵和繫結鍵被忽略——所有釋出的訊息都會發送到所有繫結的佇列。
Headers

更新 我修正了關於萬用字元的資訊,它們基於點分隔的單詞或術語。

例如,假設一個釋出者向名為“Stocks”的主題交換器傳送一個路由鍵為“NYSE”的訊息。如果一個消費者建立了一個附加到“Stocks”的佇列,並且其繫結鍵為“#”、“*”或“NYSE”,那麼該消費者將收到該訊息,因為所有這三個繫結鍵都匹配“NYSE”。然而,如果訊息釋出到一個直連交換器,並且繫結鍵是“#”或“*”,那麼消費者將不會收到訊息,因為這些字元被視為字面量,而不是萬用字元。有趣的是,“#.#”也會匹配“NYSE”,儘管路由鍵中沒有點。

現在考慮一個路由鍵為“NYSE.TECH.MSFT”的訊息。給定訊息將傳送到一個主題交換器,哪些繫結鍵會匹配它?

繫結鍵 匹配?
NYSE.TECH.MSFT
#
NYSE.#
*.*
NYSE.*
NYSE.TECH.*
NYSE.*.MSFT

基本上就這麼簡單。透過支援每個佇列的多個消費者和每個交換器的多個佇列來提供靈活性。事實上,一個佇列甚至可以繫結到多個交換器。現在讓我們看看其中的一些場景。

RPC

AMQP代理可以充當客戶端和服務之間的RPC機制。一般的設定如下,使用直連交換器。

rabbit-rpc

一般的流程如下:

  1. 客戶端向佇列傳送訊息,指定:(a)與服務匹配的路由鍵;以及(b)用於接收響應的佇列名稱。
  2. 交換器將訊息傳遞給服務的佇列(在本例中為“ops_q”)。
  3. 佇列將訊息推送到服務,服務執行一些工作,然後傳送響應訊息回交換器,並指定一個匹配回覆佇列的路由鍵。
  4. 客戶端從回覆佇列中獲取響應訊息。

從客戶端的角度來看,呼叫可以是阻塞的或非阻塞的。但是,實現哪種方式的容易程度取決於所使用的客戶端庫。

RPC場景的關鍵是確保客戶端和服務使用相同的交換器進行初始請求,並且客戶端知道要為路由鍵指定什麼。

至於回覆佇列,它通常由客戶端建立,然後客戶端會適當地填充reply_to頭。此外,雖然您可以使用與請求不同的交換器來處理回覆,但更常見的是為請求和回覆使用同一個交換器。

釋出/訂閱

JMS具有主題佇列的概念,可以確保釋出者的訊息傳送給所有訂閱者。在AMQP中,您可以透過將多個佇列繫結到一個交換器來輕鬆實現相同的行為,如下所示:

rabbit-pub-sub

更妙的是,佇列可以透過繫結鍵過濾它們接收的訊息。如果一個消費者想要接收所有訊息,它可以指定繫結鍵為“#”——正如前面提到的“匹配任意數量的單詞”萬用字元。對於普通開發人員來說,有點令人困惑的是,“*”匹配零個或一個(點分隔的)單詞。

工作分配

想象一下您的應用程式有一堆需要執行的任務。透過AMQP,您可以連線多個消費者,使每個任務僅傳送給其中一個消費者。釋出者不在乎哪個消費者完成了工作,只在乎工作是否完成。這就是工作分配。

配置起來非常簡單,如本圖所示:

rabbit-work

所以您有一個佇列繫結到交換器,有多個消費者共享這個佇列。這種設定可以保證即使有多個消費者,也只有一個消費者會處理給定的訊息。

以上是AMQP代理的三個主要使用模式。儘管我分別描述了它們,但將它們組合起來的情況也很常見。例如,在RPC模式中,您可以讓多個服務共享同一個佇列(工作分配)。最終由您來決定如何配置交換器和佇列,現在您應該已經有了足夠的理解,可以為您的具體情況找出合適的設定。

如果您想深入瞭解AMQP,請檢視規範本身,特別是“General Architecture”(通用架構)部分。要開始使用RabbitMQ,只需訪問其網站

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有