我正在使用RabbitMQ作为面向服务的体系结构中的消息队列,其中许多单独的Web服务发布绑定到RabbitMQ队列的消息。这些队列由各种消费者订阅,这些消费者执行后台工作; RabbitMQ的一个漂亮的香草用例。
现在我想更改一些队列参数(具体来说,我想将队列绑定到带有某个路由键的新死信交换)。我的问题是,由于几个原因,在生产系统上进行这种更改是有问题的。
对我来说,转换到这些新队列的最佳方式是什么,而不会丢失生产系统中的消息?
我已经考虑了从版本化队列名称到使用新设置制作新vhost以进行所有更改的所有内容。
以下是我面临的一些问题:
因为RabbitMQ队列是幂等的,所以不同的Web服务在发布之前已经声明了队列(如果它们尚不存在)。更改队列参数(但保持相同的路由键)后,队列声明失败,RabbitMQ关闭通道。
我想在更改队列时不丢失消息(这里我打算订阅一个保存消息然后重新发布到新队列的独占消费者)。
不同发布商与消费者群体之间的一般协调(或者更好的是,避免需要协调它们的方法)。
答案 0 :(得分:18)
可以在运行时添加和删除队列绑定,而不会对客户端产生任何影响,除非客户端手动修改绑定。因此,如果您关于绑定的问题只是通过CLI或Web管理面板进行更改,请跳过下面的内容。
进行反向不兼容的更改是一个常见问题,尤其是在异构环境中,尤其是当多个应用程序尝试以自己的方式(使用其特定设置)声明相同的实体时。在多个应用程序中,没有简单的方法可以同时更改队列声明,这在很大程度上取决于整个工作流程的组织方式,应用程序的关键程度,基础架构的等等。
快速而肮脏的方式:
虽然发布者不处理队列声明和绑定(至少他们不应该这样做),但您可以专注于消费者。在try-except块中包装队列声明可能是快速而肮脏的选择。此外,大多数项目,甚至众多项目都可以在较短的停机时间内存活,因此您可以在一个shell中阻止rabbitmq用户,根据需要更改队列(创建新的并使您的消费者使用它而不是旧的),然后取消阻止用户并让消费者像之前(你的工人在主管或监督下,对吧?)。然后手动将消息从旧队列迁移到新队列。
快速安全的解决方案:
是否有点棘手并基于黑客如何在单个vhost内将消息从一个队列迁移到另一个队列。整个解决方案在单个vhost中运行,但是对于要修改的每个队列都需要额外的队列。在源队列上设置Dead Letter Exchanges并指向它以将过期的消息路由到新的目标队列。然后将Per-Queue Message TTL应用于源队列,设置x-message-ttl=0
(设置为最小值,请参阅No Queueing at all有关即时投放的说明)。这两个操作都可以通过CLI或管理面板完成,并且可以在已声明的队列中完成。通过这种方式,您的发布者可以像往常一样发布消息,甚至老消费者也可以第一次按预期工作,但同时新的消费者可以使用新队列消费,新队列可以手动或以其他方式使用新的args预先声明。
请注意,在具有大消息数和大量消息流的队列上,存在一些风险,以满足流量控制限制,特别是如果您的服务器几乎使用所有资源。
更复杂但更安全的方法(对于整个消息工作流逻辑发生变化的情况):
对应用程序进行所有必要的更改并与现有的代码库并行运行新的代码库,但是在不同的RabbitMQ vhost上(或者甚至使用单独的服务器,它取决于您的应用程序负载和硬件)。实际上,有可能在同一个虚拟主机上运行,但更改交换和队列名称,但它甚至听起来不好,甚至以书面形式闻起来。设置新应用程序后,使用旧应用程序切换它们并运行从旧队列到新队列的消息迁移(或者让旧系统清空队列)。它可以保证无缝迁移,最大限度地减少停机时间。如果您的部署自动化,整个过程不会花费太多精力。
P.S。:在上述任何情况下,如果可以的话,让旧的消费者排空队列,这样你就不需要手动迁移消息了。
<强>更新强>
您可能会发现非常有用的Shovel plugin,特别是Dynamic Shovels,可以在交换机和队列之间移动消息,甚至可以在不同的虚拟机和服务器之间移动消息。这是在队列/交换之间迁移消息的最快,最安全的方法。