跳到主要内容
版本: 4.1

直接回复

概述

直接回复是一个功能,它允许 RPC(请求/回复)客户端采用类似于教程 6中演示的设计,而无需创建回复队列。

重要

客户端使用显式声明的队列(包括长期存在的客户端命名队列和连接特定的独占队列)的请求-回复实现与直接回复一样有效,并且具有其优点,特别是对于具有长时间运行任务的工作负载。

动机

RPC(请求/回复)是使用像 RabbitMQ 这样的消息代理实现的常用模式。教程 6 演示了其与各种客户端的实现。 典型的做法是让 RPC 客户端发送路由到长期存在的(已知的)服务器队列的请求。 RPC 服务器从该队列中消费请求,然后使用客户端在 reply-to 标头中命名的队列向每个客户端发送回复。

客户端的队列从哪里来? 客户端可以为每个请求-响应对声明一个一次性队列。 但这是低效的;即使是未复制的队列,创建然后再删除也可能很昂贵(与发送消息的成本相比)。 在集群中尤其如此,因为所有集群节点都需要同意队列已创建,同意其类型、复制参数和其他元数据。

因此,客户端应为多个 RPC 请求创建一个单独的回复队列。

此回复队列的属性取决于用例

  • 独占队列 通常在回复由单个客户端消费并在断开连接时删除时使用
  • 非独占的长期存在的队列 更适合长时间运行的任务,确保即使客户端暂时断开连接,回复也能持久存在

直接回复消除了对回复队列的需求。 这有利于具有短生命周期队列和瞬态响应的请求-回复实现,但代价是放弃了对如何存储响应的所有控制。

使用直接回复,RPC 客户端将直接从其 RPC 服务器接收回复,而无需通过回复队列。“直接”在此处仍然意味着通过相同的通道和 RabbitMQ 节点;RPC 客户端和 RPC 服务器进程之间没有直接网络连接。

如何使用直接回复

要使用直接回复,RPC 客户端应

  • 从伪队列 amq.rabbitmq.reply-to 以 no-ack 模式消费。 没有必要首先声明这个“队列”,尽管客户端如果愿意可以这样做。

  • 将其请求消息中的 reply-to 属性设置为 amq.rabbitmq.reply-to

然后,RPC 服务器将看到一个带有生成名称的 reply-to 属性。 它应该发布到默认交换机 (""),并将路由键设置为此值(即,就像它通常发送到回复队列一样)。 然后,消息将直接发送到客户端消费者。

如果 RPC 服务器将要执行一些昂贵的计算,它可能希望检查客户端是否已离开。 为此,服务器可以首先在一次性通道上声明生成的回复名称,以确定它是否仍然存在。 请注意,即使您使用 passive=false 声明“队列”,也无法创建它;声明只会成功(有 0 条消息准备就绪和 1 个消费者)或失败。

注意事项和限制

  • RPC 客户端必须以自动确认模式进行消费。 这是因为如果没有回复队列,回复消息在客户端断开连接或拒绝回复消息时无法返回。

  • RPC 客户端必须使用相同的连接和通道,既用于从 amq.rabbitmq.reply-to 消费,又用于发布请求消息。

  • 使用此机制发送的回复消息通常不具备容错能力;如果发布原始请求的客户端随后断开连接,它们将被丢弃。 假设是 RPC 客户端将重新连接并在这种情况下提交另一个请求。

  • 名称 amq.rabbitmq.reply-tobasic.consumereply-to 属性中被用作队列;但它不是。 它无法删除,并且不会出现在管理插件或 rabbitmqctl list_queues 中。

  • 如果 RPC 服务器发布时设置了 mandatory 标志,则 amq.rabbitmq.reply-to.* 被视为不是队列;即,如果服务器仅发布到此名称,则该消息将被视为“未路由”;如果设置了 mandatory 标志,则将发送 basic.return

何时使用直接回复

虽然客户端应使用长期存在的连接,但直接回复非常适合具有高连接流失率的工作负载,在这种情况下,客户端为单个 RPC 建立连接并在之后立即断开连接。 通过避免在元数据存储中创建队列元数据,直接回复可以减少开销和延迟。

对于具有长期存在的连接且客户端执行多个 RPC 的工作负载,与经典队列相比,直接回复的性能优势并不显著。 现代 RabbitMQ 版本已针对低延迟和最小资源使用量优化了经典队列,使其在这种情况下同样高效。

© . All rights reserved.