STOMP协议规范--转载
原文地址:http://simlegate.com/2013/10/17/stomp-specification-1.2/
摘要
STOMP是一個簡單的可互操作的協議, 被用于通過中間服務器在客戶端之間進行異步消息傳遞。它定義了一種在客戶端與服務端進行消息傳遞的文本格式.
STOMP已經被使用了很多年,并且支持很多消息brokers和客戶端庫。這個規范定義STOMP?1.2協議以及對1.1版本的更新。
發送反饋到stomp-spec@googlegroups.com.
概述
背景
由于需要用腳本語言如Ruby,?Python,?Perl去連接企業級的消息brokers, STOMP產生了.在這種情況下,STMOP實現了一些簡單的操作,比如可靠地發送單一的消息,然后斷開或者從目的地消費所有消息。
STOMP是除AMQP開放消息協議之外地另外一個選擇, 實現了被用在JMS brokers中特定的有線協議,比如OpenWire. 它僅僅是實現通用消息操作中的一部分,并非想要覆蓋全面的消息API.
STOMP目前已經是個成熟的協議,在wire-level方面, 它提供了一些簡單的用例,但仍保持其核心設計原則:簡單性和互操作性。 ### 協議概述
STOMP是基于frame的協議, 與HTTP的frame相似.一個frame包含一個command,一系列可選的headers和body.STOMP雖然是基于消息但同于也允許傳遞二進制消息。STMOP的默認消息格式是UTF-8,但是在消息體中同樣支持其他格式編碼。
STOMP服務器就好像是一系列的目的地, 消息會被發送到這里。STOMP協議把目的地當作不透明的字符串,其語法是服務端具體的實現。 此外STOMP沒有定義目的地的交付語義是什么。 交付,或“消息交換”,語義的目的地可以從服務器到服務器,甚至從目的地到目的地。這使得服務器有可創造性的語義,去支持STOMP。
STOMP client的用戶代理可以充當兩個角色(可能同時): * 作為生產者,通過SENDframe發送消息到server * 作為消費者,發送SUBSCRIBEframe到目的地并且通過MESSAGEframe從server獲取消息。
STOMP版本之間的變化
STOMP 1.2 大部分向后兼容1.1. 有兩點不兼容的改變: * 用回車加換行符代替只用換行符結束frame * 簡化了消息應答,用專用的header
除此之外,STOMP 1.2并沒有增加新特性,而是闡述規格中的一些模糊概念,比如: * 重復的frame header條目 *?content-length和content-typeheaders的用法 * 必須支持servers STOMP frame * 連接延遲 * 作用域,訂閱的唯一,事務的標示符 *RECEIPTframe的含義
?設計哲學
簡易性,互通性是STOMP主要設計哲學.
STOMP被設計成為輕量級的協議,它很容易用其他語言在client和server實現。這就意味著servers的架構沒有太多的約束,以及沒有太多的特性比如目的地命名空間,可靠的語法需要去實現。
在這份規格書里面,注意,我們沒有明確定義的STOMP 1.2 servers特性。你應該查閱STMOMP servers 文檔去獲得這些特性的詳細描述。
一致性
RFC 2119中詳細地解釋了MUST,?MUST NOT,?REQUIRED,?SHALL,?SHALL NOT,?SHOULD,?SHOULD NOT,RECOMMENDED,?MAY, 和?OPTIONAL這些關鍵字
為了阻止來自服務端地攻擊,保護內存溢出,消除平臺限制,限制了不受約束的輸入。
規格中一致性的級別適用于STOMP clients and STOMP servers.
STOMP Frames
STOMP是基于幀的協議,它假定底層為一個2-way的可靠流的網絡協議(如TCP)??蛻舳撕头掌魍ㄐ攀褂肧TOMP幀流通訊。幀的結構看起來像:
COMMAND header1:value1 header2:value2Body^@幀以command字符串開始,以EOL結束,其中包括可選回車符(13字節),緊接著是換行符(10字節)。command下面是0個或多個<key>:<value>格式的header條目, 每個條目由EOL結束。一個空白行(即額外EOL)表示header結束和body開始。body連接著NULL字節。本文檔中的例子將使用^@,在ASCII中用control-@表示,代表NULL字節。NULL字節可以選擇跟多個EOLs。欲了解更多關于STOMP幀的詳細信息,請參閱Augmented BNF節本文件。
本文檔中引用的所有command 和header 名字都是大小寫敏感的.
編碼方式
commands和headers 都是用UTF-8編碼的.在用UTF-8編碼的headers中除了CONNECT和CONNECTED幀以外,任何的回車符,換行符,colon found(?)都將被轉義.
轉義的目的在于允許header中的鍵值包含那些把octets當作值的frame header.
為了向后兼容STOMP 1.0,?CONNECT和CONNECTED不會轉義回車符,換行符,colon found(?)
C風格的字符串轉義被用在UTF-8編碼的headers中去轉義回車符,換行符以及colon found.當解碼headers時,必須使用下列轉換: * \r (octet 92 and 114) translates to carriage return (octet 13) * \n (octet 92 and 110) translates to line feed (octet 10) * \c (octet 92 and 99) translates to : (octet 58) * \\ (octet 92 and 92) translates to \ (octet 92)
未定義轉義序列如\t(octet 92 and 116)必須被視為一個致命的錯誤。相反,當編碼幀頭,必須使用逆轉變.
The STOMP 1.0 specification included many example frames with padding in the headers and many servers and clients were implemented to trim or pad header values. This causes problems if applications want to send headers that SHOULD not get trimmed. In STOMP 1.2, clients and servers MUST never trim or pad headers with spaces.
Body
只有SEND,?MESSAGE, 和ERROR幀有body。所有其他的幀不能有body。
標準header
大多數被用的header都有特殊的含義。
Header content-length
所有的幀可能都包括有content-length的header。它定義了消息體的大小。如果header包含了content-length, 包含空字節的消息體的最大字節數不能超過這個數. 幀仍然需要以空字節結束。
如幀體存在,SEND,?MESSAGE?和?ERROR幀應該包含content-length.如果幀體包含空字節,那么這個幀必須包括content-length.
Header content-type
如果幀體存在,SEND,?MESSAGE?和?ERROR幀應該包含content-type幫助接受者去理解幀體.如果設置了content-type, 它的值必須是描述幀體格式的MINE類型.否則,接收者應該認為幀體格式為二進制Blob.
以text/開頭的MINE類型的默認文本編碼是UTF-8. 如果你正在用一個基于MINE類型的不同編碼, 你應該添加;charset=<encoding>MINE類型。例如:如果你發送一個UTF-16編碼的HTML body, 應該設置text/html;charset=utf-16.?;charset=<encoding>也能添加到任何非text/?MINE類型后去作為說明。UTF-8編碼的XML是個很好的例子。它的編碼被設置為application/xml;charset=utf-8.
所有STOMP客戶端和服務端必須支持UTF-8編碼和解碼。因此,為了最大限度地使用在異構環境中的互操作性,建議基于文本的內容使用UTF-8編碼.
Header receipt
任何除了CONNECT的客戶端幀可以為receipt?header指定任何值。這會讓服務端應答帶有RECEIPT的客戶端幀的處理過程。
Repeated Header Entries
Since messaging systems can be organized in store and forward topologies, similar to SMTP, a message may traverse several messaging servers before reaching a consumer. A STOMP server MAY ‘update’ header values by either prepending headers to the message or modifying a header in-place in the message.
如果client或者server受到重復的header條目,只有第一個會被用作header條目的值。其他的值僅僅用來維持狀態改變,或者被丟棄。
例如,如果client收到:
MESSAGE foo:World foo:Hello^@foo?header的值為World.
大小限制
為了客戶端濫用服務端的內存分配,服務端可以設置可分配的內存大小:
- 單個幀允許幀頭的個數
- header中每一行的最大長度
- 幀體的大小
如果超出了這些限制,server應該向client發送一個error?frame,然后關閉連接.
連接延遲
STOMP servers必須支持client快速地連接server和斷開連接。 這意味著server在連接重置前只允許被關閉的連接短時間地延遲.
結果就是,在socket重置前client可能不會收到server發來的最后一個frame(比如ERROR或者RECEIPTframe去應答DISCONNECTframe)
Connecting
STOMP client通過CONNECTframe與server建立流或者TCP連接.
CONNECT accept-version:1.2 host:stomp.github.org^@如果server收到請求,將返回CONNECTEDframe:
CONNECTED version:1.2^@server能拒絕所有的連接請求。server應該響應ERRORframe去說明為什么連接被拒絕然后關閉連接。
CONNECT or STOMP Frame
STOMP servers 處理STOMPframe必須和處理CONNECTframe一樣。STOMP1.2?clients應該繼續使用CONNECTcommand去向后兼容1.0.
使用STOMPframe的clients只能連接上STOMP1.2?servers(以及一些STOMP1.1 servers),但是好處在于協議探針能夠從HTTP連接中區分開STOMP連接。
STOMP 1.2 clients?必須設置以下headers: *?accept-version: clients支持的STOMP的版本號。詳情見Protocol_Negotiation?*?host:client希望連接的虛擬主機名字,建議設置已經連接的socket為主機名,或者任何名字。如果headers沒有匹配到任何可用的虛擬主機,支持虛擬主機的servers將選擇默認的虛擬主機或者拒絕連接。
STOMP 1.2 clients可選擇設置以下headers: *?login: 用于在server驗證的用戶id *passcode: 用于在server驗證的密碼 *?heart-beat:?心跳設置
CONNECTED Frame
STOMP 1.2 servers?必須設置以下headers:
- version: 會話中STOMP版本。詳情見Protocol_Negotiation
STOMP 1.2 servers可選擇設置以下headers:
- heart-beat:?心跳設置
- session: 唯一的會話identifier
-
server: 描述STOMP server信息。它必須包含server-name,可以包含一些注釋信息(用空格分開)?server-name后面也可以帶著可選的版本號.
server = name ["/" version] *(comment)例如:
server:Apache/1.3.9
Protocol Negotiation
STOMP1.1 以后的版本,CONNECTframe必須包括accept-versionheader.它的值為clients支持的STOMP版本號,多個版本號用,隔開。如果不存在accept-versionheader,那么表明clients只支持1.0.
在一次會話中將使用雙方都支持的最高版本。
例如,如果client發送:
CONNECT accept-version:1.0,1.1,2.0 host:stomp.github.org^@server將返回與客戶端同時支持的最高版本。
CONNECTED version:1.1^@如果client和server不支持共同的協議版本,server必須返回如下的ERRORframe,然后斷開連接。
ERROR version:1.2,2.1 content-type:text/plainSupported protocol versions are 1.2 2.1^@心跳
心跳被用于去測試底層TCP連接的可用性,確保遠端服務處于活動狀態。
要使用心跳,每個部分必須聲明它能干什么以及想要其他部分干什么. 通過在CONNECT和CONNECTEDframe中增加heart-beatheader, 讓心跳在會話開始被定義好。heart-beatheader必須包含兩個用逗號隔開的正整數。
第一個數字代表發送方能做什么: *?0表示它不能發送心跳 * 否則它是能保證兩次心跳的最小毫秒數
第二個數字代表發送方能獲得什么: *?0表示它不想接收心跳 * 否則它表示兩次心跳期望的毫秒數
heart-beatheader是OPTIONAL的。沒有的話會被當作heart-beat:0,0header 處理,意思就是說它不會發送心跳并且不想接收心跳。
heart-beatheader提供了足夠的信息去了解每個部分心跳是否可用,發送到哪里,頻率的大小.
原始frame像這個樣子:
CONNECT heart-beat:<cx>,<cy>CONNECTED: heart-beat:<sx>,<sy>對于client發送server的心跳: * 如果<cx>為0(client不能發送心跳)或者<sy>為0(server不想接收心跳),將不起任何作用。 * 否則心跳頻率為MAX(<cx>,<sy>)毫秒數.
相反,<sx>和<cy>同樣是這樣的.
關于心跳本身,通過網絡連接收到的任何數據表明遠端服務是可用的。在給定的指向,如果心跳的頻率被期望是<n>毫秒:
- 發送者必須每<n>毫秒發送新數據。
- 如果發送者沒有真實的STOMP frame,必須發送一個end-of-line (EOL)
- 如果接受者在規定的時間內沒有收到新數據,表明連接已經斷開
- 由于時間誤差,接收者應該容錯和考慮定義錯誤的界限
Client Frames
client可以發送下列列表以外的frame,但是STOMP1.2 server會響應ERRORframe,然后關閉連接。
- SEND
- SUBSCRIBE
- UNSUBSCRIBE
- BEGIN
- COMMIT
- ABORT
- ACK
- NACK
- DISCONNECT
SEND
SENDframe發送消息到目的地,它必須包含表示目的地地址的destinationheader.SENDframe body是被發送的消息。例如:
SEND destination:/queue/a content-type:text/plainhello queue a ^@這個消息被發送到/queue/a.注意STOMP把目的地看作為一個不透明的字符串,沒有目的地假設的交互語義.你應該查閱STOMP server文檔,搞清楚如何構造目的地名字。
可靠的消息語義是server指定的,依賴備用的目的地的值和其他消息headers,比如事務headers,或者其他server指定的消息headers。
SEND可以添加transactionheader來支持事務處理.
如果body存在,那么SENDframe應該包含一個content-length和content-typeheader
一個應用可以給SENDframe增加任意多個用戶定義的headers。 通常用于用戶定義的頭,讓消費者能夠根據應用程序定義的報頭使用選擇訂閱幀過濾消息。 被定義的用戶必須通過MESSAGEframe傳送。
如果server不能無故成功處理SENDframe,那么server必須向client發送ERRORframe然后關閉連接。
SUBSCRIBE
SUBSCRIBEframe用于注冊給定的目的地.和SENDframe一樣,SUBSCRIBEframe需要包含destinationheader表明client想要訂閱目的地。 被訂閱的目的地收到的任何消息將通過MESSAGEframe發送給client。?ackheader控制著確認模式。
例子:
SUBSCRIBE id:0 destination:/queue/foo ack:client^@如果server不能成功創建此次訂閱,那么server將返回ERRORframe然后關閉連接。
STOMP服務器可能支持額外的服務器特定的頭文件,來自定義有關訂閱傳遞語義.
SUBSCRIBE id Header
一個單連接可以對應多個開放的servers訂閱,所以必須包含idheader去唯一標示這個訂閱.這個idframe可以把此次訂閱與接下來的MESSAGEframe和UNSUBSCRIBEframe聯系起來。
在相同的連接中,不同的訂閱必須擁有不同訂閱id。
SUBSCRIBE ack Header
ackheader可用的值有auto,?client,client-individual, 默認為auto.
當ack為auto時,client收到server發來的消息后不需要回復ACKframe.server假定消息發出去后client就已經收到。這種確認方式可以減少消息傳輸的次數.
當ack為client時, client必須發送ACkframe給servers, 讓它處理消息.如果在client發送ACKframe之前連接斷開了,那么server將假設消息沒有被處理,可能會再次發送消息給另外的客戶端。client發送的ACKframe被當作時積累的確認。這就意味這種確認方式會去操作ACKframe指定的消息和訂閱的所有消息
由于client不能處理某些消息,所以client應該發送NACKframe去告訴server它不能消費這些消息。
當ack模式是client-individual,確認工作就像客戶端確認模式(除了由客戶端發送的ACK或NACK幀)不會被累計。這意味著,后續ACK,?NACK消息幀,也不能影響前面的消息的確認。
UNSUBSCRIBE
UNSUBSCRIBEframe被用于去移除已經存在訂閱。一旦訂閱被刪除后,STOMP連接將不再會收到來自訂閱發出的消息。
一個單連接可以對應多個開放的server訂閱,所以必須包含idheader去唯一標示被刪除的訂閱.這個header中的id必須匹配已存在訂閱.
例如:
UNSUBSCRIBE id:0^@ACK
ACK用client和client-individual去確認訂閱消息的消費.只有通過ACK確認過后,訂閱的消息才算是被消費.
ACKframe必須包含一個idheader去匹配將要被確認的ackheader中的id.可以選擇地指定transactionheader表明消息確認應該是命名事務地一部分。
ACK id:12345 transaction:tx1^@NACK
NACK有ACK相反地作用。它地作用是告訴server client不想消費這個消息。server然后發送這個消息給另外的client,丟棄它或者把它放在無效的消息隊列中。這種準確的行為是server特定的。
NACK有相同的ACKheaders:?id(必選)和transaction(可選)。
NACK適用于單個消息(訂閱的ack模式為client-individual), 或者那些還沒有被ACK'ed和NACK'ed的消息(訂閱模式ack為client).
BEGIN
BEGIN用于事務的開始。事務被用于發送和確認消息,被發送和被確認的消息在事務過程中會被自動處理。
BEGIN transaction:tx1^@transactionheader是必填的,并且事務id將被用于在SEND, COMMIT, ABORT, ACK, and NACK frames去綁定命名的事務.在相同的連接中,不同事務必須用不同的id
如果client發送DISCONNECTframe或者TCP連接失敗,任何已開始但沒有提交的事務默認都會被中斷.
COMMIT
COMMIT用于在過程中提交事務.
COMMIT transaction:tx1^@transactionheader是必填的并且必須指定將要提交的事務的id.
ABORT
ABORT用于在過程中回滾事務.
ABORT transaction:tx1^@transactionheader是必填的并且必須指定將要提交的事務的id.
DISCONNECT
client能在任何時候斷開server的連接,但是不能保證已經發送的frame已經到達了server。為了讓這一切顯得不那么暴力,client確保所有已經發送的frames已經被server收到,client應該做以下3點:
發送帶有receiptheader的DISCONNECTframe
DISCONNECTreceipt:77^@等待帶有RECEIPTframe的響應
RECEIPTreceipt-id:77^@關閉socket
注意,如果server過早地關閉socket,client將不會收到期望地RECEIPTframe.見Connection_Lingering
client發送DISCONNECTframe后不必要在發送任何frame.
Server Frames
server偶爾也會發送frame給客戶端(除了連接最初的CONNECTEDframe).
這些frames為: *?MESSAGE?*?RECEIPT?*?ERROR
MESSAGE
MESSAGEframe用于將訂閱的消息發送給client.
MESSAGEframe必須包含destinationheader表明信息要到達的目的地。如果消息已經用STOMP發送,那么destinationheader應該和SENDframe中的一樣。
MESSAGEframe必須包含帶有唯一標識的message-idheader和帶有將接收消息的訂閱的idsubscriptionheader.
如果從訂閱收到的消息需要明確作出確認(client或者client-individual模式),那么MESSAGEframe必須包含帶有任何值的ackheader.這個header將把消息和后來的ACK,NACKframe關聯起來。
下面這個frame body包含了消息的內容:
MESSAGE subscription:0 message-id:007 destination:/queue/a content-type:text/plainhello queue a^@如果frame body包含內容的話,MESSAGEframe應該包含content-lengthheader和content-typeheader.
除了那些server指定的headers, 消息被發送到目的地時,MESSAGEframe同樣應該包括所有用戶定義的headers.查閱有關文檔,找出那些server指定添加到messages的headers.
RECEIPT
server成功處理請求帶有receipt的client frame后, 將發送RECEIPTframe到client.RECEIPTframe必須包含receipt-id?header,它的值為client frame中receiptheader的值。
RECEIPT receipt-id:message-12345^@RECEIPTframe是作為server處理的client frame后的應答. 既然STOMP是基于流的,那么receipt也是對server已經收到所有的frames的累積確認。但是,以前的frames可能并沒有被完全處理。如果clients斷開連接,以前接收到的frames應該繼續被server處理。
ERROR
如果出錯的話,server將發送ERRORframe.這種情況下,server還應該斷開連接。查看下一章connection lingering?ERRORframe應該包含帶有簡單錯誤信息的messageheader,或者Body包含詳細的描述信息,也可能沒有。
ERROR receipt-id:message-12345 content-type:text/plain content-length:171 message: malformed frame receivedThe message: ----- MESSAGE destined:/queue/a receipt:message-12345Hello queue a! ----- Did not contain a destination header, which is REQUIRED for message propagation. ^@如果錯誤關聯到了具體的某個client frame,那么server應該增加額外的headers去識別引起錯誤的frame。例如,如果frame包含receiptheader,ERRORframe應該設置receipt-idheader的值為引起錯誤的frame的receiptheader的值。
如果frame body包含內容的話,ERRORframe應該包含content-lengthheader和content-typeheader
Frames and Headers
除了上述標準headers之外(content-length,?content-type,?receipt),下面列出了所有規范中定義的headers:
- CONNECT or STOMP
- REQUIRED: accept-version, host
- OPTIONAL: login, passcode, heart-beat
- CONNECTED
- REQUIRED: version
- OPTIONAL: session, server, heart-beat
- SEND
- REQUIRED: destination
- OPTIONAL: transaction
- SUBSCRIBE
- REQUIRED: destination, id
- OPTIONAL: ack
- UNSUBSCRIBE
- REQUIRED: id
- OPTIONAL: none
- ACK or NACK
- REQUIRED: id
- OPTIONAL: transaction
- BEGIN or COMMIT or ABORT
- REQUIRED: transaction
- OPTIONAL: none
- DISCONNECT
- REQUIRED: none
- OPTIONAL: receipt
- MESSAGE
- REQUIRED: destination, message-id, subscription
- OPTIONAL: ack
- RECEIPT
- REQUIRED: receipt-id
- OPTIONAL: none
- ERROR
- REQUIRED: none
- OPTIONAL: message
除此之外,SEND和MESSAGEframes可能包含任意的用戶定義的headers ,它們會成為carried message的一部分。同樣,ERRORframe應該包含額外的headers來識別引起錯誤的frame。
最終,STOMP servers可以用額外的headers去訪問持久化或者有效期特性.查閱server文檔獲得更多信息。
Augmented BNF
A STOMP session can be more formally described using the Backus-Naur Form (BNF) grammar used in HTTP/1.1 RFC 2616.
NULL = <US-ASCII null (octet 0)> LF = <US-ASCII line feed (aka newline) (octet 10)> CR = <US-ASCII carriage return (octet 13)> EOL = [CR] LF OCTET = <any 8-bit sequence of data>frame-stream = 1*frameframe = command EOL*( header EOL )EOL*OCTETNULL*( EOL )command = client-command | server-commandclient-command = "SEND"| "SUBSCRIBE"| "UNSUBSCRIBE"| "BEGIN"| "COMMIT"| "ABORT"| "ACK"| "NACK"| "DISCONNECT"| "CONNECT"| "STOMP"server-command = "CONNECTED"| "MESSAGE"| "RECEIPT"| "ERROR"header = header-name ":" header-value header-name = 1*<any OCTET except CR or LF or ":"> header-value = *<any OCTET except CR or LF or ":">License
This specification is licensed under the?Creative Commons Attribution v3.0?license.
轉載于:https://www.cnblogs.com/davidwang456/p/4449428.html
總結
以上是生活随笔為你收集整理的STOMP协议规范--转载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Logback日志使用详解
- 下一篇: spring源码分析之spring-me