20

我在面向服务的体系结构中使用 RabbitMQ 作为消息队列,其中许多单独的 Web 服务发布绑定到 RabbitMQ 队列的消息。这些队列依次由执行后台工作的各种消费者订阅;RabbitMQ 的一个非常普通的用例。

现在我想更改一些队列参数(具体来说,我想将队列绑定到具有特定路由键的新死信交换)。我的问题是,由于几个原因,在生产系统上进行这种更改是有问题的。

在生产系统中不丢失消息的情况下,让我过渡到这些新队列的最佳方式是什么?

我已经考虑了从版本控制队列名称到使用新设置创建新虚拟主机到进行所有更改的所有内容。

以下是我面临的一些问题:

  1. 因为 RabbitMQ 队列是幂等的,不同的 Web 服务在发布到它们之前已经声明了队列(以防它们不存在)。一旦更改了队列参数(但保持相同的路由键),队列声明失败并且 RabbitMQ 关闭通道。

  2. 我希望在更改队列时不会丢失消息(在这里我计划订阅一个独占消费者来保存消息然后重新发布到新队列)。

  3. 不同的发布者和消费者群之间的一般协调(或者,甚至更好的一种避免需要协调它们的方法)。

4

1 回答 1

28

队列绑定可以在运行时添加和删除,而不会对客户端产生任何影响,除非客户端手动修改绑定。因此,如果您的问题只是关于绑定的问题,只需通过 CLI 或 Web 管理面板更改它们并跳过下面写的内容。

进行向后不兼容的更改是一个常见问题,尤其是在异构环境中,尤其是当多个应用程序尝试以自己的方式(使用其特定设置)声明同一实体时。没有简单的方法可以在多个应用程序中同时更改队列声明,这在很大程度上取决于整个工作流程的组织方式、应用程序的重要性、基础架构等。

快速而肮脏的方式:

虽然发布者不处理队列声明和绑定(至少他们不应该这样做),但您可以专注于消费者。在 try-except 块中包装队列声明可能是快速而肮脏的选择。此外,大多数项目,甚至许多项目都可以在短暂的停机时间内幸存下来,因此您可以在一个 shell 中阻止 rabbitmq 用户,根据需要更改队列(创建新的并让您的消费者使用它而不是旧的),然后取消阻止用户并让消费者像之前(你的工人在主管或监控之下,对吧?)。然后手动将消息从旧队列迁移到新队列。

快速安全的解决方案:

这有点棘手,并且基于如何将消息从一个队列迁移到单个 vhost 中的另一个队列。整个解决方案在单个 vhost 中工作,但您要修改的每个队列都需要额外的队列。在源队列上设置死信交换并将其指向将过期消息路由到新目标队列。然后将Per-Queue Message TTL应用到源队列,设置x-message-ttl=0(设置为最小值,请参阅No Queuing at all关于立即交货的说明)。这两个操作都可以通过 CLI 或管理面板完成,并且可以在已声明的队列上完成。通过这种方式,您的发布者可以像往常一样发布消息,甚至老消费者也可以首次按预期工作,但同时新消费者可以从新队列中消费,该队列可以手动或以其他方式使用新参数预先声明。

请注意,在具有大量消息数和大量消息流的队列上,存在满足流控制限制的一些风险,尤其是如果您的服务器几乎利用了所有资源。

更复杂但更安全的方法(对于整个消息工作流逻辑发生变化的情况):

对应用程序进行所有必要的更改并与现有代码库并行运行新代码库,但在不同的 RabbitMQ 虚拟主机上(甚至使用单独的服务器,这取决于您的应用程序负载和硬件)。实际上,可能可以在同一个虚拟主机上运行,​​但更改交换和队列名称,但它甚至听起来并不好,即使是书面形式也有异味。设置新应用程序后,将它们与旧应用程序切换并运行消息从旧队列迁移到新应用程序(或让旧系统清空队列)。它保证以最少的停机时间进行无缝迁移。如果您的部署自动化,整个过程将不会花费太多精力。

PS:在以上任何情况下,如果可以的话,让老消费者清空队列,这样你就不需要手动迁移消息了。

更新:

您可能会发现非常有用的Shovel 插件,尤其是Dynamic Shovel,用于在交换和队列之间移动消息,甚至在不同的虚拟主机和服务器之间移动消息。这是在队列/交换之间迁移消息的最快和最安全的方式。

于 2014-08-13T09:31:39.393 回答