AMQP消息屬性詳解

《深入RabbitMQ》讀書筆記

  • 在消息隊(duì)列中,事件通過消息總線發(fā)布到消費(fèi)者應(yīng)用程序,每個(gè)事件都執(zhí)行自己特有的任務(wù)。但是,如果沒有一個(gè)標(biāo)準(zhǔn)化的消息格式,我們就難以預(yù)測特定的消息類型如何被序列化以及這些消息包含了哪些具體數(shù)據(jù)。
  • 為了提高消息格式的復(fù)用性,AMQP協(xié)議的Basic.Properties數(shù)據(jù)結(jié)構(gòu)提供了一種標(biāo)準(zhǔn)的消息格式,通過AMQP協(xié)議發(fā)布到RabbitMQ的每條消息都包含這一結(jié)構(gòu),這使得消費(fèi)者應(yīng)用程序可以進(jìn)行自動反序列化消息,在處理消息之前驗(yàn)證消息的來源以及類型等等。

Basic.Properties的屬性

  • content-type 指定消息類型(mime-types)便于序列化/反序列化
  • content-encoding 消息體使用某種特殊的方式進(jìn)行壓縮或者編碼
  • message-id 和 correlation-id 唯一標(biāo)識消息和消息響應(yīng),用于實(shí)現(xiàn)消息跟蹤
  • timestamp 減少消息大小,描述消息創(chuàng)建時(shí)間
  • expiration 表明消息過期
  • delivery-mode 在RabbitMQ中表明將消息寫入磁盤或者內(nèi)存隊(duì)列
  • app-id 和 user-id 幫助追蹤出現(xiàn)問題的消息發(fā)布者應(yīng)用程序
  • type 定義發(fā)布者和消費(fèi)者之間的契約
  • reply-to 實(shí)現(xiàn)響應(yīng)消息的路由
  • headers 映射表定義字有格式的屬性、實(shí)現(xiàn)RabbitMQ路由

本文中對于“契約”的定義:一種確定消息格式和內(nèi)容的規(guī)范。通常用來描述API、對象和系統(tǒng)的預(yù)定義規(guī)范。契約規(guī)范中通常包含有關(guān)發(fā)送和接收消息的精確信息,例如數(shù)據(jù)類型、格式以及各種需要遵守的條件。

content-type

  • 通過RabbitMQ發(fā)布的消息,我們很容易對它進(jìn)行復(fù)用。例如:最初的消費(fèi)者應(yīng)用程序是使用Python編寫的,但不久之后,使用PHP、JAVA和C語言編寫的程序同樣成為的消息的消費(fèi)者。
  • 當(dāng)消息格式中沒有對消息體內(nèi)容的描述時(shí),應(yīng)用程序會傾向使用一種隱式契約,這種隱式契約天生容易出錯(cuò)(例如,Python程序使用pickle序列化的數(shù)據(jù)無法在其他程序中反序列化),所以你的應(yīng)用程序非??赡艹霈F(xiàn)問題。
  • 通過指定消息類型,程序員和消費(fèi)者應(yīng)用程序不需要猜測如何反序列化消息體中的數(shù)據(jù),甚至根本就不需要執(zhí)行反序列化操作。如果你在消費(fèi)者代碼中使用了一個(gè)框架,在消費(fèi)者代碼處理消息之前,通過框架對其進(jìn)行預(yù)處理,消息體可以自動地被反序列化并加載到你所使用的編程語言的本地?cái)?shù)據(jù)結(jié)構(gòu)中。從而降低消費(fèi)者應(yīng)用程序代碼的復(fù)雜性
tips:
  1. 應(yīng)該盡量使用標(biāo)準(zhǔn)的序列化格式例如JSON、Msgpack或XML。這些格式允許使用任何編程語言編寫任意的消費(fèi)者應(yīng)用程序,因?yàn)閿?shù)據(jù)是以這些格式進(jìn)行自我描述的,所以編寫潛在的消費(fèi)者應(yīng)用程序會很容易。并且在程序外部對消息解碼也更簡單。
  2. 通過cintent-type屬性指定序列化格式,可以更好地支持未來的消費(fèi)者應(yīng)用程序-----當(dāng)消費(fèi)者可以自動識別它們所支持的消息格式并選擇性地處理消息時(shí),那就不必?fù)?dān)心在使用新的序列化格式并將其路由到相同的隊(duì)列時(shí)會發(fā)生什么情況。

