生产系统中如何更改RabbitMQ队列参数

23

我在面向服务的架构中使用RabbitMQ作为消息队列,多个独立的Web服务发布消息绑定到RabbitMQ队列。这些队列又被各种消费者订阅,执行后台任务; 这是RabbitMQ的一个非常普通的用例。

现在,我想改变一些队列参数(具体而言,我想将队列绑定到一个新的死信交换机,并使用特定的路由键)。我的问题是,在生产系统中直接进行此更改有几个问题。

最好的方法是什么,可以在不丢失生产系统中的消息的情况下过渡到这些新队列?

我已经考虑了从版本控制队列名称到创建具有新设置的新vhost到直接就地进行所有更改等方案。

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

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

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

  3. 不同的发布者和使用者之间的一般协调(或者更好的方式是避免需要协调它们)。

1个回答

29

队列绑定可以在运行时添加和删除,不会对客户端造成任何影响,除非客户端手动修改绑定。因此,如果您的问题仅涉及绑定,请通过CLI或Web管理面板更改它们,跳过下面所述的内容。

在异构环境中进行不兼容的更改是一个常见的问题,尤其是当多个应用程序尝试以自己的方式声明相同的实体时(具有它们特定的设置),这种情况尤其严重。没有简单的方法可以同时更改多个应用程序中的队列声明,这高度取决于整个工作流程的组织方式,您的应用程序有多么关键,您的基础设施等等。

快速而肮脏的方法:

虽然发布者不涉及队列声明和绑定(至少不应该这样做),但您可以专注于消费者。将队列声明包装在try-except块中可能是快速而肮脏的选择。此外,大多数项目,即使是众多的项目,也可以承受小的停机时间,因此您可以在一个shell中阻止RabbitMQ用户,按照您的意愿更改队列(创建新队列并让您的消费者使用它而不是旧队列),然后解除用户阻止,让消费者像以前一样工作(您的工作者受到监督或monit的管辖吧?)。然后手动将消息从旧队列迁移到新队列。

快速且安全的解决方案:

这是一个有些棘手的技巧,基于如何在单个虚拟主机中从一个队列迁移消息到另一个队列。整个解决方案在单个虚拟主机内工作,但需要为每个要修改的队列设置额外的队列。在源队列上设置死信交换,并将其指向路由过期消息到您的新目标队列。然后对源队列应用每个队列消息 TTL,将x-message-ttl=0设置为最小值(请参见有关完全没有排队的立即交付的注意事项)。这两个操作都可以通过CLI或管理面板完成,并且可以在已声明的队列上执行。这样,您的发布者可以像往常一样发布消息,甚至旧的消费者也可以按预期方式工作,但是与此同时,新的消费者可以从可以手动预先声明新参数的新队列中进行消费。

请注意,在具有大量消息和庞大消息流的队列上,存在遇到流控制限制的风险,特别是如果您的服务器几乎利用了所有资源。

更加复杂但更加安全的方法(适用于整个消息工作流逻辑已更改的情况):

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

P.S.:在上述任何情况下,如果可以的话,请让老客户将队列清空,这样您就不需要手动迁移消息。

更新:

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


谢谢你的回答。对我来说,vhost解决方案也是最干净的,但它引入了一个问题:所有分布式发布者都必须跟踪他们发布的位置,并在每次更改时进行更新。有没有一种简单的方法让他们只需指向同一个地方,而我们可以在其他地方将其路由到正确的Vhost?如果可以避免,我认为发布者不应该关心这些细节。但也许这更像是一个系统管理员的问题,而不是RabbitMQ的问题。 - DevinR
通常,vhost 是可配置选项(或者至少应该是),因此更改它不应该成为问题。请注意,如果您不想(或者负担不起)停机时间,只需并行运行新的发布者和消费者,然后停止原始的一个,将消息从旧的 vhost 迁移到新的 vhost。但是,如果可能的话,请确保在第一阶段队列声明失败之后使您的应用程序继续运行,然后仅将新更改应用于生产者和消费者。 - pinepain
我已经添加了一个解决方案(快速且安全的解决方案),可以方便地对队列声明进行热更改,例如将其设置为持久化、自动删除或独占等。但在按照此方法操作之前,请确保您知道自己在做什么。 - pinepain
如果您对任何情况不确定,可以通过在RabbitMQ用户组中提出相同的问题来双重检查解决方案-https://groups.google.com/forum/#!forum/rabbitmq-discuss,并指向我们讨论的链接。 - pinepain
我更新了我的答案,并添加了关于铲子插件的说明,希望能对某些人有所帮助。 - pinepain
请注意,x-message-ttl 可以为 0,表示立即传递 - André Laszlo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接