RPC傳輸 作為AMQP的實現,RabbitMQ使用RPC(remote procedure call)模式進行遠程會話。而不同於一般的RPC會話——客戶端發出指令,服務端響應,但服務端不會向客戶端發出指令;在AMQP規範中,服務端與客戶端皆會發出指令。 對於AMQP,客戶端首先發送protocol ...
RPC傳輸
作為AMQP的實現,RabbitMQ使用RPC(remote procedure call)模式進行遠程會話。而不同於一般的RPC會話——客戶端發出指令,服務端響應,但服務端不會向客戶端發出指令;在AMQP規範中,服務端與客戶端皆會發出指令。
對於AMQP,客戶端首先發送protocol header至服務端。不過其並不能被當作一條請求指令,RabbitMQ的第一條指令是響應該信息的Connection.Start指令。其後客戶端通過Connection.StartOK回應此RPC請求。
為了建立客戶端與服務端的AMQP連接(connection),需要一連串的三次同步RPC請求,才能開始(start),調整(tune),打開(open)一條連接。一旦此過程完成,RabbitMQ便為應用程式即將發出的請求作好準備了。
AMQP規範里定義了通道(channel)的概念用於通信。通道使用AMQP連接作為通信的媒介,而其本身有著隔離會話的功能。一個AMQP連接可以包含多個通道,允許客戶端與服務端之間的多個會話同時發生,從技術上講,這被稱作復用,它對於處理多個任務的多線程或非同步應用十分有用。
AMQP的PRC幀結構
當指令發送至RabbitMQ及由其發出時,所需的參數皆被封裝為“幀”(frame)的數據結構——你可以將其想象成火車的運貨車廂。AMQP的幀由五個部分組成:
- 幀的類型
- 通道編號
- 幀的大小
- 幀的載荷(payload)
- 結束位元組標誌(ASCII值206)
AMQP規範定義了五種幀類型:協議header幀,方法幀,內容header幀,body幀以及心跳幀。每一種幀類型都有其特定功能。
- 協議header幀僅在連接RabbitMQ時使用一次。
- 方法幀在發送至RabbitMQ或由其發送的請求或應答中使用。
- 內容header幀包含消息的大小與屬性。
- body幀包含消息的內容。
- 心跳幀用於檢查確認連接雙方是否可用且工作正常。
當向RabbitMQ發送一條消息,方法幀,header幀與body幀會被用到。首先被髮送的是方法幀,其攜帶指令及所需的參數。之後的內容header幀包含消息屬性及body大小。AMQP會有最大幀大小的限制(預設為131KB),如果消息的body超過這個大小,內容會被分隔成多個body幀。這些幀始終以同樣順序發送:一個方法幀,內容header幀,和一或多個body幀。
如上圖所示,當發送一條消息,方法幀內包含Basic.Publish指令,其後內容header幀包含著消息屬性。這些屬性被封裝在AMQP規範的數據結構——Basic.Properties。最後消息的內容被整理成一定數量的body幀。
為了減小數據大小提高傳輸效率,方法幀與內容header幀內的數據都經過壓縮。但body幀里的數據是未被壓縮的,並且對於RabbitMQ而言這部分數據也是不會被檢驗的(相對於方法幀與內容header幀里的數據)。
AMQP協議的使用
在發送消息之前,至少需要完成三步配置,設置交換機(Exchange)及隊列(Queue),並將二者綁定(Binding)。
交換機通過Exchange.Declare指令創建。一旦RabbitMQ完成創建,一個Exchange.DeclareOK的方法幀會被髮送以作為響應。若是有任何緣由導致創建指令失敗,RabbitMQ會關閉相應的通道。
隊列由Queue.Declare指令創建。同樣地,若創建失敗,相應通道會被關閉。
當交換機與隊列都完成創建後,通過Queue.Bind指令,可以綁定一個隊列至一個交換機。
RabbitMQ接收一條消息時,會從方法幀開始檢測。Basic.Publish方法幀會包含交換機名稱,路由鍵值等關鍵信息。當RabbitMQ評估這些數據時,會嘗試用交換機名稱與所配置的交換機相匹配。當找到合適的交換機,它會再評估其中的綁定關係,通過路由鍵值以找到正確的隊列。如果能找到符合條件的隊列,RabbitMQ伺服器會以先進先出(FIFO)的順序對消息進行排列。加入隊列的並非實際的消息數據,而是其引用。這樣做可以大幅提升性能,尤其當消息需要發佈到多個隊列時。只有所有隊列中所引用的消息皆被投遞或移除,實際的消息數據才會被RabbitMQ從記憶體中移除。
在消費(consume)消息時,需要註意Basic.Consume指令中no_ack參數的設置。如果no_ack標置設為true時,RabbitMQ將會持續發送消息,除非消費側發送Basic.Cancel指令或者消費側斷開連接;若設置為false時,消費側必須在每次接收到消息時發送Basic.Ack請求。這種情形下,消費側必須在Basic.Deliver方法幀中攜帶delivery tag參數。RabbitMQ使用這個投遞標簽與通道一併作為通信的唯一標識,以用於消息應答,拒絕及否定應答。