隨筆 - 41  文章 - 7  trackbacks - 0
          <2016年8月>
          31123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          1 概述
          1.1 本文檔的目標(biāo)
          此文檔定義了一個網(wǎng)絡(luò)協(xié)議-高級消息隊(duì)列協(xié)議(AMQP), 它使一致的客戶端程序可以與一致的消息中間件服務(wù)器進(jìn)行通信.
          我們面對的是這個領(lǐng)域有經(jīng)驗(yàn)的技術(shù)讀者,同時還提供了足夠的規(guī)范和指南.技術(shù)工程師可以根據(jù)這些文檔,在任何硬件平臺上使用各種編程語言來構(gòu)建遵從該協(xié)議的解決方案。
          1.2 摘要
          1.2.1 為什么使用AMQP?
          AMQP在一致性客戶端和消息中間件(也稱為"brokers")之間創(chuàng)建了全功能的互操作.
          我們的目標(biāo)是實(shí)現(xiàn)一種在全行業(yè)廣泛使用的標(biāo)準(zhǔn)消息中間件技術(shù),以降低企業(yè)和系統(tǒng)集成的開銷,并且向大眾提供工業(yè)級的集成服務(wù)。
          我們的宗旨是通過AMQP,讓消息中間件的能力最終被網(wǎng)絡(luò)本身所具有,并且通過消息中間件的廣泛使用發(fā)展出一系列有用的應(yīng)用程序.
          1.2.2 AMQP范圍
          為了完全實(shí)現(xiàn)消息中間件的互操作性,需要充分定義網(wǎng)絡(luò)協(xié)議和消息代理服務(wù)的功能語義。
          因此,AMQP通過如下來定義了網(wǎng)絡(luò)協(xié)議(AMQP是協(xié)議!)和服務(wù)端服務(wù):
          1. 一套確定的消息交換功能,也就是“高級消息交換協(xié)議模型”。AMQP模型包括一套用于路由和存儲消息的功能模塊,以及一套在這些模塊之間交換消息的規(guī)則。
          2. 一個網(wǎng)絡(luò)線級協(xié)議(數(shù)據(jù)傳輸格式),AMQP促使客戶端可使用AMQ模型來與服務(wù)器交互.
          可以只實(shí)現(xiàn)AMQP協(xié)議規(guī)范中的的部分語義,但是我們相信這些明確的語義有助于理解這個協(xié)議。
          1.2.3 高級消息隊(duì)列模型(AMQ 模型)
          我們需要明確定義服務(wù)器語義,因?yàn)樗蟹?wù)器實(shí)現(xiàn)都應(yīng)該與這些語義保持一致性,否則就無法進(jìn)行互操作. 因此AMQ 模型定義了一系列模塊化組件和標(biāo)準(zhǔn)規(guī)則來進(jìn)行協(xié)作. 有三種類型的組件可以連接服務(wù)器處理鏈來創(chuàng)建預(yù)期的功能:
          1. "交換器(exchange)" :接收來自發(fā)布者應(yīng)用程序的消息,并基于任意條件(通常是消息屬性和內(nèi)容)將這些消息路由到消息隊(duì)列(message queues).
          2."消息隊(duì)列(message queue)":存儲消息直到它們可以被消費(fèi)客戶端應(yīng)用程序(或多線程應(yīng)用程序)安全處理.
          3."綁定(binding)":定義了消息隊(duì)列與交換器之間的關(guān)系,并提供了消息路由條件.
          使用這些模型我們可以很容易地模擬經(jīng)典的存儲轉(zhuǎn)發(fā)隊(duì)列和面向消息中間件的主題訂閱概念. 我們還可以表示更為復(fù)雜的概念,例如:基于內(nèi)容的路由,工作負(fù)載分配和按需消息隊(duì)列。
          大致上講, AMQP 服務(wù)器類似與郵件服務(wù)器, 每個交換器都扮演了消息傳送代理,每個消息隊(duì)列都作為郵箱,而綁定則定義了每個傳送代理中的路由表.發(fā)布者發(fā)送消息給獨(dú)立的傳送代理,然后傳送代理再路由消息到郵箱中.消費(fèi)者從郵箱中收取消息. 相比較而言,在AMQP之前的許多中間件系統(tǒng)中,發(fā)布者直接發(fā)送消息到獨(dú)立收件箱(在存儲轉(zhuǎn)發(fā)隊(duì)列的情況下),或者發(fā)布到郵件列表中 (在主題訂閱的情況下).
          區(qū)別就在于用戶可以控制消息隊(duì)列和交換器之間的綁定規(guī)則,這可以做很多有趣的事情,比如定義一條規(guī)則:“將所有包含這樣消息頭的消息都復(fù)制一份再發(fā)送到消息隊(duì)列中”。
          AMQ模型是基于下面的需求來驅(qū)動設(shè)計的:
          1. 支持與主要消息產(chǎn)品相媲美的語義。.
          2. 提供與主要消息產(chǎn)品相媲美的性能水平.
          3. 允許通過應(yīng)用程序使用服務(wù)器特定語義來編程.
          4.靈活性,可擴(kuò)展性,簡單性
          1.2.4 高級消息隊(duì)列協(xié)議(AMQP)
          AMQP協(xié)議是具有現(xiàn)代特征的二進(jìn)制協(xié)議: 它是多通道的, 協(xié)商的,異步的,安全的,便攜的,自然的,高效的。 AMQP通常劃分成兩層:


          功能層( functional layer)定義了一系列命令(分成功能獨(dú)立的邏輯類),可為應(yīng)用程序做有用工作。
          傳輸層,將這些方法從應(yīng)用程序應(yīng)用搬到服務(wù)器并返回,它同時會處理通道復(fù)用,幀同步, 內(nèi)容編碼,心跳檢測, 以及數(shù)據(jù)表示和錯誤處理.
          在不改變協(xié)議可視功能的前提下,可使用任意的傳輸協(xié)議來替換傳輸層.也可以將同一個傳輸層用于不同的高級協(xié)議.
          AMQ模型是基于下面需求驅(qū)動的:
           為了保證一致性實(shí)現(xiàn)之間的互操作性。.
           提供服務(wù)質(zhì)量的顯式控制.
           一貫的、明確的命名.
           通過協(xié)議允許服務(wù)器完全可配置.
           使用命令標(biāo)記法可輕易地映射到應(yīng)用程序級別API.
           一目了然,每個操作都在做自己的事情。
          AMQP傳輸層的設(shè)計是由以下需求驅(qū)動的(沒有特定順序):
           堅(jiān)實(shí)的,使用二進(jìn)制編碼來打包和解包.
           處理任何大小的消息,沒有明顯限制(實(shí)際上有限制).
           在單個連接上攜帶攜帶多個通道.
           長時間生存的,沒有顯著的內(nèi)建限制。
           允許異步命令在管道中排隊(duì).
           易于擴(kuò)展,以處理新的更新的需求。
           與未來版本兼容。
           可修復(fù),使用一個強(qiáng)大的斷言模型。
           對編程語言保持中立。
           適合代碼擴(kuò)展過程。
          1.2.5 規(guī)模化部署

          AMQP范圍涵蓋不同等級規(guī)模,大致如下:
           開發(fā)/臨時使用: 1臺服務(wù)器, 1個用戶, 10個消息隊(duì)列,每秒 1個消息.
           產(chǎn)品應(yīng)用程序: 2服務(wù)器, 10-100個用戶, 10-50個消息隊(duì)列,每秒10個消息(36Kmessages/hour).
           部門任務(wù)關(guān)鍵應(yīng)用: 4 臺服務(wù)器, 100-500個用戶, 50-100 個消息隊(duì)列, 每秒100個消息(360K/hour).
          區(qū)域任務(wù)關(guān)鍵應(yīng)用: 16 臺服務(wù)器, 500-2,000 個用戶, 100-500 個消息隊(duì)列和主題,
          每秒1000個消息(3.6M/hour).
           全球任務(wù)關(guān)鍵應(yīng)用: 64 臺服務(wù)器, 2K-10K 個用戶, 500-1000 個消息隊(duì)列和主題,
          每秒10,000 個消息(36M/hour).
           市場數(shù)據(jù)(交易): 200 臺服務(wù)器, 5K 個用戶, 10K 主題, 第秒100K個消息(360M/hour).
          規(guī)模越大,消息傳輸延遲就越重要.例如,市場數(shù)據(jù)變化相當(dāng)快. 實(shí)現(xiàn)可以依據(jù)不同的服務(wù)質(zhì)量和管理能力不區(qū)分對待,但必須與本規(guī)范兼容.
          1.2.6 功能范圍
          我們要支持多種消息傳遞架構(gòu):
           使用多個writers和一個reader來存儲轉(zhuǎn)發(fā).
           使用多個writers和多個readers來分散工作負(fù)載.
           使用多個writers和多個readers來發(fā)布訂閱
           使用多個writers和多個readers來基于內(nèi)容路由.
           使用多個writers和多個readers來進(jìn)行隊(duì)列文件傳輸.
           在兩個節(jié)點(diǎn)之間進(jìn)行點(diǎn)對點(diǎn)連接.
           使用多個源和多個readers來分布市場數(shù)據(jù).
          1.3 文檔組織
          文檔分成五個章節(jié), 其中大部分設(shè)計為可按你的興趣來獨(dú)立閱讀:
          1. "概述" (本章). 讀本章來了解介紹.
          2. "總體架構(gòu)",這章中我們描述了架構(gòu)和AMQP總體設(shè)計. 本章的目的是幫助系統(tǒng)架構(gòu)師了解AMQP如何工作的.
          3. "功能說明", 這章中我們定義了應(yīng)用程序如何與AMQP一起協(xié)同工作. 本章先講述了一個可讀性討論,其次是每個協(xié)議命令的詳細(xì)規(guī)范,以作為實(shí)施者參考。在閱讀本章之前,你應(yīng)該閱讀總體構(gòu)架.
          4. "技術(shù)說明",這章中我們定義了AMQP傳輸層是如何工作的. 這章由簡短討論和線路結(jié)構(gòu)的詳細(xì)說明組成.你如果想線路級協(xié)議是如何工作的,可以閱讀本章.

          1.4 約定
          1.4.1 實(shí)現(xiàn)者指導(dǎo)方針
           我們使用了IETF RFC 2119中定義的術(shù)語 MUST, MUST NOT, SHOULD, SHOULD NOT, 和 MAY.
           當(dāng)討論需要AMQP服務(wù)器的特定行為時,我們使用術(shù)語"the server".
           當(dāng)討論需要AMQP客戶端的特定行為時,我們使用術(shù)語"the client".
           我們使用術(shù)語"the peer" 來代表服務(wù)器或客戶端.
           如果沒有指示,數(shù)字值是十進(jìn)制的.
           Protocol常量是以大寫名稱表示. AMQP實(shí)現(xiàn)應(yīng)當(dāng)在定義,源碼,以及文檔中使用這些名稱.
           屬性名稱,方法名稱,以及幀字段是以小寫名稱來表示. AMQP實(shí)現(xiàn)應(yīng)當(dāng)在定義,源碼,以及文檔中使用這些名稱.
           AMQP中的字符串是區(qū)分大小寫的.例如, "amq.Direct"與"amq.direct”是兩個不同的交換器.
          1.4.2 版本編號方式
          AMQP版本使用兩個或三個數(shù)字進(jìn)行表示 – 主版本號,次版本號以及可選的修訂版本號.為了方便,版本號表示為:major-minor[-revision] 或major.minor[.revision]:
           官方說明中,major, minor, 和revision均支持0到99的數(shù)字.
           Major, minor, 和 revision 中100及其以上的數(shù)字保留用于內(nèi)部測試和開發(fā).
           版本號表明了語法和語義互操作性。
           版本 0-9-1 表示 major = 0, minor = 9, revision = 1.
           1.1版本表示為major = 1, minor = 1, revision = 0. AMQP/1.1等價于AMQP/1.1.0或AMQP/1-1-0.

          1.4.3 技術(shù)術(shù)語
          這些術(shù)語在本文檔的上下文中有特殊的意義:
           AMQP 命令架構(gòu)(AMQP command architecture): 用于在AMQ模型架構(gòu)上執(zhí)行操作的線級協(xié)議命令.
           AMQ模塊架構(gòu)(AMQ model architecture): 表示關(guān)鍵實(shí)體和語義的邏輯框架,它必須對兼容AMQP實(shí)現(xiàn)的服務(wù)器可用, 使得服務(wù)器的狀態(tài)可以通過客戶端按本規(guī)范中定義的語義來實(shí)現(xiàn).
           Connection: 網(wǎng)絡(luò)連接,如.一個TCP/IP socket連接.
           Channel: 兩個AMQP節(jié)點(diǎn)之間雙向通信流. 通道是多路復(fù)用的,因此單個網(wǎng)絡(luò)連接可以支撐多個通道
           Client: AMQP連接或通道的發(fā)起人. AMQP不是對稱的.客戶端生產(chǎn)和消費(fèi)消息,而服務(wù)器端入列和路由消息.
           Server:接受客戶端連接,實(shí)現(xiàn)AMQP消息隊(duì)列和路由功能的過程.也稱為"broker".
           Peer: AMQP連接中任意一方.AMQP連接明確包含兩個節(jié)點(diǎn)(一個是client, 一個是server).
           Frame: 一個正式定義的連接數(shù)據(jù)包。框架總是連接寫和讀數(shù)據(jù)包-作為連接上的一個單元。
           Protocol class: 用于處理特定類型功能的AMQP命令集合 (也稱為方法).
           Method: 用于在節(jié)點(diǎn)之間傳遞特定類型的AMQP命令幀.
           Content: 服務(wù)器和應(yīng)用程序之間傳送的數(shù)據(jù).這個術(shù)語是“message”的同義詞。
           Content header:描述內(nèi)容屬性特定類型幀.
           Content body: 包含原始應(yīng)用程序數(shù)據(jù)的特定類型幀.內(nèi)容體幀完全不透明-服務(wù)器不以任何方式檢查或修改其body內(nèi)容.
           Message: 與Content同義.
           Exchange: 服務(wù)器中接收來自生產(chǎn)者應(yīng)用程序的消息的實(shí)體,并可選擇將這些消息路由到服務(wù)器中的消息隊(duì)列.
           Exchange type: 交換器特定模型的算法和實(shí)現(xiàn).而"交換器實(shí)例"是服務(wù)器中用于接收和路由消息的實(shí)體.
           Message queue: 保存消息并將它們轉(zhuǎn)發(fā)給消費(fèi)者應(yīng)用程序的命名實(shí)體。
           Binding: 用于創(chuàng)建消息隊(duì)列和交換器綁定關(guān)系的實(shí)現(xiàn).
           Routing key: 一個虛擬地址,虛擬機(jī)可用它來確定如何路由一個特定消息.
           Durable: 服務(wù)器資源可在服務(wù)器重啟時恢復(fù).
           Transient: 服務(wù)器資源或消息會在服務(wù)器重啟后擦除或重置.
           Persistent: 服務(wù)器存儲在可靠的磁盤存儲上的消息,并且在服務(wù)器重新啟動后不丟失.
           Consumer: 從消息隊(duì)列中請求消息的客戶端應(yīng)用程序.
           Producer: 發(fā)布消息到交換器中的客戶端程序.
           Virtual host: 交換器,消息隊(duì)列以及相關(guān)對象的集合. 虛擬主機(jī)是共享同一個身份驗(yàn)證和加密環(huán)境的獨(dú)立服務(wù)器域。
           Assertion: 一個必須為true且可繼續(xù)執(zhí)行的條件.
           Exception: 一個失敗的斷言,可通過Channel或Connection來關(guān)閉.
          在AMQP中這些術(shù)語沒有特殊意義:
           Topic: 通常是分發(fā)消息的一種手段; AMQP使用一個或多個交換器類型來實(shí)現(xiàn)topics.
           Subscription:通常是從topics中接收數(shù)據(jù)的請求, AMQP以消息隊(duì)列和綁定的方式來實(shí)現(xiàn)訂閱.
           Service: 通常與server一個含義. AMQP使用“server”來遵循IETF標(biāo)準(zhǔn)命名。
           Broker: 通常與server一個含義。AMQP使用術(shù)語“client”和“server"來遵循IETF標(biāo)準(zhǔn)術(shù)語。
           Router: 有時用來描述交換器的動作.交換器也可以作為消息終點(diǎn), "router" 在網(wǎng)絡(luò)領(lǐng)域中有特殊意義,因此AMQP不會使用它.

          2 總體架構(gòu)
          2.1 AMQ 模塊架構(gòu)
          本節(jié)將講解了服務(wù)器語法,須標(biāo)準(zhǔn)化來保證AMQP實(shí)現(xiàn)之間可互操作的語義。
          2.1.1 主要實(shí)體
          下面的圖顯示了整體AMQ模型:



          我們可以總結(jié)一下中間件服務(wù)器是什么:它是一個接受消息的數(shù)據(jù)服務(wù)器,并主要做兩件事情,依據(jù)條件將消息路由給不同的消費(fèi)者,當(dāng)消費(fèi)者消費(fèi)速度不夠快時,它會把消息緩存在內(nèi)存或磁盤上.
          在AMQP之前的服務(wù)器中,它們會通過實(shí)現(xiàn)了特定類型路由和緩存的龐大引擎來完成. AMQ模塊使用較小的模塊結(jié)合更多樣和穩(wěn)健的方案來實(shí)現(xiàn). 它把這些任務(wù)分成了兩個不同角色:
           交換器, 它接受來自生產(chǎn)者的消息并將它們路由到消息隊(duì)列.
           消息隊(duì)列, 它存儲消息消息并把它們轉(zhuǎn)發(fā)給消費(fèi)者應(yīng)用程序.
          在交換器和消息隊(duì)列之間有一個明顯的界面,稱為綁定(binding),我們隨后會進(jìn)行講解.
          AMQP提供了運(yùn)行時程序語義,主要有兩方面:
          1. 運(yùn)行時通過該協(xié)議可創(chuàng)建任意的交換器和消息隊(duì)列類型的能力(有些是在標(biāo)準(zhǔn)中定義的,但可以添加其他作為服務(wù)器擴(kuò)展)。
          2. 運(yùn)行時通過協(xié)議包裝交換器和消息隊(duì)列來創(chuàng)建任何需要的消息處理系統(tǒng)的能力.
          2.1.1.1 消息隊(duì)列(Message Queue)
          消息隊(duì)列用于在內(nèi)存或磁盤上存儲消息, 并將它們依次投遞給一個或多個消費(fèi)者應(yīng)用程序.消息隊(duì)列是消息存儲和分發(fā)的實(shí)體. 每個消息隊(duì)列是完全獨(dú)立的,且是一個相當(dāng)聰明的對象。
          消息隊(duì)列有多個屬性:私有的或共享的, 持久的或臨時的,客戶端命名的或服務(wù)器端命名的等等.
          通過選擇希望的屬性,我們可以使用消息隊(duì)列來實(shí)現(xiàn)傳統(tǒng)的中間件實(shí)體,如:
           共享存儲轉(zhuǎn)發(fā)隊(duì)列:它可以持有消息,并以round-robin方式在消費(fèi)者之間分發(fā)消息.存儲轉(zhuǎn)發(fā)隊(duì)列通常是在多個消費(fèi)者之間是持久化的.
           私有回復(fù)隊(duì)列:它可以持有消息,并把消息轉(zhuǎn)發(fā)給單個消費(fèi)者. 回復(fù)隊(duì)列通常是臨時的,服務(wù)端命名的,且對于某個消費(fèi)者來說是私有的.
           私有訂閱隊(duì)列:它可持有來自不同訂閱源的消息,并將它們轉(zhuǎn)發(fā)給單個消費(fèi)者.
          訂閱隊(duì)列通常是臨時的,服務(wù)器端命名的,并對于某個消費(fèi)者來說是私有的
          AMQP沒有定義這些類別:這些只是如何使用消息隊(duì)列的例子.創(chuàng)建如持久化,共享訂閱隊(duì)列的新實(shí)體沒什么意義.

          2.1.1.2 交換器(Exchange)
          交換器接收來自生產(chǎn)者應(yīng)用程序的消息,并將它們按照事先約定的規(guī)則路由到消息隊(duì)列中.
          這些預(yù)先約定的規(guī)則或條件稱為綁定. 交換器會與路由引擎匹配.
          也就是說,他們會檢查消息,并使用他們的綁定表來決定如何將這些消息轉(zhuǎn)發(fā)到消息隊(duì)列或其他交換器中。交換器永遠(yuǎn)不會存儲信息。“交換器”一詞是指一類算法或算法實(shí)例。
          更確切的說,我們談到了交換器類型和交換器實(shí)例.
          AMQP定義了許多交換器類型,它們覆蓋了常見消息路由分發(fā)的基礎(chǔ)類型. AMQP服務(wù)器提供了這些交換器的默認(rèn)實(shí)例.使用AMQP的應(yīng)用程序可額外創(chuàng)建它們自己的交換器實(shí)例.交換器類型是命名的,這樣創(chuàng)建交換器的應(yīng)用程序就可以告知服務(wù)器他們使用的交換器類型.
          交換器實(shí)現(xiàn)也可以是命名的,這樣應(yīng)用程序可指定如何綁定隊(duì)列來發(fā)布消息.
          交換器還可以做更多的消息路由.它們可作為服務(wù)器內(nèi)的智能路由代理,按需接受消息和生產(chǎn)消息. 交換器概念的目的是定義一套模型或標(biāo)準(zhǔn),使得可以合理地擴(kuò)展AMQP服務(wù)器,因?yàn)榭蓴U(kuò)展性會對互操作產(chǎn)生影響.
          2.1.1.3 路由鍵 (Routing Key)
          在一般情況下,交換器會檢查消息的屬性,如,它的header字段,body內(nèi)容,并使用這些和其他來源中的數(shù)據(jù)來決定如何消息路由。
          在大多數(shù)簡單情況下,交換器會檢查某個單一的鍵字段,我們稱之為“路由鍵”。
          路由鍵是一個虛擬地址,該虛擬地址可用來決定如何路由消息。
          對于點(diǎn)對點(diǎn)的路由,路由鍵通常是消息隊(duì)列的名稱。
          對于主題發(fā)布訂閱路由,路由鍵通常是topic層次結(jié)構(gòu)值。
          在更復(fù)雜的情況下,路由鍵可以是消息header字段和/或消息內(nèi)容的組合體。
          2.1.1.4 類比電子郵件
          如果我們做過一個類似的電子郵件系統(tǒng),那我們會看到AMQP概念就不再是激進(jìn)的:
          一個AMQP消息類似于電子郵件;
           消息隊(duì)列就像一個郵箱;
           消費(fèi)者是一個可讀取和刪除電子郵件的郵件客戶端;
          交換器類似于一個MTA (郵件傳輸代理),它會檢查電子郵件,并基于路由鍵和表來決定如何將電子郵件發(fā)送到一個或多個郵箱中;
           路由鍵對應(yīng)于電子郵件中To:,Cc: ,Bcc: 地址, 不包含服務(wù)端信息(路由完全是AMQP服務(wù)器內(nèi)部的行為);
           每個交換器實(shí)例類似于單獨(dú)的MTA過程,用于處理一些電子郵件子域名或特定類型的電子郵件傳輸;
           綁定類似于MTA路由表中的實(shí)體.
          AMQP的強(qiáng)大來自于創(chuàng)建隊(duì)列(郵箱),交換器(MTA過程),和綁定(路由實(shí)體)的能力,并可在運(yùn)行時,將這些鏈接在一起,這遠(yuǎn)遠(yuǎn)超出了簡單的"to" 地址到郵箱名稱的映射.
          我們不應(yīng)該把email與AMQP類比得太遠(yuǎn):它們之間有根本上的區(qū)別。AMQP面臨的挑戰(zhàn)是如何來存儲和轉(zhuǎn)發(fā)服務(wù)器中的消息,SMTP(IETF RFC 821)稱其為“自治系統(tǒng)”。而在電子郵件中的挑戰(zhàn)是自治系統(tǒng)之間如何路由消息。
          在一個服務(wù)器內(nèi)路由和在多個服務(wù)器之間路由,方式是不同的,應(yīng)該有不同的解決方案.
          在多個AMQP服務(wù)器(擁有不同實(shí)體)之間路由時,必須明確建立不同的橋梁, 為達(dá)到在多個獨(dú)立實(shí)體之間傳送消息的目的,一個AMQP服務(wù)器必須作為另一個AMQP服務(wù)器的客戶端.這種工作方式很適合需要使用AMQP的業(yè)務(wù)類型,因?yàn)檫@些橋梁可以為業(yè)務(wù)流程,合同義務(wù)和安全問題打下基礎(chǔ).
          2.1.2 消息流(Message Flow)
          下面的圖展示了通過AMQ模塊服務(wù)器的消息流:

          2.1.2.1 消息生命周期
          一個AMQP消息由一組屬性和不透明的內(nèi)容組成。一個新消息是由生產(chǎn)者應(yīng)用程序通過使用AMQP client API來創(chuàng)建的.生產(chǎn)者將“內(nèi)容”附著在消息中,并對其設(shè)置一些消息“屬性”。生產(chǎn)者使用路由信息來標(biāo)記消息,其表面上類似于地址,但幾乎可以創(chuàng)建任何模式。然后,生產(chǎn)者將消息發(fā)送到服務(wù)器上的交換器中。
          當(dāng)消息到達(dá)服務(wù)器時,交換器通常會將消息路由到一級存在于服務(wù)器上的消息隊(duì)列中.如果消息不能路由,交換器會默默地丟棄或者將其返回給生產(chǎn)者. 生產(chǎn)者可以選擇如何來處理未路由消息.
          單個消息可存在于多個消息隊(duì)列. 服務(wù)器可以不同方式進(jìn)行處理,如通過拷貝消息或通過引用計數(shù)器等. 這不影響互操作性。然而,當(dāng)一個消息被路由到多個消息隊(duì)列時,它在每個消息隊(duì)列上都是一樣的。沒有獨(dú)特的標(biāo)識符來區(qū)分不同的副本。
          當(dāng)消息到達(dá)消息隊(duì)列時,消息隊(duì)列會通過AMQP,立即嘗試將消息傳遞給消費(fèi)者應(yīng)用程序.如果不行,消息隊(duì)列會存儲消息(按發(fā)布者要求存儲在內(nèi)存或磁盤中),并等待消費(fèi)者準(zhǔn)備好.如果沒有消費(fèi)者,消息隊(duì)列通過AMQP將消息返回給生產(chǎn)者(再次地,如果生產(chǎn)者對此有要求的話).
          當(dāng)消息隊(duì)列把消息投遞給消費(fèi)者后,它會從內(nèi)部緩沖區(qū)中刪除消息.這有可能立即發(fā)生,也有可能在消費(fèi)者應(yīng)答它已成功處理之后刪除.消費(fèi)者可選擇如何以及何時來應(yīng)答消息.同樣地, 消費(fèi)者也可以拒絕消息(一個否定應(yīng)答).
          生產(chǎn)者消息和消費(fèi)者應(yīng)答可以組成事務(wù). 當(dāng)一個應(yīng)用程序同時扮演兩種角色時,通常它會做混合工作:發(fā)送消息和發(fā)送應(yīng)答,然后提交或回滾事務(wù).
          從服務(wù)器投遞消息給消費(fèi)者,這個過程不是事務(wù)的,它只能通過消息應(yīng)答來處理.
          2.1.2.2 生產(chǎn)者能看到什么
          通過與電子郵件系統(tǒng)的類比,我們可以看到生產(chǎn)者不能直接向消息隊(duì)列發(fā)送消息.
          如果允許這樣做,將會破壞AMQ模塊中的抽象. 這就像允許電子郵件繞過MTA的路由表,直接發(fā)送到郵箱中一樣. 這會導(dǎo)致在中間過程中不能插入過濾處理,例如,垃圾郵件檢測.
          AMQ 模塊也使用了與電子郵件系統(tǒng)一樣的準(zhǔn)則:所有消息都發(fā)向單一的某個點(diǎn):交換器或MTA,然后這個點(diǎn)根據(jù)規(guī)則和隱藏在發(fā)送者中的信息來檢查消息,并將消息路由到落腳點(diǎn)(對于發(fā)送者來說,信息仍然是隱藏的).
          2.1.2.3 消費(fèi)者能看到什么
          當(dāng)我們站在消費(fèi)者角度來看與電子郵件系統(tǒng)的類比,這就開始崩潰了(break down). Email客戶端是被動的 - 它們可以讀取它們的郵箱,但它們不能對郵箱的收取產(chǎn)生任何影響.而AMQP消費(fèi)者也可以是被動的,就像email客戶端一樣. 也就是說,我們可以編寫一個程序來希望特定消息隊(duì)列準(zhǔn)備好綁定, 并且應(yīng)用程序脫離消息隊(duì)列來簡單處理消息(譯者注:不懂).
          此外,我們也允許AMQP 客戶端程序執(zhí)行下面的操作:
           創(chuàng)建或銷毀消息隊(duì)列;
           通過綁定來定義消息隊(duì)列填充的方式;
           選擇不同的交換器,這將完全改變路由語義.
          使用協(xié)議,這有點(diǎn)像電子郵件系統(tǒng)能做的:
           創(chuàng)建一個新郵箱;
           告訴MTA帶特定header字段的消息都可以拷貝到這個郵箱中;
           完全改變電子郵件系統(tǒng)解析地址和其它消息頭的方式
          我們看到AMQP更像是一種語言的連接片而非一個系統(tǒng).這正是目標(biāo)的一部分,通過協(xié)議來使服務(wù)器行為可編程化.

          2.1.2.4 原子模式
          大多數(shù)集成架構(gòu)不需要這個級別的復(fù)雜度.就像業(yè)余攝影師一樣,大多數(shù)AMQP用戶需要傻瓜式的模式. AMQP通過兩方面來簡化了概念:
           針對消息生產(chǎn)者的默認(rèn)交換器;
           基于隊(duì)列名稱作為路由鍵來匹配默認(rèn)綁定.
          實(shí)際上,給予適當(dāng)?shù)臋?quán)限,默認(rèn)的綁定讓生產(chǎn)者直接發(fā)送消息到消息隊(duì)列–它模擬了傳統(tǒng)中間件中簡單的“發(fā)送到目的地"的解決方案。
          默認(rèn)綁定不會阻止消息隊(duì)列更復(fù)雜方式的使用。然而它使得在不需要了解交換器和綁定如何工作的情況下,就可以使用AMQP.
          2.1.3 交換器
          2.1.3.1 交換器類型
          每種交換器類型都實(shí)現(xiàn)了某種路由算法.這里有許多標(biāo)準(zhǔn)的交換器類型(將在"功能說明"章節(jié)中講解), 但有兩點(diǎn)是很重要的:
           基于路由鍵來路由的direct 交換器類型. 默認(rèn)交換器是direct交換器.
           基于路由模式來路由的topic 交換器類型.
          在啟動時,服務(wù)器將會創(chuàng)建一系列交換器,如direct 和topic交換器.
          2.1.3.2 交換器生命周期
          每個AMQP 服務(wù)器都預(yù)先創(chuàng)建了許多交換器(實(shí)例).這些交換器當(dāng)服務(wù)器啟動時就存在了,不能被銷毀. AMQP 應(yīng)用程序也可以創(chuàng)建它們自己的交換器.AMQP不會使用像這樣的"create"方法,相反它使用 "declare"方法,其意義是:"如果你不存在就創(chuàng)建,否則繼續(xù)".這是合理的:應(yīng)用程序可以為了私有使用而創(chuàng)建交換器,并在完成工作時進(jìn)行銷毀. AMQP提供了方法來銷毀交換器,但一般來說,應(yīng)用程序不會這樣做.在本章我們的例子中,我們假設(shè)交換器已在服務(wù)器啟動時創(chuàng)建過了. 我們不會展示聲明交換器的應(yīng)用程序.

          2.1.4 消息隊(duì)列
          2.1.4.1 消息隊(duì)列屬性
          當(dāng)客戶端程序創(chuàng)建了消息隊(duì)列時,它可以選擇一些重要的屬性:
           name - 如果沒有指定,服務(wù)器會選擇一個名稱,并將其提供給客戶端.一般來說,當(dāng)應(yīng)用程序共享消息隊(duì)列時,它們已經(jīng)對消息隊(duì)列名稱作了事先的約定,當(dāng)一個應(yīng)用程序需要出于其自身目的來要求隊(duì)列時,它可讓服務(wù)器提供一個名稱.
           exclusive - 如果設(shè)置了,隊(duì)列將只屬于當(dāng)前連接,且在連接關(guān)閉時刪除.
           durable - 如果設(shè)置了, 消息會進(jìn)行存儲,并在服務(wù)器重啟時激活. 當(dāng)服務(wù)器重啟時,它可能會丟失瞬時消息.
          2.1.4.2 隊(duì)列生命周期
          這里主要有兩種消息隊(duì)列生命周期:
           持久化消息隊(duì)列:它們可被多個消費(fèi)者共享,并可獨(dú)立地存在- 即.不管是否有消費(fèi)者接收它們,它都可以繼續(xù)存在收集消息.
           臨時消息隊(duì)列:對某個消費(fèi)者是私有的,只能綁定到此消費(fèi)者.當(dāng)消費(fèi)者斷開連接時,消息隊(duì)列將被刪除.
          也存在一些變化,如共享消息隊(duì)列會在最后一個消費(fèi)才斷開連接時刪除消息隊(duì)列.下面的圖展示了臨時消息隊(duì)列創(chuàng)建和刪除的過程:



          2.1.5 綁定
          綁定表示的是交換和消息隊(duì)列之間的關(guān)系,該關(guān)系告訴交換器如何路由消息。綁定是從客戶端應(yīng)用程序命令(一個擁有和使用消息隊(duì)列的應(yīng)用程序)中來綁到交換器上的。我們可以在偽代碼中表達(dá)一個綁定命令,如下所示:
          Queue.Bind <queue> TO <exchange> WHERE <condition>
          讓我們看一下三種典型使用情況: 共享隊(duì)列,私有回復(fù)隊(duì)列,發(fā)布訂閱.
          2.1.5.1 構(gòu)造共享隊(duì)列
          共享隊(duì)列是經(jīng)典的中間件"點(diǎn)對點(diǎn)隊(duì)列".在AMQP中,我們可使用默認(rèn)交換器和默認(rèn)綁定.我們假設(shè)消息隊(duì)列稱為"app.svc01". 這里是創(chuàng)建共享隊(duì)列的偽代碼:
          Queue.Declare
          queue=app.svc01
          在這個共享隊(duì)列中,我們有許多消費(fèi)者.要從共享隊(duì)列中消費(fèi), 每個消費(fèi)者可以這樣做:
          Basic.Consume
          queue=app.svc01
          要發(fā)送到共享隊(duì)列, 每個生產(chǎn)者都要將消息發(fā)布到默認(rèn)交換器:
          Basic.Publish
          routing-key=app.svc01
          2.1.5.2 構(gòu)建回復(fù)隊(duì)列
          回復(fù)隊(duì)列通常是臨時的,服務(wù)器分配名稱的. 它們通常也是私有的,即只能由單個消費(fèi)者讀取. 除了這些特殊情況外,回復(fù)隊(duì)列使用與標(biāo)準(zhǔn)隊(duì)列相同的匹配條件,因此我們也可以使用默認(rèn)交換器.
          下面是創(chuàng)建回復(fù)隊(duì)列的偽代碼, 這里的S:表示一個服務(wù)器回復(fù):
          Queue.Declare
          queue=<empty>
          exclusive=TRUE
          S:Queue.Declare-Ok
          queue=tmp.1
          要發(fā)布到回復(fù)隊(duì)列,生產(chǎn)者需將消息發(fā)送到默認(rèn)交換器中:
          Basic.Publish
          exchange=<empty>
          routing-key=tmp.1
          有一個標(biāo)準(zhǔn)消息屬性-Reply-To, 它專門設(shè)置用來攜帶回復(fù)隊(duì)列的名稱.
          2.1.5.3 構(gòu)建Pub-Sub 訂閱隊(duì)列
          在經(jīng)典中間件中,術(shù)語"訂閱(subscription)" 在概念上是模糊的,它至少涉及了兩個不同的概念: 匹配消息的條件和用于保存匹配消息的臨時隊(duì)列. AMQP把這個工作分成了綁定和消息隊(duì)列兩個部分.在AMQP中沒有稱為訂閱的實(shí)體.
          讓我們來描述pub-sub訂閱:
           為單個消費(fèi)者(或某些情況下,多個消費(fèi)者)保存消息
           通過一系列匹配主題,消息字段或內(nèi)容的不同綁定方式來從多個源中收集消息
          訂閱隊(duì)列與命名隊(duì)列或回復(fù)隊(duì)列之間的關(guān)鍵區(qū)別是訂閱隊(duì)列名稱與路由目的無關(guān),路由是通過抽象匹配條件來完成的,而不是1對1路由鍵字段匹配來完成的.
          我們以常見的主題樹pub-sub模型進(jìn)行講解并對其實(shí)現(xiàn). 我們需要一個能夠在主題樹上匹配的交換器類型. 在AMQP中,這是"topic" 交換器類型. topic交換器會匹配類似"STOCK.USD.*"通配符, 如像"STOCK.USD.NYSE"這樣的路由鍵.
          我們不能使用默認(rèn)的交換器或綁定,因?yàn)樗鼈儾粫鰐opic風(fēng)格的路由. 因此我們必明確地創(chuàng)建一個綁定.以下是創(chuàng)建和綁定pub-sub訂閱隊(duì)列的偽代碼:
          Queue.Declare
          queue=<empty>
          exclusive=TRUE
          S:Queue.Declare-Ok
          queue=tmp.2
          Queue.Bind
          queue=tmp.2
          TO exchange=amq.topic
          WHERE routing-key=STOCK.USD.*
          要從訂閱隊(duì)列中消費(fèi)消息,消費(fèi)者需要這樣做:
          Basic.Consume
          queue=tmp.2
          當(dāng)發(fā)布消息時,生產(chǎn)者可以這樣做:
          Basic.Publish
          exchange=amq.topic
          routing-key=STOCK.USD.ACME
          topic交換器會使用其它綁定表處理傳入的路由鍵("STOCK.USD.ACME"),并會找到一個匹配項(xiàng)tmp.2.然后它會把消息路由到那個訂閱隊(duì)列上.
          2.2 AMQP 命令架構(gòu)
          本章節(jié)解釋了應(yīng)用程序如何與服務(wù)器對話.
          2.2.1 協(xié)議命令 (類&方法)
          中間件是復(fù)雜的,我們在設(shè)計協(xié)議結(jié)構(gòu)的挑戰(zhàn)是要馴服其復(fù)雜性。
          我們的方法是基于類來建立傳統(tǒng)API模型,這個類中包含方法,并定義了方法明確應(yīng)該做什么.
          這會導(dǎo)致大量的命令集合,但一個命令應(yīng)該相對容易理解.
          AMQP命令組合在類中.每個類都覆蓋了一個特定功能領(lǐng)域.有此類是可選的 -每個節(jié)點(diǎn)都實(shí)現(xiàn)了需要支持的類.
          有兩種不同方法對話:
           同步請求-響應(yīng),在其中一個節(jié)點(diǎn)發(fā)送請求,另一個節(jié)點(diǎn)發(fā)送回復(fù).
          同步請求和響應(yīng)適用于性能不是關(guān)鍵的地方.
           異步通知, 在其中,一個節(jié)點(diǎn)發(fā)送消息但不希望得到回復(fù).異步方法適用于性能是至關(guān)重要的地方.
          為使處理方法簡單,我們?yōu)槊總€異步請求定義了不同的回復(fù). 也就是說,沒有哪個方法可作為兩個不同請求的回復(fù).這意味著一個節(jié)點(diǎn),發(fā)送一個同步請求后,可以接受和處理傳入的方法,直到得到一個有效的同步答復(fù). 這使得AMQP與更加傳統(tǒng)的RPC協(xié)議是有區(qū)別的.
          方法可以形式上定義為同步請求,同步回復(fù)(針對特定請求),或者是異步的. 最后,每個方法都形貌地定義為客戶端(即. 服務(wù)器到客戶端),或服務(wù)端(客戶端到服務(wù)器).
          2.2.2 映射AMQP到中間件API
          我們已經(jīng)設(shè)計AMQP是可映射到中間件的API.這種映射有一些是智能的(不是所有方法, 也不是所有參數(shù)對應(yīng)用程序來說都是有意義的),也有一些是機(jī)械的(給定一些規(guī)則,所有方法都可以在無人工干預(yù)的情況下映射).
          這么做的優(yōu)勢是對于那些了解AMQP語義(本章描述的類)的開發(fā)者會在它們使用的環(huán)境中找到相同的語義.
          例如,下面是Queue.Declare 方法示例:
          Queue.Declare
          queue=my.queue
          auto-delete=TRUE
          exclusive=FALSE
          這會轉(zhuǎn)換為線路級幀(wire-level frame):

          或者更高級的API:
          映射為異步方法的偽代碼邏輯是:



          映射為同步方法的偽代碼邏輯是:



          值得一提的是,對于大部分應(yīng)用程序, 中間件可以完全隱藏在技術(shù)層面中, 而且實(shí)際API使用的影響會小于中間件的健壯性和能力性.
          2.2.3 無確認(rèn)
          一個聊天式協(xié)議(chatty protocol)是很慢的. 如果在這些情況中,性能是很嚴(yán)重的,我們會使用異步.
          一般我們從一個節(jié)點(diǎn)發(fā)送消息到另一個節(jié)點(diǎn). 我們會使發(fā)送方法盡可能地快,而不用等待確認(rèn).在必要時,我們可以以較高級別來實(shí)現(xiàn)窗口和限制,如以消費(fèi)者水平.
          我們免除了確認(rèn),因?yàn)槲覀優(yōu)樗胁僮魇褂昧藬嘌阅P? 要么它們成功,要么就是我們有關(guān)閉通道或連接的異常.
          AMQP中可以沒有確認(rèn).成功是寂靜的,而失敗是喧鬧的.當(dāng)應(yīng)用程序明確需要追蹤成功和失敗時,它們應(yīng)該使用事務(wù).
          2.2.4 Connection類
          AMQP是一個連接協(xié)議. 連接設(shè)計為長期的,且可運(yùn)載多個通道. 連接生命周期是這樣的:
           client打開與服務(wù)器的TCP/IP連接并發(fā)送一個協(xié)議頭(protocol header).這只是client發(fā)送的數(shù)據(jù),而不是作為方法格式的數(shù)據(jù).
           server使用其協(xié)議版本和其它屬性,包括它支持安全機(jī)制列表(Start方法)進(jìn)行響應(yīng).
           client選擇一種安全機(jī)制(Start-Ok).
           server開始認(rèn)證過程, 它使用SASL的質(zhì)詢-響應(yīng)模型(challenge-response model). 它向客戶端發(fā)送一個質(zhì)詢(Secure).
           client向server發(fā)送一個認(rèn)證響應(yīng)(Secure-Ok). 例如,對于使用"plain"機(jī)制,響應(yīng)會包含登錄用戶名和密碼.
          server 重復(fù)質(zhì)詢(Secure) 或轉(zhuǎn)到協(xié)商,發(fā)送一系列參數(shù),如最大幀大小(Tune).
           client接受或降低這些參數(shù)(Tune-Ok).
           client 正式打開連接并選擇一個虛擬主機(jī)(Open).
           服務(wù)器確認(rèn)虛擬主機(jī)是一個有效的選擇 (Open-Ok).
           客戶端現(xiàn)在使用希望的連接.
           一個節(jié)點(diǎn)(client 或 server) 結(jié)束連接(Close).
           另一個節(jié)點(diǎn)對連接結(jié)束握手(Close-Ok).
           server 和 client關(guān)閉它們的套接字連接.
          沒有為不完全打開的連接上的錯誤進(jìn)行握手. 根據(jù)成功協(xié)議頭協(xié)商(后面有詳細(xì)定義),在發(fā)送或收到Open 或Open-Ok之前,如果一個節(jié)點(diǎn)檢測到錯誤,這個節(jié)點(diǎn)必須關(guān)閉socket,而不需要發(fā)送任何進(jìn)一步的數(shù)據(jù)。
          2.2.5 Channel 類
          AMQP是一個多通道協(xié)議. 通道提供了一種方式來將一個重量級TCP/IP連接分成多個輕量級連接.
          這使得協(xié)議對于防火墻更加友好,因?yàn)槎丝谑褂檬强深A(yù)測的. 這也意味著傳輸調(diào)整和網(wǎng)絡(luò)服務(wù)質(zhì)量可以得到更好的利用.
          通道是獨(dú)立的,它們可以同時執(zhí)行不同的功能,可用帶寬會在當(dāng)前活動之間共享.
          這是令人期待的,我們鼓勵多線程客戶端應(yīng)用程序經(jīng)常使用"每個通道一個線程"編程模型.
          然而,從單個client打開一個或多個AMQP servers連接也是完全可以接受的.
          通道生命周期如下:
          1. client打開一個新通道(Open).
          2. server確認(rèn)新通道準(zhǔn)備就緒(Open-Ok).
          3. client和server按預(yù)期來使用通道.
          4. 一個節(jié)點(diǎn)(client或server) 關(guān)閉了通道(Close).
          5. 另一個節(jié)點(diǎn)對通道關(guān)閉進(jìn)行握手(Close-Ok).
          2.2.6 Exchange 類
          交換器類讓應(yīng)用程序來管理服務(wù)器上的交換器。這個類可以讓應(yīng)用程序腳本自己布線(而不是依賴于一些配置接口)。注:大多數(shù)應(yīng)用程序不需要這個級別的復(fù)雜度,傳統(tǒng)的中間件是不太可能能夠支持這種語義。
          交換器生命周期如下:
          1. client 請求server確保交換器是否存在(Declare). client可細(xì)化到,"如果交換器不存在則進(jìn)行創(chuàng)建",或 "如果交換器不存在,警告我,不需要創(chuàng)建".
          2. client發(fā)布消息到交換器.
          3. client可選擇刪除交換器(Delete).
          2.2.7 Queue 類
          queue類可讓應(yīng)用程序來管理服務(wù)器上的消息隊(duì)列. 在幾乎所有消費(fèi)消息的應(yīng)用程序中,這是基本步驟,至少要驗(yàn)證期望的消息隊(duì)列是否實(shí)際存在.
          持久化消息隊(duì)列的生命周期相當(dāng)簡單:
          1. client斷言消息隊(duì)列存在(Declare, 使用"passive"參數(shù)).
          2. server確認(rèn)消息隊(duì)列存在(Declare-Ok).
          3. client從消息隊(duì)列中讀取消息。
          臨時消息隊(duì)列的生命周期更加有趣:
          1. client創(chuàng)建消息隊(duì)列(Declare,不提供隊(duì)列名稱,服務(wù)器會分配一個名稱). server 確認(rèn)(Declare-Ok).
          2. client 在消息隊(duì)列上啟動一個消費(fèi)者. 消費(fèi)者的精確功能是由Basic類定義的。
          3. client 取消消費(fèi)者, 要么是顯示取消,要么是通過關(guān)閉通道/連接隱式取消的
          4. 當(dāng)最后一個消費(fèi)者從消息隊(duì)列中消失的時候,在過了禮貌性超時后,server會刪除消息隊(duì)列.
          AMQP 像消息隊(duì)列一樣為主題訂閱實(shí)現(xiàn)了分發(fā)機(jī)制. 這使結(jié)構(gòu)更多有趣,訂閱可以在合作訂閱應(yīng)用程序池中進(jìn)行負(fù)載均衡.
          訂閱生命周期涉及到額外的綁定階段:
          1. client 創(chuàng)建消息隊(duì)列(Declare),server進(jìn)行確認(rèn)(Declare-Ok).
          2. client 綁定消息隊(duì)列到一個topic交換器 (Bind),server進(jìn)行確認(rèn)(Bind-Ok).
          3. client像前面的例子來使用消息隊(duì)列.
          2.2.8 Basic 類
          Basic 類實(shí)現(xiàn)本規(guī)范中描述的消息功能.它支持如下主要語義:
           從client發(fā)送消息給server, 異步發(fā)生(Publish)
           啟動和停止消費(fèi)者(Consume, Cancel)
           從server發(fā)送消息給client, 異步發(fā)生(Deliver, Return)
           應(yīng)答消息(Ack, Reject)
           同步從消息隊(duì)列中取消息 (Get).
          2.2.9 Transaction 類
          AMQP 支持兩種類型的事務(wù):
          1. 自動事務(wù): 每個發(fā)布的消息和應(yīng)答都處理為獨(dú)立事務(wù).
          2. Server 本地事務(wù), 服務(wù)器會緩存發(fā)布的消息和應(yīng)答,并會根據(jù)需要由client來提交它們.
          Transaction 類(“tx”) 使應(yīng)用程序可訪問第二種類型,即服務(wù)器事務(wù)。這個類的語義是:
          1. 應(yīng)用程序要求在每個通道中都有事務(wù)(Select).
          2. 應(yīng)用程序做一些工作(Publish, Ack).
          3. 應(yīng)用程序提交或回滾工作(Commit, Roll-back).
          4. 應(yīng)用程序做一些工作,循環(huán)往復(fù)。
          事務(wù)能覆蓋發(fā)布內(nèi)容和應(yīng)答,但不能覆蓋投遞(deliveries). 因此回滾不能導(dǎo)致消息重新入隊(duì)或者重新投遞, 客戶端有權(quán)在事務(wù)中確認(rèn)這些消息。
          2.3 AMQP 傳輸架構(gòu)
          這個章節(jié)解釋了命令是如何映射到線路協(xié)議的.
          2.3.1 一般描述
          AMQP是二進(jìn)制協(xié)議. 信息被組織成各種類型的幀(frames). Frames可以攜帶協(xié)議方法和其它信息.所有 幀(frames)都有同樣的格式: 幀頭(frame header),幀負(fù)載(frame payload)和幀尾(frame end).幀負(fù)載( frame payload)的格式依賴于幀類型(frame type).
          我們假設(shè)有一個可靠的面向流的網(wǎng)絡(luò)傳輸層(TCP/IP或相當(dāng)?shù)?.
          在單個套接字連接中,可以存在多個獨(dú)立控制線程,它們被稱為通道.
          每個幀都使用通道編號來編號.通過交織它們的幀,不同的通道共享連接。對于任何給定的通道,幀運(yùn)行在一個嚴(yán)格的序列,這樣可以用來驅(qū)動一個協(xié)議解析器(通常是一個狀態(tài)機(jī)).
          我們使用一組小的數(shù)據(jù)類型,如位,整數(shù),字符串和字段表來構(gòu)造幀。幀字段是緊密包裝的,不會使得它們緩慢或解析復(fù)雜。從協(xié)議規(guī)范中生成框架層是相對簡單的。
          線路級格式的設(shè)計是可擴(kuò)展性,一般可以用于任意的高層協(xié)議(不只是AMQP)。我們假設(shè)AMQP將來會擴(kuò)展、改進(jìn),隨時間的推移線路級格式仍然會得到支持。
          2.3.2 數(shù)據(jù)類型
          AMQP數(shù)據(jù)類型用于方法幀中,它們是:
           Integers ( 1到8個字節(jié)),用來表示大小,數(shù)量,范圍等等. Integers通常是無符號的,在幀中可能是未對齊的.
           Bits,用來表示開/關(guān)值.位被包裝成字節(jié)。
           短字符串(short string),用來保存短的文本屬性.短字符串限制為255個字節(jié),可以在無緩沖區(qū)溢出的情況下進(jìn)行解析.
           長字符串(long string),用來保存二進(jìn)制數(shù)據(jù)塊.
           字段表(Field tables),用來保存名稱-值對(name-value pairs). 字段值類型可以是字符串,整數(shù)等等.
          2.3.3 協(xié)議協(xié)商
          AMQP client 和server 可對協(xié)議進(jìn)行協(xié)商.這就是說當(dāng)client連接時,server可處理client能接受或修改的操作.當(dāng)兩個點(diǎn)對結(jié)果達(dá)成一致時, 連接會繼續(xù)前行.協(xié)商是一種有用的技術(shù),因?yàn)樗屛覀兛梢詳嘌约僭O(shè)和前提條件。在AMQP,我們協(xié)商協(xié)議的下面方面:
           實(shí)現(xiàn)協(xié)議和版本. server 可在同一個端口上保存多個協(xié)議.
           加密參數(shù)和兩者之間的認(rèn)證.這是功能層的一部分,以前解釋過。
           最大幀大小,通道數(shù)量,以及其它操作限制.
          達(dá)成一致的限制可能會使兩者重新分配關(guān)鍵緩存區(qū)以避免死鎖.每個傳入的幀要么服從達(dá)成的限制(這是安全的),或者超過它們(在這種情況下,另一方必須斷開連接).這非常符合"它要么工作,要么就完全不工作"的AMQP哲學(xué).
          兩個節(jié)點(diǎn)達(dá)成一致的最低限度為:
           服務(wù)器必須告訴客戶端它提出了什么限制。
           客戶端進(jìn)行響應(yīng),并可能減少其連接的限制。
          2.3.4 限制幀
          TCP/IP是一個流協(xié)議,即沒有限制幀的內(nèi)建機(jī)制. 現(xiàn)有協(xié)議可以幾種不同的方式解決這個問題:
           每個連接中只發(fā)送單個幀.這很簡單,但很慢.
           在流中添加幀定界符.這很簡單,但解析較慢.
           計算幀的大小, 并在每個幀的前面發(fā)送大小。這是簡單和快速,和我們的選擇.
          2.3.5 幀細(xì)節(jié)
          所有的幀都由一個頭(header,7個字節(jié)),任意大小的負(fù)載(payload),和一個檢測錯誤的幀結(jié)束(frame-end)字節(jié)組成:

          要讀取一個幀,我們必須:
          1. 讀取header,檢查幀類型(frame type)和通道(channel).
          2. 根據(jù)幀類型,我們讀取負(fù)載并進(jìn)行處理.
          3. 讀取幀結(jié)束字節(jié).
          在實(shí)際實(shí)現(xiàn)中,如果性能很關(guān)鍵的話,我們應(yīng)該使用讀前緩沖(read-ahead buffering)”或“收集讀取(gathering reads)”,以避免為了讀一個幀而做三次獨(dú)立的系統(tǒng)調(diào)用。
          2.3.5.1 方法幀
          方法幀可以攜帶高級協(xié)議命令(我們稱之為方法(methods)).一個方法幀攜帶一個命令. 方法幀負(fù)載有下面的格式:



          要處理一個方法幀,我們必須:
          1. 讀取方法幀負(fù)載.
          2. 將其拆包成結(jié)構(gòu). 方法通常有相同的結(jié)構(gòu),因此我們可以快速對方法進(jìn)行拆包.
          3. 檢查在當(dāng)前上下文中是否允許出現(xiàn)方法.
          4. 檢查方法參數(shù)是否有效.
          5.執(zhí)行方法.
          方法主體(bodies) 由AMQP數(shù)據(jù)字段(位,整數(shù), 字符串和字符串表組成)構(gòu)成. 編組代碼直接從協(xié)議規(guī)范中生成,因此是非常快速地.
          2.3.5.2 內(nèi)容幀
          內(nèi)容是我們通常AMQP服務(wù)器在客戶端與客戶端之間傳送和應(yīng)用數(shù)據(jù). 粗略地說,內(nèi)容是由一組屬性加上一個二進(jìn)制數(shù)據(jù)部分組成的。它所允許的屬性集合由Basic類定義,而這些屬性的形式為內(nèi)容頭幀(content header frame)。其數(shù)據(jù)可以是任何大小,也有可能被分解成幾個(或多個)塊,每一個都有內(nèi)容體幀(content body frame)。
          看一個特定通道的幀,當(dāng)它們在線路上傳輸時,我們可能會看到下面這樣的東西:


          某些方法(如Basic.Publish, Basic.Deliver等等.)通常情況下定義為傳輸內(nèi)容.
          當(dāng)一個節(jié)點(diǎn)發(fā)送像這樣的方法幀時,它總是會遵循一個內(nèi)容頭幀(conent header frame)和零個或多個內(nèi)容體幀(content body frame)的形式.
          一個內(nèi)容頭幀有下面的格式:
          某些方法(如Basic.Publish, Basic.Deliver等等.)通常情況下定義為傳輸內(nèi)容.
          當(dāng)一個節(jié)點(diǎn)發(fā)送像這樣的方法幀時,它總是會遵循一個內(nèi)容頭幀(conent header frame)和零個或多個內(nèi)容體幀(content body frame)的形式.
          一個內(nèi)容頭幀有下面的格式:

          我們將內(nèi)容體放置在不同的幀中(并不包含在方法中),因此AMQP可支持零拷貝技術(shù),這樣其內(nèi)容就不需要編組或編碼. 我們將內(nèi)容屬性安放在它們自己的幀中,以便收件人可以有選擇地丟棄他們不想處理的內(nèi)容。
          2.3.5.3 心跳幀
          心跳是一種設(shè)計用來撤銷(undo)TCP/IP功能的技術(shù),也就是說在長時間超時后,它有能力通過關(guān)閉broker物理連接來進(jìn)行恢復(fù).在某些情景下,我們需要快速知道節(jié)點(diǎn)連接是否斷開了,或者是由于什么原因不能響應(yīng)了.因?yàn)樾奶梢栽谳^低水平上進(jìn)行,我們在傳輸層次上按節(jié)點(diǎn)交換的特定幀類型來處理,而不是按類方法.
          2.3.6 錯誤處理
          AMQP使用異常來處理錯誤.任何操作錯誤(未找到消息隊(duì)列,訪問權(quán)限不足)都會導(dǎo)致一個通道異常. 任何結(jié)構(gòu)化的錯誤(無效參數(shù),壞序列的方法.)都會導(dǎo)致一個連接異常.異常會關(guān)閉通道或連接,同時也會向客戶端應(yīng)用返回響應(yīng)碼和響應(yīng)文本.我們使用了類似于HTTP等協(xié)議和其它大多數(shù)協(xié)議中的三位回復(fù)代碼和文字回復(fù)文本方案.
          2.3.7 關(guān)閉通道和連接
          連接或通道,對于客戶端來說,當(dāng)其發(fā)送Open時則被認(rèn)為是“打開”的,對于服務(wù)器端來說,當(dāng)其發(fā)送Open-Ok時則被認(rèn)為是打開的。基于這一點(diǎn),一個希望關(guān)閉通道或連接的對等體也必須使用握手協(xié)議來這樣做。
          可出于任何原因,可能會正常地或異常地關(guān)閉一個通道或連接-因此必須仔細(xì)小心。
          對于突然或意外關(guān)閉,并不能得到快速探測,因此當(dāng)發(fā)生異常時,我們可能會丟失錯誤回復(fù)代碼。
          正確的設(shè)計是對于所有關(guān)閉必須進(jìn)行握手,使我們關(guān)閉后對方知道相應(yīng)的情況。
          當(dāng)一個節(jié)點(diǎn)決定關(guān)閉一個通道或連接時,它發(fā)送一個Close方法。接收節(jié)點(diǎn)必須使用Close-Ok來響應(yīng)Close,然后雙方可以關(guān)閉他們的通道或連接。請注意,如果節(jié)點(diǎn)忽略了關(guān)閉,當(dāng)兩個節(jié)點(diǎn)同時發(fā)送Close時,可能會發(fā)生死鎖。
          2.4 AMQP Client 架構(gòu)
          可直接從應(yīng)用程序中讀寫AMQP幀,但這是相當(dāng)糟糕的設(shè)計.
          即使是最簡單的對話框也比較復(fù)雜(比如同HTTP比較),應(yīng)用程序開發(fā)者沒必要為了向消息隊(duì)列發(fā)送消息, 而來理解二進(jìn)制這樣的東西. 推薦的AMQP client架構(gòu)須由下面的多個抽象層組成:
          1. 幀層. 此層接受AMQP協(xié)議方法,并按某種語言格式(結(jié)構(gòu),類等等) 來序列化成線路級幀.幀層可以根據(jù)AMQP規(guī)范機(jī)械產(chǎn)生(這是在一個協(xié)議的建模語言,專為AMQP定義了XML實(shí)現(xiàn)).
          2. 連接管理層. 此層用于讀寫AMQP幀,并管理所有連接,會話邏輯.在此層中,我們可以封裝打開連接和會話,錯誤處理,內(nèi)容傳輸和接收的全部邏輯. 此層的大部分都可通過AMQP規(guī)范來生成.例如,規(guī)范定義了哪些方法可以攜帶內(nèi)容, 因?yàn)檫壿嫲l(fā)送方法和可選的發(fā)送內(nèi)容可以機(jī)械的生成.
          3. API 層. 此層暴露了應(yīng)用程序工作的特定API. API層可能會反映一些現(xiàn)有的標(biāo)準(zhǔn),或暴露高層AMQP的方法,或?qū)Ρ竟?jié)前面介紹的內(nèi)容做一個映射。AMQP方法設(shè)計為使這些映射簡單有用。API層本身可能是由多個層組成的,如.構(gòu)建于AMQP方法API之上的高級API.
          此外,通常還會有一些I / O層,這此可以是非常簡單的(同步套接字讀取和寫入)或復(fù)雜的(完全異步多線程I / O)。此圖顯示了整體推薦的架構(gòu):

          在本文檔中,當(dāng)我們說"client API"的時候,我們指的則是應(yīng)用程序下的所有層(i/o,幀,連接按理和API層).我們通常將客戶端API和應(yīng)用程序分開說, 在這里,應(yīng)用程序會使用客戶端API來同中間件服務(wù)器進(jìn)行對話.
          3 功能說明
          3.1 Server 功能說明
          3.1.1 消息和內(nèi)容
          消息是中間件路由和隊(duì)列系統(tǒng)處理的原子單元。消息可攜帶一份內(nèi)容,它包括一個內(nèi)容頭,一組屬性,和一個內(nèi)容體,和持有一個不透明的二進(jìn)制數(shù)據(jù)塊。
          一個消息可以對應(yīng)到許多不同應(yīng)用程序的實(shí)體:
           一個應(yīng)用程序級消息
           一個傳輸文件
           一個數(shù)據(jù)流幀等等.
          消息可以持久化.一個持久化消息可以安全地存儲在磁盤上,即使是在嚴(yán)重的網(wǎng)絡(luò)故障,服務(wù)器崩潰、溢出等情況下也可確保投遞.消息也可以有優(yōu)先級.高優(yōu)先級消息會在等待同一個消息隊(duì)列時,在低優(yōu)先級消息之前發(fā)送. 當(dāng)消息必須被丟棄以確保服務(wù)器質(zhì)量水平,將會優(yōu)先丟棄低優(yōu)先級消息.
          服務(wù)器不能修改接收到并將傳遞給消費(fèi)者應(yīng)用程序的消息內(nèi)容體. 服務(wù)器可在內(nèi)容頭中添加額外信息,但不能刪除或修改現(xiàn)有信息.
          3.1.2 虛擬主機(jī)(Virtual Hosts)
          虛擬主機(jī)是服務(wù)器內(nèi)的數(shù)據(jù)分區(qū), 它為在共享基礎(chǔ)設(shè)施上的管理帶來了方便.
          一個虛擬主機(jī)包括其命名空間,一組交換器,消息隊(duì)列以及所有相關(guān)對象. 每個連接必須關(guān)聯(lián)一個單個虛擬主機(jī).
          在認(rèn)證后,客戶端可在Connection.Open方法中選擇虛擬主機(jī). 這意味著,服務(wù)器上的認(rèn)證方案可在此服務(wù)器上的所有虛擬主機(jī)上共享. 然而,對于每個虛擬主機(jī)來說,也可以獨(dú)特的認(rèn)證方案. 對于每個虛擬主機(jī)需要不同的身份驗(yàn)證方案的管理員應(yīng)該使用單獨(dú)的服務(wù)器。
          連接中的所有通道都在同一個虛擬主機(jī)上工作.在同一個連接中,沒有與不同虛擬主機(jī)通信的方式, 也沒有在不斷開連接重新開始的情況下,切換到其它虛擬主機(jī)的可能性.
          該協(xié)議沒有提供用于創(chuàng)建或配置虛擬主機(jī)的機(jī)制-這在服務(wù)器內(nèi)是一個不確定的方式,是完全依賴于實(shí)現(xiàn)的。
          3.1.3 交換器
          交換器是一個虛擬主機(jī)內(nèi)的消息路由代理。交換器實(shí)例(我們通常稱之為“交換器”)接受消息和路由信息-主要是一個路由鍵-或者將消息傳遞到消息隊(duì)列,或到內(nèi)部服務(wù)。交換器是基于每個虛擬主機(jī)命名的。
          應(yīng)用程序可以在權(quán)限范圍內(nèi)自由地創(chuàng)建、共享、使用和銷毀交換器實(shí)例.交換器可能是持久的、臨時的或自動刪除的。持久化的交換器會持續(xù)到他們被刪除,臨時的交換器會持續(xù)到服務(wù)器關(guān)閉。自動刪除的交換器直到他們不再使用。服務(wù)器提供了一組特定的交換器類型。每個交換器類型都實(shí)現(xiàn)了一個特定的匹配和算法,如下一節(jié)中定義的。AMQP只要求少量的交換器類型,并推薦了一些。此外,每個服務(wù)器實(shí)現(xiàn)可以添加自己的交換類型。
          交換器可以將單個消息并發(fā)地路由到的消息隊(duì)列中。這將創(chuàng)建一個獨(dú)立消息的多個實(shí)例。
          3.1.3.1 Direct交換器類型
          direct 交換器按如下方式來工作:
          1. 消息隊(duì)列使用路由鍵K來綁定交換器.
          2. 發(fā)布者使用路由鍵R來向交換器發(fā)送消息.
          3. 在K=R時,消息會傳遞到消息隊(duì)列中.
          server必須實(shí)現(xiàn)direct交換器,并且在每個虛擬主機(jī)中必須預(yù)定義兩個direct交換器: 一個名為 amq.direct, 另一個無公共名稱(為Publish方法的默認(rèn)交換器).
          注意,消息隊(duì)列可以使用任何有效的路由鍵值進(jìn)行綁定,但通常消息隊(duì)列使用它們自己的名稱作路由鍵來綁定.
          事實(shí)上,所有消息隊(duì)列必須能使用其自身隊(duì)列名稱作路由鍵自動綁定無名稱的交換器上.
          3.1.3.2 Fanout 交換器類型
          fanout交換器類型按如下方式來工作:
          1. 消息隊(duì)列不使用參數(shù)來綁定交換器.
          2. 發(fā)布者向交換器發(fā)送消息.
          3. 消息無條件傳遞給消息隊(duì)列。
          fanout 交換器是微不足道的設(shè)計與實(shí)現(xiàn).此交換器類型和預(yù)聲明的交換器稱為amq.fanout,它是強(qiáng)制的.
          3.1.3.3 Topic交換器類型
          topic交換器類型按如下方式來工作:
          1. 消息隊(duì)列使用路由模式P來綁定到交換器.
          2. 發(fā)布者使用路由鍵R來向交換器發(fā)送消息.
          3. 當(dāng)R匹配P時,消息將被傳遞到消息隊(duì)列.
          用于topic交換器的路由鍵必須由0個或多個由點(diǎn)號
          用于topic交換器的路由鍵必須由點(diǎn)分隔的零或多個單詞組成.每個單詞必須包含字母A-Z和a-z 以及數(shù)字0-9.
          路由模式與路由鍵遵循相同的規(guī)則,* 用于匹配單個單詞,# 用于匹配0個或多個單詞.因此路由模式*.stock.# 會匹配路由鍵usd.stock 和eur.stock.db 但不匹配stock.nasdaq.
          對于topic交換器我們建議的設(shè)計是保持所有已知路由鍵的集合,當(dāng)發(fā)布者使用了新的路由鍵時,才更新此集合. 通過給定一個路由鍵來確實(shí)所有綁定是可能的,因此可為消息快速找到消息隊(duì)列. 此交換器類型是可選的.
          server應(yīng)該實(shí)現(xiàn)topic交換器類型,在這種情況下,server 必須在每個虛擬主機(jī)中預(yù)先定義至少一個 topic交換器,其名稱為amq.topic.
          3.1.3.4 Headers交換器類型
          headers交換器類型按如下方式進(jìn)行工作:
          1. 消息隊(duì)列使用包含匹配綁定和帶有默認(rèn)值的header參數(shù)表來綁定交換器.在這種交換器類型中,不使用路由鍵.
          2.發(fā)布者向交換器發(fā)送消息,這些消息的headers屬性中包含名稱-值對的表.
          3.如果消息頭屬性與隊(duì)列綁定的參數(shù)相匹配,則消息傳遞給隊(duì)列。
          匹配算法是由參數(shù)表中的名稱值對這樣的特殊綁定參數(shù)來控制的. 這個參數(shù)的名稱是'x-match'.
          它可以接受兩種值, 以表示表格中其它的名稱值對將如何來進(jìn)行匹配:
           'all' 則表明所有其它的名稱值對必須與路由消息的頭屬性相匹配(即.AND匹配)
           'any' 則表明只要消息頭屬性中的任何一個字段匹配參數(shù)表中的字段,則消息就應(yīng)該被路由(即. OR匹配).
          綁定參數(shù)中的字段必須與消息字段中的字段相匹配,這些情況包括:如果綁定參數(shù)中的字段沒有值且在消息頭中存在相同名稱的字段,或者綁定參數(shù)中的字段有值,且消息屬性中存在同樣的字段且有相同的值。
          任何以'x-'而不是'x-match'開頭的字段為將來保留使用并會被忽略.
          server應(yīng)該實(shí)現(xiàn)headers交換器類型, 且server必須在每個虛擬主機(jī)中預(yù)先聲明至少一個headers交換器,且名稱為amq.match.
          3.1.3.5 System交換器類型
          system交換器類型按如下方式進(jìn)行工作:
          1. 發(fā)布者使用路由鍵S來向交換器發(fā)送消息.
          2. system交換器將其傳遞給系統(tǒng)服務(wù)S.
          系統(tǒng)服務(wù)以"amq."開頭,為AMQP保留使用. 在服務(wù)器環(huán)境中,所有其它名稱可自由使用. 此交換器類型是可選的.
          3.1.3.6 實(shí)現(xiàn)定義的交換器類型
          所有非規(guī)范交換器類型必須以"x-"開頭. 不以"x-"開頭的交換器作為將來AMQP標(biāo)準(zhǔn)保留使用.
          3.1.4 消息隊(duì)列
          消息隊(duì)列是一個名為FIFO的緩沖區(qū)且為一組消費(fèi)者應(yīng)用程序保存消息.
          在其權(quán)限范圍內(nèi),應(yīng)用程序可以自由地創(chuàng)建、共享、使用和銷毀消息隊(duì)列.
          注意,在一個隊(duì)列中可能存在多個讀者,或存在客戶端事務(wù),或存在使用了優(yōu)先級字段,或存在使用了消息選擇器,或特定實(shí)現(xiàn)了投遞優(yōu)化的隊(duì)列可能不會真正地展現(xiàn)出FIFO特性. 唯一可以確保FIFO的方式是只有一個消費(fèi)者連上了隊(duì)列.在那些情況下,隊(duì)列可描述為弱-FIFO.
          消息隊(duì)列可能是持久化的或自動刪除的.持久化消息隊(duì)列會持續(xù)到它們刪除時為止. 臨時消息隊(duì)列可持續(xù)到服務(wù)器關(guān)閉時為止.自動刪除消息隊(duì)列可持續(xù)到它們不再使用時為止.
          Message隊(duì)列可將消息存儲在內(nèi)存,磁盤,或兩者的組合中.消息隊(duì)列是基于虛擬主機(jī)來命名的.
          消息隊(duì)列保存信息,并可在一個或多個消費(fèi)客戶端之間進(jìn)行分發(fā).路由到消息隊(duì)列中的消息不能再發(fā)給多個客戶端,除非在失敗或拒絕后進(jìn)行重發(fā).
          單個消息隊(duì)列可在同個時間可獨(dú)立地持有不同類型的內(nèi)容.也就是,如果Basic和文件內(nèi)容都發(fā)給了同一個消息隊(duì)列,這些將會作為請求獨(dú)立地分發(fā)給消費(fèi)應(yīng)用程序.
          3.1.5 綁定
          綁定是消息隊(duì)列和交換器之間的關(guān)系.綁定特有的路由參數(shù)將告訴交換器那些隊(duì)列應(yīng)該得到消息. 應(yīng)用程序可根據(jù)需要來驅(qū)動消息流向它們的消息隊(duì)列. 綁定的壽命依賴于定義它們的消息隊(duì)列 - 當(dāng)消息隊(duì)列被銷毀時,其綁定也會被銷毀.Queue.Bind 方法的特定語義將依賴于交換器類型.
          3.1.6 消費(fèi)者
          我們使用術(shù)語"consumer"來表示應(yīng)用程序和控制客戶端程序來接收消息隊(duì)列中的實(shí)體.當(dāng)客戶端啟動一個消費(fèi)者,它就在服務(wù)器中創(chuàng)建了一個消費(fèi)實(shí)體 .當(dāng)客戶端退出一個消費(fèi)者時,它就銷毀了一個服務(wù)器中的消費(fèi)者實(shí)體. 屬于單個客戶端通道的消費(fèi)者可異步地將消息發(fā)送到隊(duì)列中.
          3.1.7 服務(wù)質(zhì)量
          服務(wù)質(zhì)量控制了消息發(fā)送的速度. 服務(wù)質(zhì)量依賴于被分發(fā)的內(nèi)容類型.一般的服務(wù)質(zhì)量,在客戶端應(yīng)答消息前,會使用預(yù)提取的概念來指定發(fā)送多少個消息或多少個字節(jié)的數(shù)量. 目標(biāo)是提前發(fā)送消息數(shù)據(jù),以減少延遲。
          3.1.8 確認(rèn)/應(yīng)答
          應(yīng)答是從客戶端程序發(fā)出的正式信號,用以表示消息隊(duì)列中的消息已經(jīng)得到成功處理. 有兩種應(yīng)答模型:
          1. 自動地(Automatic), 在這種情況下,只要消息投遞到了應(yīng)用程序,服務(wù)器就會立即從消息隊(duì)列中刪除消息(通過 Deliver 或 Get-Ok 方法).
          2. 明確地(Explicit),在這種情況下,客戶端程序必須對每個消息發(fā)磅一個Ack方法以表示消息被處理了.客戶端層可以不同方式來實(shí)現(xiàn)明確應(yīng)答,如.只要收到了消息或當(dāng)應(yīng)用程序表示消息已經(jīng)處理了.
          這些區(qū)別不會影響AMQP或互操作性.
          3.1.9 流控制(Flow Control)
          流控制是一個用來中止節(jié)點(diǎn)消息流的緊急過程. 它在客戶端和服務(wù)器端都按同樣方式工作,且都是由Channel.Flow命令實(shí)現(xiàn)的. 流控制是唯一可以阻止一個過度生產(chǎn)發(fā)布者的機(jī)制.如果它使用消息確認(rèn)(這通常意味著使用事務(wù)), 消費(fèi)者則可以使用更優(yōu)雅的預(yù)取機(jī)制窗口。
          3.1.10 命名約定
          這些約定規(guī)范了AMQP實(shí)體命名. 服務(wù)器和客戶端必須遵守這些約定:
           用戶定義的交換器類型前輟必須是"x-"
           標(biāo)準(zhǔn)交換器實(shí)例前輟是"amq."
           標(biāo)準(zhǔn)系統(tǒng)服務(wù)前輟是"amq."
           標(biāo)準(zhǔn)消息隊(duì)列前輟是"amq."
           所有其他的交換器、系統(tǒng)服務(wù)和消息隊(duì)列名稱都在應(yīng)用程序空間中。
          3.2 AMQP 命令說明(Classes & Methods)
          3.2.1 解釋性注釋
          出于互操作原因,AMQP方法可以定義特定的最小值(如每消息隊(duì)列的消費(fèi)者數(shù)量)。這些極小值被定義在每個類的描述中。
          遵從AMQP的實(shí)現(xiàn)應(yīng)該為這些字段實(shí)現(xiàn)合理值, 最小值只用在最小能力的平臺上.
          語法使用這樣的標(biāo)記法:
           'S:' 指示從服務(wù)器發(fā)送到客戶端的數(shù)據(jù)或方法;
           'C:' 指示從客戶端發(fā)送到服務(wù)器的數(shù)據(jù)或方法;
           +term or +(...) 表達(dá)式表示1個或多個實(shí)例;
           *term or *(...) 表達(dá)式表示0個或多個實(shí)例.
          我們定義的方法是:
           一個同步請求("syn request").發(fā)送節(jié)點(diǎn)應(yīng)該等待特定的回復(fù)方法,但可以異步實(shí)現(xiàn)此方法;
          一個同步回復(fù)("syn reply for XYZ");
           一個異步請求或答復(fù) ("async").
          3.2.2 類和方法細(xì)節(jié)
          這部分是由生成的文件amqp-xml-spec.odt提供。
          4 技術(shù)說明
          4.1 IANA分配的端口號
          IANA為標(biāo)準(zhǔn)AMQP的TCP和UDP分配了5672端口。UDP端口被保留用于將來的組播實(shí)現(xiàn)。
          4.2 AMQP 線程級格式
          4.2.1 正式協(xié)議語法
          我們?yōu)锳MQP提供了一個完整語法(這只是AMQP提供的參考,跳到下一節(jié),你會發(fā)現(xiàn)不同的幀類型和格式):


          我們使用了IETF RFC 2234中定義的增強(qiáng)BNF語法. 總體而言,
           規(guī)則的名稱僅僅是名稱本身。
           終端是由一個或多個數(shù)字字符指定的,這些字符的基本解釋為“d”或“x”。
           通過列出一系列規(guī)則名稱,一個規(guī)則可以定義一個簡單的,有序的字符串的值.
           其他數(shù)值的范圍可以簡潔指定,使用破折號(“-”)來表示替代值的范圍。
           在圓括號中的元素被視為單個元素,其內(nèi)容是嚴(yán)格有序的。
           由/分隔的元素是可替代值.
           元素之間的操作符 "*"表示重復(fù).完整格式為: "<a>*<b>element",這里<a>的<b>是可選的十進(jìn)制值, 表示只能出現(xiàn)大于<a>而小于<b>的元素.
           規(guī)則形式: "<n>element" 等價于<n>*<n>element.
           方括號中的元素是可選元素.
          4.2.2 協(xié)議頭
          client必須通常發(fā)送一個協(xié)議頭開始新連接.它是8字節(jié)序列:

          協(xié)議頭由大寫字母"AMQP",其后跟常量%d0組成:
          1. 協(xié)議主版本號, 按照章節(jié)1.4.2中描述的使用.
          2. 協(xié)議次版本號, 按照章節(jié)1.4.2中描述的使用.
          3. 協(xié)議修訂版本, 按照章節(jié)1.4.2中描述的使用.
          該協(xié)議協(xié)商模型與現(xiàn)有HTTP協(xié)議兼容,使用常量文本字符串來發(fā)起連接, 并使用防火墻來檢測協(xié)議的開始以決定應(yīng)用什么規(guī)則.
          client和服務(wù)通過以下方式來達(dá)成協(xié)議版本一致:
           client打開一個到AMQP服務(wù)器的新socket連接,并發(fā)送協(xié)議頭.
           server可接受或拒絕協(xié)議頭.如果它拒絕了協(xié)議頭,它將會輸出一個有效的協(xié)議頭到socket,然后再關(guān)閉socket.
           否則它會同意(leaves)socket打開,并相應(yīng)地實(shí)現(xiàn)協(xié)議.
          示例:

          實(shí)現(xiàn)者指導(dǎo)方針:
           server可接受非AMQP協(xié)議,如HTTP.
           如果server無法識別socket數(shù)據(jù)中的前5個字節(jié),或者它不支持client請求的協(xié)議版本,它必須輸出一個有效的協(xié)議頭到socket,然后再關(guān)閉socket (必須確保client應(yīng)用程序能收到數(shù)據(jù)) ,最后再關(guān)閉socket連接.服務(wù)器可以打印診斷信息以輔助調(diào)試。
           client可使用服務(wù)器支持的最高版本來進(jìn)行檢測,如果收到了服務(wù)器發(fā)回的這種信息,就可使用較低版本來進(jìn)行重連
           實(shí)現(xiàn)了多版本AMQ的Clients和servers都應(yīng)該使用8字節(jié)的協(xié)議頭來標(biāo)識協(xié)議.
          4.2.3 通用幀格式
          所有幀都以7個字節(jié)的頭開始,其中包括一個type字段 ,一個channel字段和一個size字段:

          AMQP 定義了如下的幀類型:
           Type = 1, "METHOD": 方法幀
           Type = 2, "HEADER": 內(nèi)容頭幀
           Type = 3, "BODY": 內(nèi)容體幀.
           Type = 4, "HEARTBEAT": 心跳幀.
          通道編號為0的代表全局連接中的所有幀,1-65535代表特定通道的幀.
          size字段是負(fù)載的大小,不包括結(jié)束幀字節(jié). 由于AMQP假設(shè)是一個可靠的連接協(xié)議,我們使用結(jié)束幀來檢測錯誤客戶端和服務(wù)器實(shí)現(xiàn)引起的錯誤.
          實(shí)現(xiàn)者指導(dǎo)方針:
           結(jié)束幀必須是十六進(jìn)制值%xCE.
           如果一個節(jié)點(diǎn)收到了未定義類型的幀,它必須將其視為致命的協(xié)議錯誤,并關(guān)閉連接,而不進(jìn)一步地發(fā)送任何數(shù)據(jù)
           當(dāng)一個節(jié)點(diǎn)讀取到幀時,在解碼幀前,它必須檢查結(jié)束幀是否是有效的. 如果結(jié)束幀無效,它必須將其視為致使的協(xié)議錯誤,并關(guān)閉連接,而不進(jìn)一步地發(fā)送任何數(shù)據(jù). 它應(yīng)該記錄相關(guān)問題的日志信息,這樣就可以服務(wù)器或客戶端幀代碼實(shí)現(xiàn)中表示錯誤.
           節(jié)點(diǎn)發(fā)送的幀大小不能超過約定的大小. 節(jié)點(diǎn)收到超過大小的幀時,必須發(fā)出一個回復(fù)碼為501(幀錯誤)的連接異常信號.
           對于所有心跳幀,方法幀,連接類的頭和體,通道編號必須為0. 節(jié)點(diǎn)收到非0通道編號的這些幀必須使用回復(fù)碼503(無效命令)來發(fā)出異常信號.
          4.2.4 方法負(fù)載
          方法幀的體包括一個不可變的數(shù)據(jù)字段列表,稱為"arguments".所有方法體都以類型和方法的標(biāo)識符開始:

          實(shí)現(xiàn)者指導(dǎo)方針:
           class-id 和 method-id是由AMQP類和方法定義的常量.
           arguments 是特定于每個方法中的一組AMQP字段.
           Class id 中%x00.01-%xEF.FF范圍內(nèi)的值被AMQP標(biāo)準(zhǔn)類保留使用.
           Class id 中%xF0.00-%xFF.FF (%d61440-%d65535) 范圍內(nèi)的值可用于非標(biāo)準(zhǔn)擴(kuò)展類實(shí)現(xiàn).
          4.2.5 AMQP 數(shù)據(jù)字段
          AMQP有兩種級別的數(shù)據(jù)字段:用于方法參數(shù)的原生數(shù)據(jù)字段, 以及用于多個應(yīng)用之間傳遞數(shù)據(jù)的字段表. 字段表是原生數(shù)據(jù)字段的超集.
          4.2.5.1 Integers
          AMQP定義了這些原生整數(shù)類型:
           無符號字節(jié)(8 bits).
           無稱號短整形(16 bits).
           無符號長整形(32 bits).
           無符號長長整形(64 bits).
          整形和字符串長度總是無符號的,且按網(wǎng)絡(luò)字節(jié)順序保存. 當(dāng)存在兩個高低系統(tǒng)時(如.兩個Intel CPUS),我們不會對它們的交互嘗試優(yōu)化.
          實(shí)現(xiàn)方針:
           實(shí)現(xiàn)不能假設(shè)幀內(nèi)的整形編碼在內(nèi)存邊界中是對齊的.
          4.2.5.2 Bits
          AMQP定義了一個原生位字段類型. 位累積成整個字節(jié). 當(dāng)在幀中兩個或更多位相鄰時,它們會被包裝成一個或多個字節(jié),且在每個字節(jié)中以低位開始.
          沒有要求在一個幀中的所有位必須是連續(xù)的,但這通常是做,以盡量減少幀尺寸。
          4.2.5.3 Strings
          AMQP 字符串是可變長度,由一個整數(shù)長度后跟零個或多個字節(jié)數(shù)據(jù)表示. AMQP定義了兩種原生字符串類型:
           短字符串(Short strings),以8位無稱號整形長度后跟0個或多個字節(jié)數(shù)據(jù)存儲. 短字符串可攜帶最多255字節(jié)的UTF-8數(shù)據(jù), 但不能包含二進(jìn)制零字節(jié).
           長字符串(Long strings), 以32位無稱號整形長度后跟0個或多個字節(jié)數(shù)據(jù)存儲. 長字符串可包含任意數(shù)據(jù).
          4.2.5.4 時間戳(Timestamps)
          時間戳是以精度為1秒的64位POSIX time_t 格式保存的.使用64伴可以避免31位和32位相關(guān)的time_t值概括問題(wraparound issues).
          4.2.5.5 字段表
          字段表是包含名稱-值對的長字符串. 名稱-值對編碼為:以短字符串定義名稱,字節(jié)定義值類型和值. 有效的表字段類型是原生整形,位,字符串,時間戳類型的擴(kuò)展. 多字節(jié)整形字段通常是按網(wǎng)絡(luò)字節(jié)順序保存的.
          指導(dǎo)方針:
           字段名稱必須以字母開頭,其后可跟'$,'#',數(shù)字,下劃線,最大長度為128個字符.
           server應(yīng)該驗(yàn)證字段名稱,如果收到了無效的字段名稱,它應(yīng)該使用回復(fù)碼503(語法錯誤)來發(fā)出異常信號.
           十進(jìn)制值不用于支持浮點(diǎn)值,它是固定的業(yè)務(wù)值,如貨幣匯率和金額。其字節(jié)編碼代表了位置編號,其后跟著一個無符號的長整數(shù).“十進(jìn)制”是無符號的.
           重復(fù)字段是非法的。對于一個包含重復(fù)字段的表,其行為是未定義的。
          4.2.6 內(nèi)容幀
          某些特定的方法(Publish, Deliver, etc.) 會攜帶內(nèi)容.請參考 "Functional Specifications" 來了解每種方法的說明,以及它們是否是攜帶內(nèi)容的方法.
          內(nèi)容由1個或多個幀組成:
          1. 只有一個內(nèi)容頭幀能提供內(nèi)容屬性.
          2. 可選的, 可以有1個或多個內(nèi)容體幀.
          特定通道上的內(nèi)容幀是嚴(yán)格有序的. 也就是說,它們可以和其它通道的幀混合,但同一個通道內(nèi)兩個幀是不可能混合或重疊, 也不可能出現(xiàn)單個內(nèi)容上的內(nèi)容幀與相同通道上的方法幀相混合.
          注意,任何非內(nèi)容幀都會明確地標(biāo)識內(nèi)容的結(jié)束. 盡管可從內(nèi)容頭中知道內(nèi)容的大小,但也允許發(fā)送者在不關(guān)閉通道的情況下中止內(nèi)容發(fā)送.
          實(shí)現(xiàn)者指導(dǎo)方針:
           收到不完整或錯誤格式內(nèi)容的節(jié)點(diǎn)必須使用回復(fù)碼500(非希望幀)拋出一個連接異常. 這包括缺少內(nèi)容頭,內(nèi)容頭中錯誤的class IDs,缺少內(nèi)容體幀等等.
          4.2.6.1 內(nèi)容頭
          內(nèi)容頭負(fù)載有下面的格式:

          實(shí)現(xiàn)者指導(dǎo)方針:
           class-id必須與方法幀class id匹配. 節(jié)點(diǎn)必須對無效的class-id使用501回復(fù)碼(幀錯誤)拋出一個連接異常.
           weight字段未使用且必須是0.
           body大小是一個64位值,它定義了內(nèi)容體的總大小,也就是后面內(nèi)容體幀的body大小的總和. 0表示無內(nèi)容體幀.
           property flags是位數(shù)組,它表示每個屬性的存在性. 位是從最高到最低進(jìn)行排序的,位15代表第一個屬性.
           property flags可指定多于16屬性.如果最后位(0)被設(shè)置了,這表明其后有進(jìn)一步的屬性標(biāo)志字段。根據(jù)需要,這里有許多屬性標(biāo)志字段。
           屬性值是特定類的AMQP數(shù)據(jù)字段.
           位屬性僅由它們各自的屬性標(biāo)志(0或1)表示,并且在屬性列表中不存在。
           內(nèi)容幀中的通道編碼不能為0.在內(nèi)容幀中收到0通道編號的節(jié)點(diǎn)必須使用504回復(fù)碼(通道錯誤)來發(fā)出異常信號
          4.2.6.2 內(nèi)容體
          內(nèi)容體負(fù)載是是不透明的二進(jìn)制塊,其后跟著一個結(jié)束幀字節(jié):

          內(nèi)容體可以根據(jù)需要分成多個幀.幀負(fù)載的最大大小可在連接時,由兩端進(jìn)行協(xié)商.
          實(shí)現(xiàn)者指導(dǎo)方針:
           節(jié)點(diǎn)必須要能將分成多個幀的內(nèi)容體作為單一集合進(jìn)行存儲處理,要么分成更小的幀重新傳輸,要么 連接成單個塊分發(fā)給應(yīng)用程序.
          4.2.7 心跳幀
          心跳幀告訴收件人發(fā)件人仍然是活的. 在連接時,心跳幀的速率和時間都可以調(diào)整.
          實(shí)現(xiàn)者指導(dǎo)方針:
           心跳幀的通道編號必須為0. 收到無效心跳幀的節(jié)點(diǎn)需使用501回復(fù)碼(幀錯誤)來拋出異常.
           如果節(jié)點(diǎn)不支持心跳,它必須在不發(fā)出錯誤或失敗信號的情況下丟棄心跳幀.
           client收到Connection.Tune方法后,必須要開始發(fā)送心跳, 并在收到Connection.Open后,必須要開始監(jiān)控.server在收到Connection.Tune-Ok后,需要開始發(fā)送和監(jiān)控心跳.
           節(jié)點(diǎn)應(yīng)該盡最大努力按固定頻率來發(fā)送心跳. 心跳可在任何時候發(fā)送. 任何發(fā)送字節(jié)都可作為心跳的有效替代,因此當(dāng)超過固定頻率還沒有發(fā)送非AMQP心跳時,必須發(fā)送心跳.如果節(jié)點(diǎn)在兩個心跳間隔或更長時間內(nèi),未探測到傳入的心跳,它可在不遵循Connection.Close/Close-Ok握手的情況下,關(guān)閉連接,并記錄錯誤信息.
           心跳應(yīng)該具有持續(xù)性,除非socket連接已經(jīng)被關(guān)閉, 包括在Connection.Close/Close-Ok 握手期間或之后的時間.
          4.3 通道復(fù)用
          AMQP 允許節(jié)點(diǎn)創(chuàng)建多個獨(dú)立的控制線程.每個通道都可作為共享單個socket的虛擬連接:

          實(shí)現(xiàn)者指導(dǎo)方針:
           AMQP節(jié)點(diǎn)可支持多個通道.在連接協(xié)商期間,可定義最大通道數(shù)目,節(jié)點(diǎn)可協(xié)商這個數(shù)值為1.
           每個節(jié)點(diǎn)都應(yīng)該以公平的方式平衡所有打開通道的流量. 這種平衡可以每幀為基礎(chǔ),也可以以每個通道上的總交通流量為基礎(chǔ). 節(jié)點(diǎn)不應(yīng)該允許一個非常繁忙的通道讓一個不太繁忙的通道餓死.
          4.4 可見性保證
          服務(wù)器必須確保客戶端對服務(wù)器狀態(tài)的觀察是一致的。
          下面的示例說明了在這種情況下,客戶端的觀察方法:
           Client 1 和 Client 2 連上了同一個虛擬主機(jī)
           Client 1 聲明了一個隊(duì)列
           Client 1 收到了Declare.Ok回復(fù) (觀察”的一個例子)
           Client 1 將其告知了Client 2
           Client 2 對同一個隊(duì)列做了被動聲明
          可見性必須保證Client 2能看到隊(duì)列(在沒有刪除的情況下)
          4.5 通道關(guān)閉
          當(dāng)發(fā)生以下事件時,server會考慮通道已經(jīng)關(guān)閉了:
          1. 節(jié)點(diǎn)關(guān)閉了通道或其父連接使用了Close/Close-Ok握手.
          2. 節(jié)點(diǎn)在通道或父連接上拋出了異常.
          3.節(jié)點(diǎn)未使用 Close/Close-Ok握手關(guān)閉了父連接socket.
          當(dāng)服務(wù)器關(guān)閉通道時,通道上任何未應(yīng)答的消息將標(biāo)記為重新分發(fā).
          當(dāng)服務(wù)器關(guān)閉連接時,它會刪除連接所擁有的自動刪除信息.
          4.6 內(nèi)容同步
          在某些情況下,同步請求響應(yīng)方法會對同一個信道上的異步內(nèi)容傳遞產(chǎn)生影響,包括:
           Basic.Consume 和 Basic.Cancel 方法, 這些會啟動和停止消息隊(duì)列中的消息流.
           Basic.Recover 方法,它會要求服務(wù)器重新分發(fā)消息到通道.
           Queue.Bind, Queue.Unbind, 和Queue.Purge 方法, 它會影響消息進(jìn)入消息隊(duì)列.
          實(shí)現(xiàn)者指導(dǎo)方針:
           請求-響應(yīng)效果在response方法之前必須不可見,但在之后必須可見.
          4.7 內(nèi)容排序保證
          流經(jīng)通道的方法順序是穩(wěn)定的:方法按發(fā)送時的順序接收. 這是由AMQP使用的TCP/IP傳輸所保證的.
          此外,服務(wù)器也會按一種穩(wěn)定的方式來處理內(nèi)容.尤其是,經(jīng)過服務(wù)器中單個路徑的內(nèi)容會保持順序.
          對于設(shè)定了優(yōu)先級并經(jīng)過單個路徑內(nèi)容,我們定義了一個內(nèi)容處理路徑-由一個傳入通道,一個交換器,一個隊(duì)列和一個傳出通道組成.
          實(shí)現(xiàn)者指導(dǎo)方針:
           server必須保持流經(jīng)單個內(nèi)容處理路徑上的順序性,除非在Basic.Deliver或Basic.Get-Ok方法上設(shè)置了redelivered字段,可根據(jù)條件規(guī)則來設(shè)置字段.
          4.8 錯誤處理
          4.8.1 異常
          使用標(biāo)準(zhǔn)的異常編程模型, AMQP不會發(fā)出成功信號,只在失敗時才發(fā)出信號. AMQP定義了兩種異常級別:
          1. 通道異常.指那些關(guān)閉通道引起的錯誤.通道異常通常是因?yàn)檐涘e誤引起的,這些錯誤并不影響應(yīng)用程序的其它部分.
          2. 連接異常. 這些關(guān)閉socket連接的異常通常是因?yàn)橛插e誤造成的,如程序錯誤,錯誤配置或其他需要干預(yù)的情況。
          4.8.2 回復(fù)代碼格式
          AMQP 回復(fù)代碼按照 IETF RFC 2821的回復(fù)代碼的嚴(yán)重程度和理論進(jìn)行定義.
          4.9 限制
          AMQP規(guī)范為將來的AMQP擴(kuò)展或同種線路級格式使用了如下限制:
           每個連接上的通道數(shù)量: 16位通道數(shù)量.
           協(xié)議類數(shù)量: 16位class id.
           每個協(xié)議內(nèi)的方法數(shù)量: 16位 method id.
          AMQP規(guī)范對于數(shù)據(jù)做了如下限制:
           短字符串的最大長度為: 255字節(jié).
           長字符串或字段表的最大長度: 32位大小.
           幀負(fù)載的最大大小: 32位大小
           內(nèi)容的最大長度: 64位大小.
          服務(wù)器或客戶端也可以對資源施加自己的限制,如并發(fā)連接的數(shù)量、每個通道的消費(fèi)者數(shù)量、隊(duì)列的數(shù)量等。這些不影響互操作性,因此并沒有指定。
          4.10 安全
          4.10.1 目標(biāo)和原則
          為了防止緩沖區(qū)溢出,我們在所有地方都使用特定長度的緩沖區(qū). 當(dāng)讀取數(shù)據(jù)時,所有數(shù)據(jù)都可以使用允許的最大長度來進(jìn)行驗(yàn)證.無效的數(shù)據(jù)可以被明確地處理,通過關(guān)閉通道或連接。
          4.10.2 拒絕服務(wù)攻擊
          AMQP 通過回復(fù)碼并關(guān)閉通道或連接來處理錯誤.這避免了錯誤出現(xiàn)后的模糊狀態(tài).在連接協(xié)商期間,服務(wù)器可假設(shè)特殊條件是因獲取訪問服務(wù)器的敵對嘗試所造成的.對于連接協(xié)商中的任何異常,一般處理是暫停該連接 (可能是一個線程)幾秒種時間,然后再關(guān)閉網(wǎng)絡(luò)連接. 這包括語法錯誤,過大數(shù)據(jù),或認(rèn)證失敗.服務(wù)器應(yīng)該記錄所有這些異常標(biāo)志或阻止客戶端挑起多個故障.
          posted on 2016-08-12 18:30 胡小軍 閱讀(10877) 評論(0)  編輯  收藏 所屬分類: RabbitMQ
          主站蜘蛛池模板: 中宁县| 宜宾县| 大荔县| 墨玉县| 南靖县| 伊金霍洛旗| 宜兴市| 沙坪坝区| 赤峰市| 班玛县| 宁德市| 广丰县| 盐津县| 绥德县| 察隅县| 略阳县| 马龙县| 汉寿县| 资中县| 邛崃市| 彭泽县| 临安市| 永新县| 社会| 安西县| 江口县| 通河县| 叶城县| 清新县| 奉贤区| 滦南县| 化州市| 嵊泗县| 南丹县| 上犹县| 阳新县| 行唐县| 鄂伦春自治旗| 拜城县| 仁布县| 隆林|