通過gzip和 content-encoding屬性壓縮消息大小

  • 默認(rèn)情況下,通過AMQP發(fā)送的消息并不會被壓縮。
  • 在處理像XML這種過于繁雜的標(biāo)記語言,甚至在消息數(shù)量較大的場景下處理像JSON或YAML等輕量級數(shù)據(jù)格式時(shí),你的發(fā)布者可以在發(fā)布消息之前壓縮消息,并在收到消息時(shí)進(jìn)行解壓縮,就像我們使用gzip在服務(wù)器上壓縮網(wǎng)頁然后在瀏覽器端實(shí)時(shí)解壓這些網(wǎng)頁之后再進(jìn)行展示一樣。
  • 通過與content-type屬性相結(jié)合,content-encoding屬性使消費(fèi)者應(yīng)用程序能夠基于一種明確的契約與發(fā)布者進(jìn)行交互。你可以編寫擴(kuò)展性更強(qiáng)的代碼,確保代碼不會由于消息格式變更而導(dǎo)致意外錯(cuò)誤。例如:在應(yīng)用程序的生命周期中,你可能發(fā)現(xiàn)bzip2壓縮更加適合你的消息內(nèi)容。如果你在編寫消費(fèi)者應(yīng)用程序來檢查content-encoding屬性,則可以拒絕那些不能解碼的消息,并把它們留在隊(duì)列中供其它支持這種解碼方式的消費(fèi)者去消費(fèi)。

使用message-id和correlation-id引用消息

  • 在AMQP規(guī)范中,message-id和correlation-id是“應(yīng)用級別”的屬性,并沒有提供正式的行為定義。這就意味著你可以利用它們實(shí)現(xiàn)任何目的,這兩個(gè)字段允許最多255個(gè)字節(jié)的UTF-8編碼數(shù)據(jù),并以未壓縮的方式存儲在Basic.Properties數(shù)據(jù)結(jié)構(gòu)中。

message-id

  • 某些消息類型(如登錄事件)并不需要與其關(guān)聯(lián)的唯一標(biāo)識,但是訂單類型的消息可能需要具備這個(gè)唯一標(biāo)識。當(dāng)消息流對系統(tǒng)中的各個(gè)組件進(jìn)行耦合時(shí),message-id屬性使得消息能夠在消息頭中攜帶數(shù)據(jù)從而唯一地識別該消息。

correlation-id

  • 在AMQP規(guī)范中沒有關(guān)于correlation-id的正式定義,但是通過指定該消息的correlation-id為另一條消息關(guān)聯(lián)消息的message-id,可以指定該消息是另一個(gè)消息的響應(yīng)。另一種用法是使用它來傳遞關(guān)聯(lián)消息的事務(wù)ID或其他類似。

創(chuàng)建事件:timtstamp屬性

  • 與message-id和correlation-id一樣,timsstamp屬性也是“應(yīng)用級別”的屬性。通過timestamp屬性來指定消息的創(chuàng)建事件,消費(fèi)者可以評估消息投遞過程的性能、決定是否處理消息、丟棄消息、甚至對應(yīng)用程序發(fā)布警報(bào)消息。
tips:
  • 時(shí)間戳沒有上下文,因此建議在所有消息中使用統(tǒng)一的時(shí)區(qū)。

expiration:消息自動過期

  • 如果消息沒有被消費(fèi),expiration概述RabbitMQ何時(shí)應(yīng)該丟棄消息。expiration屬性在AMQP的規(guī)范定義中比較奇怪:“用于實(shí)現(xiàn),但沒有正式的行為”。這意味著RabbitMQ可以提供任何它認(rèn)為合理的實(shí)現(xiàn)方式。同時(shí),expiration的格式是一個(gè)短字符串,最多允許255個(gè)字符,而代表時(shí)間單位的另一個(gè)屬性timstamp則是一個(gè)整數(shù)值。
  • 由于規(guī)范中沒有給出明確說明,當(dāng)使用不同的消息代理服務(wù)器甚至同一消息代理服務(wù)器的不同版本時(shí),expiration可能會有不同的含義。想要利用expiretion屬性來實(shí)現(xiàn)RabbitMQ消息的自動過期,必須把一個(gè)UNIX時(shí)間戳存儲為字符串。
  • 使用expiration屬性時(shí),如果把一個(gè)已經(jīng)過期的消息發(fā)布到服務(wù)器,那么這條消息不會被路由到任何隊(duì)列,而是直接被丟棄。
