2. 將 STOMP 目的地對應到位址和佇列
STOMP 用戶端在傳送訊息和訂閱時會處理目的地。目的地名稱只是簡單的字串,這些字串對應到伺服器上的某種形式的目的地 – 伺服器如何轉換這些字串則留給伺服器實作決定。
在 Apache ActiveMQ Artemis 中,這些目的地會根據執行的操作和所需的語義(例如,單播或多播)對應到位址和佇列。
3. 日誌記錄
透過為 org.apache.activemq.artemis.core.protocol.stomp.StompConnection
啟用 DEBUG
,可以記錄傳入和傳出的 STOMP 框架。這對於偵錯或只是監控用戶端活動非常有用。除了 STOMP 框架本身之外,還會記錄用戶端的遠端 IP 位址以及內部連線 ID,以便可以關聯來自同一用戶端的框架。
請按照這些步驟來適當設定記錄。
4. 路由語義
STOMP 規範在訊息路由語義方面刻意含糊不清。在提供協定概述時,STOMP 1.2 規範表示:
STOMP 伺服器被建模為一組可以將訊息傳送到的目的地。STOMP 協定將目的地視為不透明字串,其語法是伺服器實作特定的。此外,STOMP 並未定義目的地的傳遞語義應該是什麼。目的地的傳遞或「訊息交換」語義可能因伺服器而異,甚至因目的地而異。這允許伺服器在他們可以使用 STOMP 支援的語義方面具有創意。
因此,有幾種不同的方法可以在用戶端和 Broker 端指定所需的語義。
4.1. 從客戶端設定路由語義
4.1.1. 傳送
當 STOMP 用戶端傳送訊息(使用 SEND
框架)時,協定管理員會檢視 destination-type
標頭,以判斷將訊息路由到何處,以及可能如何建立要傳送到的位址和/或佇列。有效值為 ANYCAST
和 MULTICAST
(大小寫敏感)。如果未提供任何路由類型的指示(無論是透過用戶端還是 Broker),則會根據需要使用對應的 default-address-routing-type
& default-queue-routing-type
位址設定中定義的預設值。
如果使用 MULTICAST
,則 destination
標頭會對應到相同名稱的位址,如果使用 ANYCAST
,則會額外對應到相同名稱的佇列。
4.1.2. 訂閱
當 STOMP 用戶端訂閱目的地(使用 SUBSCRIBE
框架)時,協定管理員會檢視 subscription-type
標頭框架,以判斷要使用的訂閱語義,以及可能如何為訂閱建立位址和/或佇列。如果未提供任何路由類型的指示(無論是透過用戶端還是 Broker),則會根據需要使用對應的 default-address-routing-type
& default-queue-routing-type
位址設定中定義的預設值。
如果使用 MULTICAST
,則 destination
標頭會對應到相同名稱的位址,如果使用 ANYCAST
,則會額外對應到相同名稱的佇列。
4.2. 從 Broker 端設定路由語義
在 Broker 端,有兩個主要選項可以指定路由語義 - 字首和位址設定
4.2.1. 字首
使用字首需要在 STOMP 用戶端使用的 acceptor 上指定 anycastPrefix
和/或 multicastPrefix
。對於 STOMP 用例,這些字首會告訴 Broker,使用它們的目的地應被視為單播或多播。例如,如果 acceptor 具有 anycastPrefix=queue/
,則當 STOMP 用戶端將訊息傳送到 destination:queue/foo
時,Broker 會自動建立位址 foo
和佇列 foo
,並將其作為單播,並且訊息將放置在該佇列中。此外,如果 acceptor 具有 multicastPrefix=topic/
,則當 STOMP 用戶端將訊息傳送到 destination:topic/bar
時,Broker 將自動建立位址 bar 作為多播,但它不會建立佇列,因為多播(即發布/訂閱)語義要求用戶端明確建立訂閱才能接收這些訊息。
acceptor 上的 anycastPrefix 和/或 multicastPrefix 將會從 destination 值中移除。 |
4.2.2. 位址設定
使用位址設定需要定義位址設定元素,這些元素的 match
對應到用戶端將使用的目的地名稱,以及適當的 delimiter
以啟用比對。例如,broker.xml 可以使用以下內容:
<address-settings>
<address-setting match="queue/#">
<default-address-routing-type>ANYCAST</default-address-routing-type>
<default-queue-routing-type>ANYCAST</default-queue-routing-type>
</address>
<address-setting match="topic/#">
<default-address-routing-type>MULTICAST</default-address-routing-type>
<default-queue-routing-type>MULTICAST</default-queue-routing-type>
</address>
</address-settings>
<wildcard-addresses>
<delimiter>/</delimiter>
</wildcard-addresses>
然後,如果 STOMP 用戶端將訊息傳送到 destination:queue/foo
,則 Broker 將自動建立位址 queue/foo
和佇列 queue/foo
,並將其作為單播,並且訊息將放置在該佇列中。此外,如果 STOMP 用戶端將訊息傳送到 destination:topic/bar
,則 Broker 將自動建立位址 topic/bar
作為多播,但它不會建立佇列,如先前所述。
5. STOMP 心跳與連線存活時間 (connection-ttl)
行為良好的 STOMP 用戶端在關閉其連線之前,始終會傳送 DISCONNECT
框架。在這種情況下,伺服器會同步清除任何伺服器端資源,例如會話和消費者。但是,如果 STOMP 用戶端在未傳送 DISCONNECT
框架的情況下退出,或者如果它們崩潰,則伺服器將無法立即知道用戶端是否仍然處於活動狀態。因此,STOMP 連線預設的 connection-ttl
值為 1 分鐘(如需更多資訊,請參閱關於connection-ttl的章節)。可以使用 connection-ttl-override
屬性覆寫此值,或者,如果您需要特定的 connectionTtl 來用於您的 stomp 連線,而不影響 Broker 範圍的 connection-ttl-override
設定,您可以透過 connectionTtl
屬性設定您的 stomp acceptor,該屬性用於設定從該 acceptor 建立的連線的存活時間。例如:
<acceptor name="stomp-acceptor">tcp://127.0.0.1:61613?protocols=STOMP;connectionTtl=20000</acceptor>
上述設定將確保從該 acceptor 建立且不包含 heart-beat
標頭,或透過指定 0
值來停用用戶端到伺服器心跳的任何 STOMP 連線,其 connection-ttl
將設定為 20 秒。在 acceptor 上設定的 connectionTtl
將優先於 connection-ttl-override
。預設的 connectionTtl
為 60,000 毫秒。
由於 STOMP 1.0 不支援心跳,因此來自 STOMP 1.0 用戶端的所有連線都將由 Broker 根據上述組態選項強制設定連線存活時間。同樣地,任何未指定 heart-beat
標頭或停用用戶端到伺服器心跳(例如,透過在 heart-beat
標頭中傳送 0,X
)的 STOMP 1.1 或 1.2 用戶端,都將由 Broker 強制設定連線存活時間。
對於傳送非零客戶端至伺服器 heart-beat
標頭值的 STOMP 1.1 和 1.2 客戶端,其連線 TTL 將會據此設定。然而,訊息中繼站不會嚴格地將連線 TTL 設定為與 heart-beat
中指定的值相同,因為即使是微小的網路延遲也可能導致虛假的斷線。相反地,heart-beat
中的客戶端至伺服器值將會乘以在接受器上指定的 heartBeatToConnectionTtlModifier
。heartBeatToConnectionTtlModifier
是一個小數值,預設為 2.0
,因此舉例來說,如果客戶端傳送 1000,0
的 heart-beat
標頭,則連線 TTL 將設定為 2000
,以便每 1000 毫秒傳送的資料或 ping 幀有足夠的緩衝時間,而不至於被視為延遲並觸發斷線。這也符合 STOMP 1.1 和 1.2 規範,兩者皆指出:「由於計時不準確,接收器應容忍並考量誤差範圍。」
允許的最小和最大連線 TTL 也可以透過接受器的 connectionTtlMin
和 connectionTtlMax
屬性分別指定。預設的 connectionTtlMin
為 1000,預設的 connectionTtlMax
為 Java 的 Long.MAX_VALUE
,這意味著預設情況下基本上沒有最大連線 TTL。請記住,heartBeatToConnectionTtlModifier
在這裡很重要。例如,如果客戶端傳送 20000,0
的 heart-beat
標頭,並且接受器使用 30000
的 connectionTtlMax
和預設的 2.0
的 heartBeatToConnectionTtlModifier
,則連線 TTL 將為 40000
(即 20000
* 2.0
),這將超過 connectionTtlMax
。在這種情況下,伺服器會以 0,15000
(即 30000
/ 2.0
)的 heart-beat
標頭回應客戶端。如先前所述,這是為了確保客戶端心跳有足夠的緩衝時間,符合 STOMP 1.1 和 1.2 規範。connectionTtlMin
也會進行相同的計算。
最小的伺服器至客戶端心跳值為 500 毫秒。
請注意,STOMP 協定 1.0 版不包含任何心跳幀。因此,使用者有責任確保在連線 TTL 內傳送資料,否則伺服器會假設客戶端已停止運作並清除伺服器端的資源。使用 STOMP 1.1,使用者可以使用心跳來維護 STOMP 連線的生命週期。 |
6. 選擇器/篩選器運算式
STOMP 訂閱者可以使用 selector
標頭指定一個運算式,用於選擇或篩選訂閱者接收的內容。篩選器運算式語法遵循「篩選器運算式」文件中描述的核心篩選器語法。
7. STOMP 和 JMS 互通性
7.1. 從 JMS 或核心 API 傳送和取用 STOMP 訊息
STOMP 主要是一個以文字為導向的協定。為了簡化與 JMS 和核心 API 的互通性,我們的 STOMP 實作會檢查 content-length
標頭是否存在,以決定如何將 STOMP 1.0 訊息對應到 JMS 訊息或核心訊息。
如果 STOMP 1.0 訊息沒有 content-length
標頭,則會將其對應到 JMS TextMessage 或核心訊息,其中主體緩衝區中包含單一可為 null 的 SimpleString。
或者,如果 STOMP 1.0 訊息有 content-length
標頭,則會將其對應到 JMS BytesMessage 或核心訊息,其中主體緩衝區中包含 byte[]。
當將 JMS 訊息或核心訊息對應到 STOMP 時,也適用相同的邏輯。STOMP 1.0 客戶端可以檢查 content-length
標頭是否存在,以判斷訊息主體的類型(字串或位元組)。
7.2. STOMP 訊息的訊息 ID
透過 JMS 取用者或 QueueBrowser 接收 STOMP 訊息時,預設情況下訊息沒有像 JMSMessageID 這樣的屬性。然而,這可能會為想要 ID 以達成其目的的客戶端帶來一些不便。訊息中繼站 STOMP 提供一個參數,可在每個傳入的 STOMP 訊息上啟用訊息 ID。如果您希望每個 STOMP 訊息都有一個唯一的 ID,只需將 stompEnableMessageId
設定為 true 即可。例如
<acceptor name="stomp-acceptor">tcp://127.0.0.1:61613?protocols=STOMP;stompEnableMessageId=true</acceptor>
當伺服器使用上述設定啟動時,透過此接受器傳送的每個 STOMP 訊息都會新增一個額外的屬性。屬性鍵為 amqMessageId
,值是以 STOMP
為字首的長整數類型內部訊息 ID 的字串表示形式,例如
amqMessageId : STOMP12345
預設的 stompEnableMessageId
值為 false
。
8. 持久訂閱
SUBSCRIBE
和 UNSUBSCRIBE
幀可以使用特殊的標頭來擴充,以分別建立和銷毀持久訂閱。
若要建立持久訂閱,必須在 CONNECT
幀上設定 client-id
標頭,並且必須在 SUBSCRIBE
幀上設定 durable-subscription-name
。這兩個標頭的組合將形成持久訂閱的識別。
若要刪除持久訂閱,必須在 CONNECT
幀上設定 client-id
標頭,並且必須在 UNSUBSCRIBE
幀上設定 durable-subscription-name
。這些標頭的值應與在 SUBSCRIBE
幀上設定的值相符,以刪除對應的持久訂閱。
除了 durable-subscription-name
之外,訊息中繼站也支援 durable-subscriber-name
(在 durable-subscription-name
之前使用的已棄用屬性)以及來自 ActiveMQ Classic 的 activemq.subscriptionName
。如果幀包含多個這些屬性,則優先順序如下:
-
durable-subscription-name
-
durable-subscriber-name
-
activemq.subscriptionName
由於 STOMP 實作以確定性的方式建立用於持久訂閱的佇列(即使用 client-id
.subscription-name
的格式),因此可以預先設定持久訂閱。例如,如果您想要在地址 myAddress
上設定持久訂閱,其 client-id 為 myclientid
,訂閱名稱為 mysubscription
,則請設定持久訂閱
<addresses>
<address name="myAddress">
<multicast>
<queue name="myclientid.mysubscription"/>
</multicast>
</address>
</addresses>
9. 使用 STOMP 處理大型訊息
STOMP 客戶端可能會傳送非常大的幀主體,這些主體可能會超過訊息中繼站的內部緩衝區大小,導致意外錯誤。為了防止這種情況發生,訊息中繼站提供了一個 STOMP 設定屬性 stompMinLargeMessageSize
。此屬性可以在 STOMP 接受器內設定為參數。例如
<acceptor name="stomp-acceptor">tcp://127.0.0.1:61613?protocols=STOMP;stompMinLargeMessageSize=10240</acceptor>
此屬性的類型為整數。設定此屬性後,訊息中繼站將檢查透過使用此接受器建立的連線到達的每個 STOMP 幀的主體大小。如果主體的大小等於或大於 stompMinLargeMessageSize
的值,則該訊息將作為大型訊息保留。當大型訊息傳遞給 STOMP 取用者時,訊息中繼站將在將其傳送給客戶端之前,自動處理從大型訊息到正常訊息的轉換。
如果大型訊息已壓縮,伺服器將在將其傳送給 STOMP 客戶端之前將其解壓縮。stompMinLargeMessageSize
的預設值與 minLargeMessageSize 的預設值相同。
10. Web Sockets
Apache ActiveMQ Artemis 也支援透過 Web Sockets 的 STOMP。支援 Web Sockets 的現代網頁瀏覽器可以傳送和接收 STOMP 訊息。
透過正常的 STOMP 接受器支援透過 Web Sockets 的 STOMP
<acceptor name="stomp-ws-acceptor">tcp://127.0.0.1:61614?protocols=STOMP</acceptor>
透過此設定,Apache ActiveMQ Artemis 將在連接埠 61614
上接受透過 Web Sockets 的 STOMP 連線。然後,網頁瀏覽器可以使用 Web Socket 連線到 ws://<伺服器>:61614
,以傳送和接收 STOMP 訊息。
Web Socket 幀的酬載長度可能因客戶端實作而異。預設情況下,訊息中繼站將接受酬載長度為 65,536 的幀。如果客戶端需要在單個幀中傳送長度大於此值的酬載,則可以使用接受器上的 webSocketMaxFramePayloadLength
URL 參數調整此長度。在先前的版本中,此參數是透過名稱相似的 stompMaxFramePayloadLength
接受器 URL 參數設定的。
Web Socket 幀可以編碼為二進位或文字。預設情況下,訊息中繼站會將其編碼為二進位。但是,可以使用 webSocketEncoderType
接受器 URL 參數變更此設定。有效值為 binary
和 text
。
stomp-websockets
範例顯示如何設定 Apache ActiveMQ Artemis 訊息中繼站,讓網頁瀏覽器和 Java 應用程式交換訊息。
11. 流量控制
STOMP 客戶端可以使用 SUBSCRIBE
幀上的 consumer-window-size
標頭來控制傳送到客戶端的訊息流量。在流量控制章節中已廣泛討論此內容。
此功能類似於 ActiveMQ「Classic」支援的 activemq.prefetchSize
標頭。然而,該標頭是以訊息為單位指定大小,而 consumer-window-size
則是以位元組為單位指定大小。ActiveMQ Artemis 為了向後相容性而支援 activemq.prefetchSize
標頭,但該值會被解釋為位元組,就像 consumer-window-size
一樣。如果同時設定 activemq.prefetchSize
和 consumer-window-size
,則會使用 consumer-window-size
的值。
將 consumer-window-size
設定為 0
將確保 STOMP 客戶端在收到訊息後,在針對其已擁有的訊息傳送適當的 ACK
或 NACK
幀之前,不會收到另一個訊息。
將 consumer-window-size
設定為大於 0
的值,將允許其接收訊息,直到這些訊息的累積位元組數達到設定的大小。一旦發生這種情況,客戶端將不會再收到任何訊息,直到它針對其已擁有的訊息傳送適當的 ACK
或 NACK
幀。
將 consumer-window-size
設定為 -1
表示沒有流量控制,訊息中繼站將盡快將訊息分派給客戶端。
也可以在 acceptor
上設定流量控制,方法是使用 stompConsumerWindowSize
URL 參數。對於使用 client
和 client-individual
確認模式的客戶端,此值預設為 10240
(即 10K)。對於使用 auto
確認模式的客戶端,此值為 -1
。即使在 STOMP acceptor
上設定了 stompConsumerWindowSize
,也會被個別客戶端使用其 SUBSCRIBE
幀上的 consumer-window-size
標頭所提供的值覆寫。
|
使用先前提到過的除錯記錄,可以檢視分派給用戶端的 MESSAGE
訊框大小。這有助於判斷最佳的 consumer-window-size
設定。