STOMP 插件
概述
RabbitMQ 通过一个包含在核心发行版中的插件支持 STOMP。该插件支持 STOMP 版本 1.0 至 1.2,并包含一些 扩展和限制。
STOMP 客户端可以与其他协议互操作。尽管可能存在一些限制或需要调整默认设置,但 管理 UI 和其他几个插件中的所有功能都可以与 STOMP 一起使用。
启用插件
STOMP 插件包含在 RabbitMQ 发行版中。在客户端成功连接之前,必须使用 rabbitmq-plugins 来启用它。
rabbitmq-plugins enable rabbitmq_stomp
插件配置
TCP 监听器
当未指定配置时,STOMP 适配器将在所有接口的 61613 端口上监听,默认用户登录/密码为 guest/guest。
要更改监听端口,请编辑您的 配置文件,为 rabbitmq_stomp 应用程序包含一个 tcp_listeners 变量。
例如,一个将监听端口更改为 12345 的极简配置文件将如下所示:
stomp.listeners.tcp.1 = 12345
而一个仅监听 localhost(IPv4 和 IPv6)的配置将如下所示
stomp.listeners.tcp.1 = 127.0.0.1:61613
stomp.listeners.tcp.2 = ::1:61613
TCP 监听器选项
该插件支持 TCP 监听器选项配置。
这些设置使用一个通用前缀 stomp.tcp_listen_options,并控制 TCP 缓冲区大小、入站 TCP 连接队列长度、是否启用 TCP keepalives 等。详情请参阅 网络指南。
stomp.listeners.tcp.1 = 127.0.0.1:61613
stomp.listeners.tcp.2 = ::1:61613
stomp.tcp_listen_options.backlog = 4096
stomp.tcp_listen_options.recbuf = 131072
stomp.tcp_listen_options.sndbuf = 131072
stomp.tcp_listen_options.keepalive = true
stomp.tcp_listen_options.nodelay = true
stomp.tcp_listen_options.exit_on_close = true
stomp.tcp_listen_options.send_timeout = 120
TLS 支持
要为 STOMP 连接使用 TLS,必须在 代理中配置 TLS。要启用支持 TLS 的 STOMP 连接,请使用 stomp.listeners.ssl.* 配置键为 STOMP 添加一个 TLS 监听器。
该插件将使用核心 RabbitMQ 服务器证书和密钥(就像 AMQP 0.9.1 和 AMQP 1.0 监听器一样)
ssl_options.cacertfile = /path/to/tls/ca_certificate.pem
ssl_options.certfile = /path/to/tls/server_certificate.pem
ssl_options.keyfile = /path/to/tls/server_key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true
stomp.listeners.tcp.1 = 61613
# default TLS-enabled port for STOMP connections
stomp.listeners.ssl.1 = 61614
此配置将创建一个在端口 61613 上的标准 TCP 监听器,以及一个在端口 61614 上的 TLS 监听器。
当设置了 TLS 监听器时,您可能希望禁用所有非 TLS 监听器。这可以这样配置:
stomp.listeners.tcp = none
stomp.listeners.ssl.1 = 61614
默认用户
RabbitMQ STOMP 适配器允许 CONNECT 帧省略 login 和 passcode 头部,如果配置了默认值。
要配置默认的登录名和密码,请在 rabbitmq_stomp 应用程序配置中添加一个 default_user 部分。例如:
stomp.default_user = guest
stomp.default_pass = guest
上面的配置示例将 guest/guest 设置为默认的登录/密码对。
使用 TLS/x509 客户端证书进行身份验证
该插件可以通过从客户端的 TLS (x509) 证书中提取名称来验证启用了 TLS 的连接,而无需使用密码。
为了安全起见,服务器必须使用 fail_if_no_peer_cert 设置为 true 和 verify 设置为 verify_peer 的 TLS 选项进行配置,以强制所有 TLS 客户端拥有可验证的客户端证书。
要启用此功能,请为 rabbitmq_stomp 应用程序将 ssl_cert_login 设置为 true。例如:
stomp.ssl_cert_login = true
默认情况下,这将用户名设置为证书主题的专有名称(Distinguished Name)的 RFC4514 格式字符串,类似于 OpenSSL 的 "-nameopt RFC2253" 选项生成的格式。
要改用通用名(Common Name),请在您的配置中添加:
ssl_cert_login_from = common_name
到您的配置中。
请注意
- 经过身份验证的用户必须存在于配置的身份验证/授权后端中。
- 客户端不得提供
login和passcode头部。
隐式连接
如果您配置了默认用户或使用了 SSL 客户端证书身份验证,您还可以选择允许客户端省略 CONNECT 帧。在此模式下,如果一个会话上发送的第一个帧不是 CONNECT 帧,则客户端将自动以默认用户或 SSL 证书中提供的用户身份连接。
要启用隐式连接,请为 rabbit_stomp 应用程序将 implicit_connect 设置为 true。例如:
stomp.default_user = guest
stomp.default_pass = guest
stomp.implicit_connect = true
隐式连接默认未启用。
注意: 触发隐式连接的客户端将不会收到服务器发送的 CONNECTED 帧。
代理协议
STOMP 插件支持 代理协议。此功能默认关闭。要为 STOMP 客户端启用它:
stomp.proxy_protocol = true
有关代理协议的更多信息,请参阅 网络指南。
帧大小限制
默认情况下,帧大小限制为 4MB。当帧超过限制时,将被视为错误并关闭连接。
stomp.max_frame_size = 4 * 1024 * 1024
目标
STOMP 规范并未规定代理必须支持哪些类型的目标,而是 SEND 和 MESSAGE 帧中的 destination 头部的值特定于代理。RabbitMQ STOMP 适配器支持多种不同的目标类型:
/exchange-- 发送到任意路由键的SEND和订阅任意绑定模式的SUBSCRIBE;/queue-- 发送到 STOMP 网关管理的队列的SEND和订阅;/amq/queue-- 发送到 STOMP 网关外部创建的队列的SEND和订阅;/topic-- 发送到临时和持久化主题的SEND和订阅;/temp-queue/-- 创建临时队列(仅在reply-to头部中)。
AMQP 0-9-1 语义
MESSAGE 帧上的 destination 头部设置为消息好像是从 SEND 帧发出的那样。
- 发送到默认交换机的消息被赋予目标
/queue/队列名; - 发送到
amq.topic的消息被赋予目标/topic/路由键; - 所有其他消息被赋予目标
/exchange/交换机名[/路由键]。
如果 队列名、交换机名 或 路由键 中包含 /、% 或非 ASCII 字节,它们将被替换为序列 %dd,其中 dd 是该字节的十六进制代码。
由于这些规则,MESSAGE 帧上的目标可能与发布它的 SEND 帧上的目标不完全匹配。
不同的目标具有不同的队列参数默认值。它们可以通过头部进行显式控制,本指南将在后面详细说明。
交换机目标
可以使用以 /exchange 开头的目标来访问任何交换机/队列或交换机/路由键组合。
对于 SUBSCRIBE 帧,可以使用形式为 /exchange/<name>[/<pattern>] 的目标。此目标:
- 在
<name>交换机上创建一个独占的、自动删除的队列; - 如果提供了
<pattern>,则使用<pattern>将队列绑定到<name>交换机;并且 - 为当前 STOMP 会话注册对该队列的订阅。
对于 SEND 帧,可以使用形式为 /exchange/<name>[/<routing-key>] 的目标。此目标:
- 将消息发送到交换机
<name>,路由键为<routing-key>。
注意: 交换机目标不适合用于从现有队列消费消息。每次订阅都会创建一个新队列,并使用提供的路由键绑定到指定的交换机。要处理现有队列,请使用 /amq/queue 目标。
队列目标
对于简单队列,可以使用形式为 /queue/<name> 的目标。
队列目标最多将每条消息传递给一个订阅者。当没有订阅者存在时发送的消息将被排队,直到订阅者连接到该队列。
AMQP 0-9-1 语义
对于 SUBSCRIBE 帧,这些目标会创建一个共享队列 <name>。为当前 STOMP 会话创建对队列 <name> 的订阅。
对于 SEND 帧,在会话中第一次发送到此目标时会创建一个共享队列 <name>,之后不再创建。消息将被发送到默认交换机,路由键为 <name>。
如果未指定队列参数,则假定队列是持久的、非独占的、非自动删除的。
AMQ 队列目标
要寻址 STOMP 适配器外部创建的现有队列,可以使用形式为 /amq/queue/<name> 的目标。
AMQP 0-9-1 语义
对于 SEND 和 SUBSCRIBE 帧,都不会创建队列。对于 SUBSCRIBE 帧,如果队列不存在则会报错。
对于 SEND 帧,消息将通过默认交换机直接发送到名为 <name> 的现有队列。
对于 SUBSCRIBE 帧,为当前 STOMP 会话创建对现有队列 <name> 的订阅。
如果未指定队列参数,则假定队列是持久的、非独占的、非自动删除的。
主题目标
STOMP 客户端可能最常使用的目标类型是 /topic/<name>。它们在发布消息时对订阅者模式执行主题匹配,并将消息路由到多个订阅者(每个订阅者都收到自己的副本)。主题目标支持 AMQP 0-9-1 主题交换机 的所有路由模式。
发送到没有活动订阅者的主题目标的消息将被简单地丢弃。
AMQP 0-9-1 语义
对于 SEND 帧,消息将被发送到 amq.topic 交换机,路由键为 <name>。
对于 SUBSCRIBE 帧,将创建一个自动删除的、非持久化的队列,并以路由键 <name> 绑定到 amq.topic 交换机。将创建对该队列的订阅。
可以使用 stomp.default_topic_exchange 配置设置来指定一个不同于 amq.topic 的默认交换机。
stomp.default_topic_exchange = some.exchange
持久化主题订阅
STOMP 适配器支持持久化主题订阅。持久化订阅允许客户端根据需要断开并重新连接到 STOMP 代理,而不会丢失发送到该主题的消息。
主题既不是持久化也不是临时的,而是订阅是持久化或临时的。给定的主题可以混合持久化和临时订阅。
创建持久化订阅
要创建持久化订阅,请在 SUBSCRIBE 帧中将 durable 头部设置为 true。为向后兼容早期插件版本,也支持 persistent 作为 durable 的别名。
在创建主题目标的持久化订阅时,将 auto-delete 设置为 false,以确保在最后一个订阅者断开连接时,支持您订阅的队列不会被删除。
创建持久化订阅时,必须指定 id 头部。例如:
SUBSCRIBE
destination:/topic/my-durable
id:1234
durable:true
auto-delete:false
AMQP 0-9-1 语义
对于 SEND 帧,消息将被发送到 amq.topic 交换机,路由键为 <name>。
对于 SUBSCRIBE 帧,将为每个不同的订阅 ID x 目标对创建一个共享队列,并以路由键 <name> 绑定到 amq.topic 交换机。将创建对该队列的订阅。
注意:可以使用 stomp.default_topic_exchange 配置设置来指定一个不同于 amq.topic 的默认交换机。
删除持久化订阅
要永久删除持久化订阅,请发送一个 UNSUBSCRIBE 帧,其中包含与订阅时相同的 durable 和 auto-delete 头部值,用于该订阅 ID。
例如:
UNSUBSCRIBE
id:1234
durable:true
auto-delete:false
临时队列目标
临时队列目标允许您在 SEND 帧的 reply-to 头部中定义临时目标。
临时队列由代理管理,其身份对每个会话都是私有的——无需在不同会话中为临时队列选择不同的名称。
要使用临时队列,请在 SEND 帧上放置 reply-to 头部,并使用以 /temp-queue/ 开头的头部值。例如:
SEND
destination:/queue/reply-test
reply-to:/temp-queue/foo
Hello World!
此帧将创建一个临时队列(带有生成的名称),该队列对会话是私有的,并自动订阅该队列。另一个使用 reply-to:/temp-queue/foo 的会话将创建一个新的、不同的队列。
内部订阅 ID 是字符串 /temp-queue/ 和临时队列(在本例中为 /temp-queue/foo)的串联。订阅 ID 可用于识别回复消息。无法从 destination 头部识别回复消息,因为它将与 reply-to 头部中的值不同。内部订阅使用自动确认模式,并且无法取消。
/temp-queue/ 目标不是接收客户端在发送回复时使用的目标名称。相反,接收客户端可以从 MESSAGE 帧的 reply-to 头部获取(实际的)回复目标队列名称。然后,此回复目标名称可用作在回复接收到的 MESSAGE 帧时发送的 SEND 帧的 destination 头部的值。
回复目标队列名称是不透明的,无法从 /temp-queue/ 名称推断。
SEND 和 SUBSCRIBE 帧不得在 destination 头部中包含 /temp-queue 目标。无法将消息发送到 /temp-queue 目标,并且回复队列的订阅是自动创建的。
AMQP 0-9-1 语义
每个 /temp-queue/ 都对应一个不同的匿名、独占、自动删除队列。因此,无需显式清理回复队列。
主题和交换机目标的由用户生成的队列名称
订阅 exchange 或 topic 目标时,RabbitMQ 默认会生成一个队列名称。可以使用 x-queue-name 头部提供自定义名称。
SUBSCRIBE
destination:/topic/alarms
x-queue-name:my-alarms-queue
使用 STOMP 控制 RabbitMQ 队列参数
队列属性可以通过 STOMP 头部进行控制:
durable(别名为persistent)auto-deleteexclusive
以及用于控制死信、队列和消息 TTL、队列限制等的可选参数 ("x-arguments"):
x-dead-letter-exchangex-dead-letter-routing-keyx-expiresx-message-ttlx-max-lengthx-max-length-bytesx-max-age(仅适用于 流)x-stream-max-segment-size-bytes(仅适用于 流)x-overflowx-max-priorityx-queue-type(用于 声明 仲裁队列 和 流)
每个头部的含义与通过 AMQP 0-9-1 声明队列时相同。有关详细信息,请参阅其余文档。
在 STOMP 中使用策略
RabbitMQ 策略允许对队列和交换机进行灵活、集中的属性配置。策略可以与 STOMP 插件使用的队列一起使用。
策略使得在 STOMP 中可以使用更多 RabbitMQ 功能:
STOMP 插件创建的所有服务器命名的队列都以 stomp- 作为前缀,这使得在策略中匹配队列变得容易。例如,要将 STOMP 队列长度限制为 1000 条消息,请创建以下策略:
rabbitmqctl set_policy stomp-queues "^stomp-" '{"max-length":1000}' --apply-to queues
在 Windows 上使用 rabbitmqctl.bat
rabbitmqctl.bat set_policy stomp-queues "^stomp-" "{""max-length"":1000}" --apply-to queues
请注意,一次只有一个策略应用于队列,因此要指定多个参数(例如,队列长度限制和死信),需要将它们放入一个策略中。
协议扩展和限制
RabbitMQ STOMP 适配器放宽了 CONNECT 协议,并在某些帧上支持一系列非标准头部。这些额外的头部提供了对 STOMP 规范中未描述的功能的访问。此外,我们禁止了一些保留供服务器使用的头部。详细信息如下:
连接和虚拟主机
CONNECT(或 STOMP)帧在 STOMP 1.1 中有一个必需的 host 头部(用于选择要使用的虚拟主机)。RabbitMQ 适配器允许此头部成为可选的。
如果省略,则假定为默认虚拟主机(/)。要配置其他默认虚拟主机,请在 rabbitmq_stomp 应用程序配置中添加一个 default_vhost 部分,例如:
stomp.default_vhost = /
如果指定了 host 头部,它必须是 RabbitMQ 服务器已知的虚拟主机之一,否则连接将被拒绝。即使在连接时协商了 STOMP 1.0 版本,也会遵循 host 头部。
消息持久化
在 SEND 帧上,STOMP 适配器支持包含 persistent 头部。
将 persistent 头部设置为 true 会使消息持久化。
带有 persistent:true 的 SEND 帧的确认,直到收到代理的确认后才会发送。持久化消息确认的确切语义可以在 此处找到。
持久化消息的 MESSAGE 帧将包含一个 persistent:true 头部。
ACK 和 NACK
RabbitMQ STOMP 插件支持 auto、client 和 client-individual 订阅头部,这些头部会影响 ACK 和 NACK 操作的工作方式。
auto 模式使用自动确认。client 模式是手动(客户端驱动)的批量消息确认。client-individual 用于逐条手动确认。
NACK 帧可以选择携带 requeue 头部,该头部控制消息是将被重新排队还是被丢弃/死信。默认值为 true。
预取
所有订阅的预取计数默认设置为无限制。可以通过在 SUBSCRIBE 帧上设置 prefetch-count 头部为所需的整数来控制。
流支持
SUBSCRIBE 帧支持 x-stream-offset 头部,用于指定在 流中开始消费的偏移量。典型的流订阅帧如下所示:
SUBSCRIBE
destination:/amq/queue/my-stream
ack:client
prefetch-count:10
x-stream-offset:next
请注意,ack 和 prefetch-count 头部也是必需的。x-stream-offset 头部与 AMQP 0.9.1 中的语义相同,可能的值包括:
first从流中第一个可用消息开始消费last从最后写入的消息块开始消费next从流的末尾开始消费(注意,在有人发布到流之前,消费者不会收到消息)offset=<offset-value>从特定偏移量开始,例如offset=40000timestamp=<unix-time>从给定时间开始,例如timestamp=1619432061表示2021-04-26T10:14:21+00:00
默认值为 next。
在传递流中的消息时,消息偏移量(即消息在流中的位置)包含在 MESSAGE 帧的 x-stream-offset 头部中。
还支持 流过滤。流协议 是与流交互的首选方式,但大多数功能也可通过其他协议使用。流过滤也不例外,它与 STOMP 的工作方式与 AMQP 相同。
- 声明:可以通过订阅创建流。将
x-queue-type头部设置为stream,并使用x-stream-filter-size-bytes头部设置过滤器大小(可选)。 - 发布:使用
x-stream-filter-value头部为出站消息设置过滤器值。 - 消费:使用
x-stream-filter头部设置预期的过滤器值(使用逗号分隔值),还可以选择使用x-stream-match-unfiltered头部(true或false)来接收没有过滤器值的消息(默认值为false)。应用程序还必须实现客户端过滤,因为仍有可能收到不符合过滤器值标准的消息。
SEND 帧禁止的头部
不允许在 SEND 帧上设置 message-id 头部。该头部及其值由服务器在发送到客户端的 MESSAGE 帧上设置。
队列属性
SEND 帧还允许包含与发布消息时可用的AMQP 属性相对应的头部。这些头部也会在发送到客户端的 MESSAGE 帧上设置。
所有非废弃的 AMQP 0-9-1 属性(content-type、content-encoding、headers、delivery-mode、priority、correlation-id、reply-to、expiration、message-id、timestamp、type、user-id 和 app-id)都受支持。以下特殊规则适用:
- STOMP 中的
amqp-message-id转换为 AMQP 中的message-id,反之亦然。 reply-to头部会导致创建临时队列(请参阅上面的 临时队列目标)。- 某些带 x 前缀的 STOMP 头部会被翻译成可选的队列参数(见下文)。
可选队列属性
在 RabbitMQ 中,SEND 和 SUBSCRIBE 帧可以包含一组头部来配置队列行为,例如,使用 TTL 或类似的扩展。
支持的头部列表如下:
- x-message-ttl
- x-expires
- x-max-length
- x-max-length-bytes
- x-dead-letter-exchange
- x-dead-letter-routing-key
- x-max-priority
例如,如果您想使用带 STOMP 的优先级队列,您可以订阅(或发送)以下头部:
SUBSCRIBE
destination:/queue/my-priority-queue
x-max-priority:5
队列不可变性
一旦队列被声明,其属性就不能被更改。可选参数可以通过 策略进行修改。否则,必须删除并重新声明队列。这对 STOMP 客户端和 AMQP 0-9-1 都是如此。