tips:
  • RabbitMQ3.0以上才支持expiration屬性。

使用delvery-mode平衡速度和安全性

  • delivery-mode屬性是一個(gè)字節(jié)字段,像消息代理服務(wù)器表明在將消息投遞到任何正在等待的消費(fèi)者之前,你希望先將它持久化到磁盤上。delivery-mode屬性有兩個(gè)可能的值:1 代表非持久化消息,2 代表持久化消息。
  • 消息的持久性與隊(duì)列的持久性(durable)
    • 隊(duì)列的持久性(durable)告訴RabbitMQ該隊(duì)列在重新啟動RabbitMQ服務(wù)器或集群之后是否仍然有效。
    • 只有消息的delivery-mode為2時(shí),才會向RabbitMQ指定消息是否應(yīng)該被持久化。
    • 一個(gè)隊(duì)列可能包含持久化和為持久化的消息

使用app-id和user-id驗(yàn)證消息來源

  • app-id和user-id屬性提供了關(guān)于消息的另一層信息,并且有很多潛在的用途

app-id

  • app-id屬性在AMQP規(guī)范中定義為“短字符串”,最多允許255個(gè)UTF8字符,如果應(yīng)用程序采用的時(shí)以帶版本的API為中心的設(shè)計(jì),那么在生成消息時(shí)可以使用app-id傳遞特定的版本號,在處理消息之前檢查app-id允許應(yīng)用程序丟棄那些來源不明或者不受支持的消息
  • app-id的另一個(gè)屬性是收集統(tǒng)計(jì)數(shù)據(jù)。例如,如果你使用消息來傳遞登錄事件,則可以將app-id設(shè)置為觸發(fā)登錄事件的平臺和應(yīng)用程序版本。在一個(gè)需要同時(shí)支持web端、桌面端和移動端應(yīng)用的環(huán)境中,如果希望跟蹤并統(tǒng)計(jì)各個(gè)平臺的登錄數(shù)據(jù),使用這種方式我們甚至不需要檢查消息體。
  • 如果一個(gè)新的消息發(fā)布者錯(cuò)誤地使用了與現(xiàn)有發(fā)布者應(yīng)用程序相同的Exchange和routing_key時(shí),通過app-id可以更容易地追蹤惡意消息的來源

user-id

  • 在需要驗(yàn)證用戶身份時(shí),可以使用user-id屬性來標(biāo)識已登錄的用戶。但大多數(shù)情況下,并不推薦這種做法。RabbitMQ會根據(jù)發(fā)布消息的RabbitMQ用戶信息檢擦每條已發(fā)布消息的user-id屬性值,如果這兩個(gè)值不匹配,那么該消息會被拒絕。

使用type屬性獲取明細(xì)

  • AMQP規(guī)范的0-9-1版本將type屬性定義為“消息類型名稱”,它用來描述消息中的內(nèi)容。
  • 像JSON和XML這樣的自描述格式被一些人認(rèn)為太冗長了。它們可能在網(wǎng)絡(luò)傳輸或者內(nèi)存存儲上帶來不必要的開銷,序列化和反序列化相較一些語言也比較慢。當(dāng)消息體沒有以自描述格式進(jìn)行序列化(例如Apache Thrift、ProtoBuf這樣的序列化格式),這些二進(jìn)制編碼的消息格式不是自描述的,需要依賴外部定義的文件來進(jìn)行序列化和反序列化。這時(shí),可以通過type屬性指定記錄類型或者外部定義文件,如果無法正確訪問處理消息所需的.thrift或.proto文件,消費(fèi)者就能夠拒絕這些消息。

