AMQP 協議詳解
一、AMQP 歷史
訊息佇列(Message Queue)起源於一位來自 MIT 的硬體設計教育工作者 Vivek Ranadivé 設想了一種通用軟體匯流排,就像主機板上的匯流排那樣,供其他應用程式接入。Vivek在1983年成立了 Teknekron,高盛等公司作為第一批使用者再金融交易中採用了 Teknekron的軟體,同時還誕生了第一代訊息佇列軟體:Teknekron 的 The Information Bus(TIB)。
Teknekron 的 TIB 允許應用開發者建立一系列規則去描述訊息內容,只要訊息按照這些規則釋出出去,任何消費者應用都可以訂閱感興趣的內容,資訊的生產者和消費者完全解耦,並且可以再傳輸過程中靈活混合。這個特性引起了電信特別是新聞機構的注意。1994年路透社收購了 Teknekron 。
由於訊息佇列再金融交易中應用的反響,BIM 在1990年也開始研發自己的訊息佇列軟體(BIM MQ),並且逐步演化成 WebSphere MQ 並統治著商業訊息佇列平臺市場。同時微軟開發了Microsoft Message Queue(MSMQ)。然而這些商業MQ問題在供應商壁壘,各個廠商的 MQ 之間無法互通。為了解決這個問題,Java Message Service(JMS)在2001年誕生了,試圖通過提供公共 Java API的方式隱藏MQ各個供應商提供的實際介面,從而跨越壁壘和解決互通問題,但是由於使用單獨的標準化介面來膠合眾多不同的介面使應用程式反而變得更加脆弱。
2004年 JPMorgan Chase 和 iMatix 公司一起合作開發 Advanced Message Queuing Protocol (AMQP,高階訊息佇列協議),從一開始就設計成為開放標準,任何人都可以執行這一標準,針對該標準任何人都可以和任何 AMQP 供應商提供的 MQ 伺服器進行互動。
二、AMQP 協議
AMQP,即Advanced Message Queuing Protocol,一個提供統一訊息服務的應用層標準高階訊息佇列協議,是應用層協議的一個開放標準,為面向訊息的中介軟體設計。基於此協議的客戶端與訊息中介軟體可傳遞訊息,並不受客戶端/中介軟體同產品,不同的開發語言等條件的限制。
AMQP協議這種降低耦合的機制是基於與上層產品,語言無關的協議。是一種二進位制協議,提供客戶端應用與訊息中介軟體之間多通道、協商、非同步、安全、中立和高效地互動。從整體來看,AMQP協議可劃分為兩層:
Functional Layer
功能層,位於協議上層主要定義了一組命令(基於功能的邏輯分類),用於應用程式呼叫實現自身所需的業務邏輯。例如:應用程式可以通過功能層定義佇列名稱,生產訊息到指定佇列,消費指定佇列訊息等基於(Message queues 模型)
Transport Layer
傳輸層,基於二進位制資料流傳輸,用於將應用程式呼叫的指令傳回伺服器,並返回結果,同時可以處理通道複用,幀處理,內容編碼,心跳傳輸,資料傳輸和異常處理。
傳輸層可以被任意傳輸替換,只要不改變應用可見的功能層相關協議,也可以使用相同的傳輸層,同時使用不同的高階協議
AMQ 模型設計驅動基於如下要求:
1.保證基於模型實現的應用之間相互可以聯通;
2.提供對服務質量的可靠控制;
3.命名規劃,要求命名明確且保持一致;
4.允許通過協議配置伺服器連線;
5.功能層命名能夠簡單的對映到應用程式級別的 API;
6.職責單一明確,每個操作只做一件事情。
AMQP 傳輸層設計驅動基於如下要求:
1.使用二進位制資料流壓縮和解壓,提高效率;
2.可以處理任意大小的訊息,且不做任何限制;
3.單個連線支援多個通訊通道;
4.客戶端和服務端基於長連結實現,且無特殊限制;
5.允許非同步指令基於管道通訊;
6.易擴充套件,基於新的需求和變化支援擴充套件;
7.新版本向下相容老版本;
8.基於斷言模型,異常可以快速定位修復;
9.對程式語言保持中立;
10.適應程式碼發展演變;
三、AMQP 通用元件
1.AMQ Model 架構
AMQ 作為中間層服務,把訊息生產和消費分隔開來,當訊息生產者出現異常,不影響消費者對訊息的消費,當消費者異常時,生產者生產的訊息可以存放到服務的記憶體或者磁碟,不會影響到消費的速率,同時,訊息也可以基於路由的規則可以投遞到指定的消費者消費。
AMQ 基於模組化通過 Exchange 和 Message Queue 兩個組建組合實現訊息路由分發:
Exchange:
基於訊息生產者和路由規則可以將訊息投遞到指定的 Message Queue;
Message Queue:
能夠將傳送過來的訊息進行儲存,同時將訊息轉發給消費者;
Exchange 和 Message Queue之間存在繫結關係,訊息到了 Exchange 後基於路由策略可以將訊息投遞到已繫結且符合路由策略的 Message Queue。
1.1 模型重要元件
Message Queue
訊息佇列會將訊息儲存到記憶體或者磁碟中,並將這些訊息按照一定順序轉發給一個或者多個消費者,每個訊息佇列都是獨立隔離的,相互不影響。
訊息佇列具有不同的屬性:私有,共享,持久化,臨時,客戶端定義 或者服務端定義等,可以基於實際需求選擇對應的型別,以 RabbitMQ 佇列特性為例:
共享持久化訊息佇列:將傳送的訊息儲存到磁碟,然後將訊息轉發給訂閱該佇列的所有消費者;
私有臨時訊息佇列:RabbitMQ 支援 rpc 呼叫,再呼叫過程中消費者都會臨時生成一個訊息佇列,只有當前消費者可見,且由服務端生成,呼叫完就會銷燬佇列。
Exchange
交換機收到生產者投遞的訊息,基於路由規則及佇列繫結關係匹配到投遞對應的交換機或者佇列進行分發,交換機不儲存訊息,只做轉發。
AMQP定義了許多標準交換型別,基本涵蓋了訊息傳遞所需的路由型別,一般 AMQP 伺服器都會提供預設的交換機基於交換機型別命名,AMQP 的應用程式也可以建立自己的交換機用於繫結指定的訊息佇列釋出訊息。
1.2 訊息的流轉過程
訊息生命週期
訊息主要由屬性及訊息內容組成,生產者建立訊息時可以給訊息設定屬性及訊息內容,同時也可以標記路由資訊在訊息上,可以將訊息傳送到指定交換機。
當訊息到達交換機時,交換機會基於路由規則判斷訊息能否轉發,如果不能轉發會丟棄訊息同時反饋給生產者。
交換機基於路由規則可以將訊息投遞到一個或者多個訊息佇列,伺服器通過複製或者計數器的方式將訊息儲存到不同佇列中,每個佇列中的訊息內容是相同的,但是操作是隔離的,相互不影響。
當訊息到達訊息佇列後,訊息佇列會基於 AMQP 協議投遞給消費者,如果無法投遞給消費者或者沒有消費者,訊息將在記憶體或者磁碟中儲存,等待消費者。
當訊息佇列可以將訊息傳遞給消費者時,訊息將從其內部緩衝區中刪除。 刪除操作可能立刻執行也可以再消費者確認訊息消費後再執行,刪除策略消費者可以選擇。
生產訊息投遞確認和消費訊息消費確認可以作為兩個事務,然後提交或者回滾事務。
2.AMQP 指令架構
2.1 協議指令(類和方法)
作為訊息中介軟體傳統的 API 定義的操作非常複雜,為了解決這個問題 AMQP 基於傳統 API 的功能,定義方法來對應實現 API 的操作每個方法只完成一件事,通過方法之間的組合來實現完整的功能,所以AMQP 形成了一個非常龐大的指令集,但是指令集中的方法都是便於理解的。
AMQP 指令集中指令,基於對應的特定功能域被劃分為不同的類,其中有一些類作為特定類的支援類,屬於可選的。
有如下兩個場景:
同步請求:
一邊等待對方傳送請求,一邊等待對方傳送回覆。適用於對效能要求不高的場景。
非同步請求:
傳送請求後不等待回覆,使用場景對效能要求比較高的場景。
為了簡化指令處理,我們給每個同步請求定義不同的回覆指令,也就是說同一個回覆指令不可能返回給2個不同的請求。這也意味著傳送同步請求的傳送方可以接受和處理回覆的指令,知道獲得有效的同步回覆指令為止。這種方式可以將 AMQP 與傳統的 RPC 協議區分開來。
一條指令可以被定義為同步請求,同步回覆(針對特定請求)或者非同步回覆,但是每種指令真正再被定義是在客戶端(即伺服器到客戶端)或者服務端(即戶端到伺服器)。
2.2 AMQP 對映到中間層 API
AMQP 對映到中間層 API,這個對映過程並不是所有方法和引數完全對映,因為有部分方法或者引數對應用程式沒有意義。同時對映規則也是固定的,基於已定的一些規則,所有方法按照這個規則對映,不需要人工干預。
例如:佇列宣告方法:
Queue.Declare
queue=my.queue
auto-delete=TRUE
exclusive=FALSE
可以作為一條線性記錄
+--------+---------+----------+-----------+-----------+
| Queue | Declare | my.queue | 1 | 0 |
+--------+---------+----------+-----------+-----------+
class method name auto-delete exclusive
也可以作為高階 API
queue_declare (session, "my.queue", TRUE, FALSE);
對於大多數應用程式來說,中間層(指令層)隱藏再技術層面,應用程式實際使用的 API 功能對比中間層相對會較少。
3.AMQP 傳輸層架構
3.1 簡要概述
AMQP 傳輸基於二進位制協議,傳輸的資訊被組織成各種型別的幀,幀攜帶協議方法和其他相關資訊,所有的幀具有相同個格式:幀頭,有效內容,幀尾。幀的有效內容格式取決於幀的型別。
假設再一個可靠的面向流的網路傳輸層(例如:TCP / IP)
再一個 Socket 連線中,可以有多個獨立的執行緒訪問,這種情況就是上文中提到的 Channel(通道),每個幀都有一個屬於自己的通道號碼,再同一個連線中所有的幀混合在一起,不同的通道共享連線,但是針對每個通道自身的幀都是按照嚴格的順序執行。
由於幀的有效內容都是由幀頭和幀尾包裝,所以對應幀資料的解析是相當簡單便捷的,同時基於協議規範生成幀資料也是非常容易。
3.2 資料型別
AMQP 使用的資料型別如下:
- Integers(數值範圍1-8, 8個位元組):用於表示大小,數量,限制等,整數型別無符號的,可以在幀內不對齊。
- Bits(統一為8個位元組):用於表示開/關值。
- Short strings:用於儲存簡短的文字屬性,字串個數限制為255,8個位元組
- Long strings:用於儲存二進位制資料塊。
- Field tables:包含鍵值對,欄位值一般為字串,整數等。
3.3 協議協商
AMQP 客戶端和伺服器存在協商協議。這意味著當客戶端連線時,服務端會提出一些客戶端可以接受或者修改的選項,如果雙方達成一致,連線繼續,基於協商協議,可以設定好一些先決條件。
在AMQP中,協商協議的一些具體方面:
實際的協議和版本。伺服器可以在同一埠上監聽多個協議。
加密引數和雙方的身份驗證。
最大幀大小,通道數量和其他操作限制。