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 支持的语义进行创造。
因此,在客户端和代理端,有几种不同的方法来指定所需的语义。
4.1. 从客户端配置路由语义
4.1.1. 发送
当 STOMP 客户端发送消息(使用 SEND
帧)时,协议管理器会查看 destination-type
标头以确定将其路由到何处,以及如何创建将消息发送到的地址和/或队列。有效值为 ANYCAST
和 MULTICAST
(区分大小写)。如果没有提供路由类型的指示(由客户端或代理提供),则将使用相应 default-address-routing-type
和 default-queue-routing-type
地址设置中定义的默认值(根据需要)。
如果使用 MULTICAST
,则 destination
标头映射到具有相同名称的地址,如果使用 ANYCAST
,则还会映射到具有相同名称的队列。
4.1.2. 订阅
当 STOMP 客户端订阅目的地(使用 SUBSCRIBE
帧)时,协议管理器会查看 subscription-type
标头帧以确定使用什么订阅语义,以及如何创建订阅的地址和/或队列。如果没有提供路由类型的指示(由客户端或代理提供),则将使用相应 default-address-routing-type
和 default-queue-routing-type
地址设置中定义的默认值(根据需要)。
如果使用 MULTICAST
,则 destination
标头映射到具有相同名称的地址,如果使用 ANYCAST
,则还会映射到具有相同名称的队列。
4.2. 从代理端配置路由语义
在代理端,有两种主要方法来指定路由语义 - 前缀和地址设置
4.2.1. 前缀
使用前缀涉及在 STOMP 客户端正在使用的 acceptor
上指定 anycastPrefix
和/或 multicastPrefix
。对于 STOMP 使用案例,这些前缀告诉代理使用它们的目的地方便应作为单播或多播进行处理。例如,如果 acceptor
具有 anycastPrefix=queue/
,则当 STOMP 客户端向 destination:queue/foo
发送消息时,代理将自动创建地址 foo
和队列 foo
,并将其适当设置为单播,并且消息将被放置在该队列中。此外,如果 acceptor
具有 multicastPrefix=topic/
,则当 STOMP 客户端向 destination:topic/bar
发送消息时,代理将自动创建地址 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
发送消息,代理将自动创建地址 queue/foo
和队列 queue/foo
,并将其适当设置为单播,并且消息将被放置在该队列中。此外,如果 STOMP 客户端向 destination:topic/bar
发送消息,代理将自动创建地址 topic/bar
作为多播,但它不会创建队列,如前所述。
5. STOMP 心跳和连接 TTL
行为良好的 STOMP 客户端将在关闭连接之前始终发送 DISCONNECT
帧。在这种情况下,服务器将同步清除任何服务器端资源,例如会话和使用者。但是,如果 STOMP 客户端在没有发送 DISCONNECT
帧的情况下退出,或者如果它们崩溃,服务器将无法立即知道客户端是否仍然存活。因此,STOMP 连接默认情况下为 1 分钟的 connection-ttl
值(有关更多信息,请参阅关于 connection-ttl 的章节)。此值可以使用 connection-ttl-override
属性覆盖,或者如果您需要为 STOMP 连接使用特定的 connectionTtl 而不会影响代理范围的 connection-ttl-override
设置,则可以使用 connectionTtl
属性配置 STOMP 接收器,该属性用于设置从该接收器创建的连接的 ttl。例如
<acceptor name="stomp-acceptor">tcp://127.0.0.1:61613?protocols=STOMP;connectionTtl=20000</acceptor>
上述配置将确保从该接收器创建的任何 STOMP 连接,如果未包含 heart-beat
标头或通过指定 0
值禁用客户端到服务器心跳,其 connection-ttl
将设置为 20 秒。接收器上设置的 connectionTtl
将优先于 connection-ttl-override
。默认 connectionTtl
为 60,000 毫秒。
由于 STOMP 1.0 不支持心跳,因此来自 STOMP 1.0 客户端的所有连接都将根据上述配置选项,由代理对其施加连接 TTL。同样,任何不指定 heart-beat
标头或禁用客户端到服务器心跳(例如,通过在 heart-beat
标头中发送 0,X
)的 STOMP 1.1 或 1.2 客户端都将由代理对其施加连接 TTL。
对于发送非零客户端到服务器 heart-beat
标头值的 STOMP 1.1 和 1.2 客户端,它们的连接 TTL 将相应设置。但是,代理不会严格将连接 TTL 设置为与 heart-beat
中指定的相同值,因为即使是小的网络延迟也可能导致虚假断开连接。相反,heart-beat
中的客户端到服务器值将乘以 acceptor
上指定的 heartBeatToConnectionTtlModifier
。heartBeatToConnectionTtlModifier
是一个十进制值,默认为 2.0
,因此例如,如果客户端发送 heart-beat
标头为 1000,0
,则连接 TTL 将设置为 2000
,以便每 1000 毫秒发送的数据或 ping 帧将有足够的缓冲,因此不会被视为延迟并触发断开连接。这也符合 STOMP 1.1 和 1.2 规范,两者都指出,“由于时间不精确,接收者应该宽容,并考虑误差范围”。
接收器可以通过 connectionTtlMin
和 connectionTtlMax
属性分别指定允许的最小和最大连接 TTL。默认的 connectionTtlMin
为 1000,默认的 connectionTtlMax
为 Java 的 Long.MAX_VALUE
,这意味着默认情况下基本上没有最大连接 TTL。请记住,heartBeatToConnectionTtlModifier
与此相关。例如,如果客户端发送 heart-beat
标头为 20000,0
,而接收器使用 connectionTtlMax
为 30000
且默认的 heartBeatToConnectionTtlModifier
为 2.0
,则连接 TTL 将为 40000
(即 20000
* 2.0
),这将超过 connectionTtlMax
。在这种情况下,服务器将使用 heart-beat
标头 0,15000
(即 30000
/ 2.0
)响应客户端。如前所述,这是为了确保根据 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 或核心消息,其主体缓冲区中包含一个可为空的 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
,值为一个长型内部消息 ID 的字符串表示形式,以 STOMP
为前缀,例如
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
上配置持久订阅,使用 myclientid
的客户端 ID 和 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. WebSockets
Apache ActiveMQ Artemis 还支持通过 WebSockets 使用 STOMP。支持 WebSockets 的现代 Web 浏览器可以发送和接收 STOMP 消息。
通过 WebSockets 使用 STOMP 是通过普通的 STOMP 接收器支持的
<acceptor name="stomp-ws-acceptor">tcp://127.0.0.1:61614?protocols=STOMP</acceptor>
使用此配置,Apache ActiveMQ Artemis 将在端口 61614
上通过 WebSockets 接受 STOMP 连接。然后,Web 浏览器可以使用 WebSockets 连接到 ws://<server>:61614
以发送和接收 STOMP 消息。
WebSocket 帧的有效负载长度可能因客户端实现而异。默认情况下,代理将接受有效负载长度为 65,536 的帧。如果客户端需要在一个帧中发送比这更长的有效负载,则可以使用接收器上的 webSocketMaxFramePayloadLength
URL 参数调整此长度。在以前的版本中,这是通过同名 stompMaxFramePayloadLength
接收器 URL 参数配置的。
WebSocket 帧可以编码为 二进制或文本。默认情况下,代理将它们编码为二进制。但是,可以使用 webSocketEncoderType
接收器 URL 参数更改此设置。有效值为 binary
和 text
。
stomp-websockets
示例 演示了如何配置 Apache ActiveMQ Artemis 代理以使 Web 浏览器和 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
,它也会被使用 consumer-window-size
标头在 SUBSCRIBE
帧上提供值的单个客户端的值覆盖。
|
使用前面提到的 DEBUG 日志记录,可以查看分派给客户端的 MESSAGE
帧的大小。这有助于确定最佳的 consumer-window-size
设置。