使用reply-to實(shí)現(xiàn)動態(tài)工作流

  • AMQP規(guī)范中,reply-to屬性被指定用于應(yīng)用程序,但是沒有規(guī)定的行為,他還有一個(gè)附加說明:使用reply-to可以構(gòu)架一個(gè)用來回復(fù)消息的私有響應(yīng)隊(duì)列。
  • 盡管在AMQP規(guī)范中沒有說明私有響應(yīng)隊(duì)列的確切定義,但是該屬性可以在最初發(fā)布消息的相同Exchange中攜帶特定的隊(duì)列名稱或者routing_key,這些隊(duì)列名稱或者routing_key可以用于回復(fù)消息。

使用消息頭定義頭屬性

  • headers是一個(gè)鍵值對映射表,允許用戶自定義任何的key/value。鍵可以是ASCII或者Unicode字符串,最大長度為255個(gè)字符。值可以是任何有效的AMQP值類型。
  • headers屬性允許添加任何你想要的數(shù)據(jù)到消息頭中。除此之外,它還具有另一個(gè)獨(dú)特的功能:RabbitMQ可以根據(jù)headers表中填充值來進(jìn)行消息的路由,而不需要依賴于routing_key

優(yōu)先級屬性

  • 截至3.5.0版本,RabbitMQ已經(jīng)按照AMQP規(guī)范實(shí)現(xiàn)了priority字段,它的取值范圍是一個(gè)介于0~9之間的整數(shù),用于指定隊(duì)列中消息的優(yōu)先級。
  • 如果首先發(fā)布一條優(yōu)先級為9的消息,隨后再發(fā)布一條優(yōu)先級為0的消息,則新連接的消費(fèi)者將會先接收到優(yōu)先級為0的消息
tips:
  • RabbitMQ將priority字段實(shí)現(xiàn)為無符號字節(jié),所以優(yōu)先級可以是0到255之間的任意值,但最好將取值范圍限制在0到9之間以保證規(guī)范性。

不能使用的屬性: cluster-id/reserved

  • cluster-id屬性是AMQP 0-8中定義的,但隨后被刪除,RabbitMQ從未實(shí)現(xiàn)過關(guān)于改屬性的任何行為
    。
  • AMQP 0-9-1將cluster-id屬性重新命名為reserved,并聲明它必須為空,雖然RabbitMQ目前沒有根據(jù)規(guī)范要求它是空的,但是最好規(guī)避這個(gè)屬性。

總結(jié)

屬性 類型 用途 使用建議或特殊用法
app-id short-string 應(yīng)用程序 用于發(fā)布消息的應(yīng)用程序
content-encoding short-string 應(yīng)用程序 指定消息體是否以某種特殊方式編碼,如zlib、deflate或Base64
content-type short-string 應(yīng)用程序 使用mime-types指定消息體的類型
correlation-id short-string 應(yīng)用程序 如果消息引用了某個(gè)其他消息或具有唯一標(biāo)識的項(xiàng)目,那么correlation-id可以用來指定這種引用關(guān)系
delivery-mode octet RabbitMQ 值為1告訴RabbitMQ可以將消息保存在內(nèi)存中;值為2表示它也應(yīng)該被寫入磁盤
expiration short-string RabbitMQ 用文本字符串表示的紀(jì)元時(shí)間或者UNIX時(shí)間戳,表示消息的過期時(shí)間
headers table 應(yīng)用程序/RabbitMQ 一個(gè)自由格式的鍵值表,可以使用它來添加消息相關(guān)的附加元數(shù)據(jù);RabbitMQ也可以基于它進(jìn)行路由
message-id short-string 應(yīng)用程序 唯一的標(biāo)識符,例如在應(yīng)用程序中可以使用uuid來標(biāo)識消息
priority octet RabbitMQ 隊(duì)列中標(biāo)識消息的優(yōu)先順序
timestamp timestamp 應(yīng)用程序 用文本字符串表示的紀(jì)元時(shí)間或者UNIX時(shí)間戳,表示消息的創(chuàng)建時(shí)間
type short-string 應(yīng)用程序 一個(gè)文本字符串,用于表示應(yīng)用程序中描述消息或有效負(fù)載的類型
user-id short-string 應(yīng)用程序/RabbitMQ 一個(gè)自由格式的字符串,如果啟用該屬性,RabbitMQ會驗(yàn)證當(dāng)前連接的用戶,若不匹配則丟棄消息
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容