了解 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 規範。 定義以下四種類型

交換器類型 行為
直接 繫結鍵必須與路由鍵完全匹配 - 沒有萬用字元支援。
主題 與直接相同,但繫結鍵中允許使用萬用字元。 '#' 匹配零個或多個以點分隔的單字,而 '*' 匹配恰好一個這樣的單字。
扇出 路由和繫結鍵被忽略 - 所有發布的訊息都發送到所有繫結的佇列。
標頭

更新 我更正了關於萬用字元的資訊,它們以點分隔的單字或術語為基礎工作。

例如,假設發布者將路由鍵為 "NYSE" 的訊息發送到名為 "Stocks" 的主題交換器。 如果消費者建立一個連接到 "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. 佇列將訊息推送到服務,然後服務執行一些工作並將回應訊息發回交換器,指定與回覆佇列匹配的 routing_key。
  4. 客戶端從回覆佇列中獲取回應訊息。

從客戶端的角度來看,呼叫可以是阻塞的或非阻塞的。 但是,做其中一種的難易程度取決於使用的客戶端函式庫。

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

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

發布/訂閱 (Pub/Sub)

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

rabbit-pub-sub

更好的是,佇列可以透過繫結鍵過濾它們接收的訊息。 如果消費者想要接收所有訊息,那麼它可以指定繫結鍵 "#" - 「匹配任意數量的單字」萬用字元。 如前所述,對於您的普通開發人員來說,非常令人困惑的是,"*" 匹配零個或一個(以點分隔的)單字。

工作分配

想像一下,您有一個應用程式,其中包含許多需要執行的工作。 使用 AMQP,您可以連接多個消費者,以便每個工作都發送給其中一個且僅僅一個消費者。 發布者不在乎哪個消費者執行工作,只關心工作是否完成。 這就是工作分配。

配置它非常簡單,如圖所示

rabbit-work

因此,您有一個佇列繫結到交換器,並且有多個消費者共享該佇列。 無論有多少消費者,此設定都保證只有一個消費者處理給定的訊息。

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

如果您想更深入地研究 AMQP,請查看 規格本身,尤其是關於通用架構的部分。 要開始使用 RabbitMQ,只需訪問 其網站

獲取 Spring 電子報

透過 Spring 電子報保持聯繫

訂閱

搶先一步

VMware 提供培訓和認證,以加速您的進程。

瞭解更多

獲取支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

瞭解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

檢視全部