2024/08/12

STOMP

Simple (or Streaming) Text Oriented Message Protocol (STOMP) 是一種單純的以純文字為基礎的協定,通常用在訊息導向的 client-server 互動的訊息交換標準,所以會在 Message Queue 以及 WebSocket 看到使用這個 Protocol 傳遞訊息。

Commands

STOMP 類似 HTTP protocol,在 client 傳送給 server 定義了以下這些 Commands

  • CONNECT
  • SEND
  • SUBSCRIBE
  • UNSUBSCRIBE
  • BEGIN
  • COMMIT
  • ABORT
  • ACK
  • NACK
  • DISCONNECT

在 Server 傳送給 client 定義了以下的 commands

  • CONNECTED
  • MESSAGE
  • RECEIPT
  • ERROR

在規格中,稱呼每一則傳遞的訊息為 frame,frame 的第一行都是 command,後面跟著多行 key:value 的 headers,然後有一行空白,最後面是 body content,這樣的訊息格式跟 http 完全一樣

frame 結束時,要有一個 NULL octet,在規格文件標記為 ^@

只有 SEND, MESSAG, ERROR 這三個 frame 能夠有 body,其他 frames 不能有 body

Headers

commands 跟 headers 必須使用 UTF-8 encoding,在 parsing header 時,必須要做這樣的轉換

  • \r (0x5C, 0x72) 要轉換為 carriage return (0x0D)

  • \n (0x5C, 0x6E) 要轉換為 line feed (0x0A)

  • \c (0x5C, 0x63) 要轉換為 : (0x3A)

  • \\ (0x5C, 0x5C) 要轉換為 \ (0x5C)

在 header 的部分,除了因應自己的需求,要定義的 headers 以外,建議要定義這些標準的 header

  • content-length

  • content-type

這兩個 headers 類似 http protocol,就是整個 frame 的長度,以及 content 的內容格式

為避免惡意的 client,故意傳送非常大的 frame,把 server 的訊息 buffer 撐爆,server 可以限制以下這些部分的長度

  • 單一 frame 裡面可使用的 header 數量

  • 每一行 header 的最大長度

  • frame body 的最大長度

Spec 裡面,將 frame 區分為 Connecting, Client, Server 三個部分

Connecting

client 連線時,要發送 CONNECT

CONNECT
accept-version:1.2
host:stomp.github.org

^@

連線也可加上其他 headers,例如 login 帳號,passcode 密碼,heart-beat

server 接受連線要回應

CONNECTED
version:1.2

^@

還可以加上 session 代表 session id,server 代表 server name


CONNECT
accept-version:1.0,1.1,2.0
host:stomp.github.org

^@

完全不接受連線,就回應ERROR,這樣是說明 client 版本不符

ERROR
version:1.2,2.1
content-type:text/plain

Supported protocol versions are 1.2 2.1^@

Heart-beating

因為 TCP 連線有可能因為太久沒有資料通過,該連線通道有可能會被網路中兼任一個節點關閉,heart-beating 功能,類似 ping pong,定時發送,維持 TCP 連線。

heart-beat header 格式有兩個正整數,用逗號隔開

第一個正整數代表 sender 的 outgoing heart-beat

  • 0: 表示無法發送 heart-beat

  • 其他: 代表該 sender 保證在多少 milliseconds 之間,可發送 heart-beat

第二個正整數代表 sender 期待收到的 heart-beat

  • 0: 表示不需要接收 heart-beat

  • 其他: 代表該 sender 希望在多少 milliseconds 之間,可收到 heart-beat

CONNECT
heart-beat:<cx>,<cy>

CONNECTED
heart-beat:<sx>,<sy>

Client Frames

  • SEND

  • SUBSCRIBE

  • UNSUBSCRIBE

  • BEGIN

  • COMMIT

  • ABORT

  • ACK

  • NACK

  • DISCONNECT

SEND

send message to a destination

SEND
destination:/queue/a
content-type:text/plain

hello queue a
^@

SUBSCRIBE

註冊要 listen to a given destination

SUBSCRIBE
id:0
destination:/queue/foo
ack:client

^@

UNSUBSCRIBE

UNSUBSCRIBE
id:0

^@

ACK

acknowledge 確認收到某個訊息,如果有 transaction header,代表這個 ACK 是某個 transaction 的一部分

ACK
id:12345
transaction:tx1

^@

NACK

ACK 的相反

BEGIN

開始某個 transaction

BEGIN
transaction:tx1

^@

COMMIT

commit transaction

COMMIT
transaction:tx1

^@

ABORT

roll back a transaction

ABORT
transaction:tx1

^@

DISCONNECT

client 在關閉 TCP connection 之前,發送 DISCONNECT,正式通知 server 要關閉連線

client

DISCONNECT
receipt:77
^@

server response

RECEIPT
receipt-id:77
^@

client 在收到 RECEIPT 後,才正式關閉連線

Server Frames

  • MESSAGE

  • RECEIPT

  • ERROR

MESSAGE

傳送訊息給 destination,一定要加上 message-id header

MESSAGE
subscription:0
message-id:007
destination:/queue/a
content-type:text/plain

hello queue a^@

RECEIPT

server 確認有收到 client 發送的某一個 FRAME

RECEIPT
receipt-id:message-12345

^@

ERROR

錯誤發生時,server 發送給 client

ERROR
receipt-id:message-12345
content-type:text/plain
content-length:170
message:malformed frame received

The message:
-----
MESSAGE
destined:/queue/a
receipt:message-12345

Hello queue a!
-----
Did not contain a destination header, which is REQUIRED
for message propagation.
^@

Frames and Headers

除了上面提到,標準的 headers: content-length, content-type, receipt,所有 frames 建議必要使用與選擇性使用的 headers 如下

  • CONNECT or STOMP
    • REQUIRED: accept-versionhost
    • OPTIONAL: loginpasscodeheart-beat
  • CONNECTED
    • REQUIRED: version
    • OPTIONAL: sessionserverheart-beat
  • SEND
    • REQUIRED: destination
    • OPTIONAL: transaction
  • SUBSCRIBE
    • REQUIRED: destinationid
    • 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: destinationmessage-idsubscription
    • OPTIONAL: ack
  • RECEIPT
    • REQUIRED: receipt-id
    • OPTIONAL: none
  • ERROR
    • REQUIRED: none
    • OPTIONAL: message

雖然規格有提到這些 header 建議,但實際上,這份規格比較接近是針對 messaging system 的 frame 建議。

常常會看到的是只使用了類似 http frame 的格式,每一則傳遞的訊息為 frame,frame 的第一行都是 command,後面跟著多行 key:value 的 headers,然後有一行空白,最後面是 body content,就只有符合這樣的使用規則,其他部分的內容,都是讓 application 自己決定該怎麼使用。

header, body 的內容,都是讓 application 自己決定自己的 client-server 溝通的 protocol 訊息內容。

References

STOMP

Streaming Text Oriented Messaging Protocol - Wikipedia

STOMP Protocol - GeeksforGeeks

沒有留言:

張貼留言