RabbitMQ在生产系统上修改队列参数



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

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

在生产系统中切换到这些新队列而不丢失消息的最佳方法是什么?

我考虑了所有的事情,从队列名称的版本控制到用新的设置创建一个新的vhost,再到在适当的地方做所有的更改。

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

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

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

  3. 不同出版商和消费者群体之间的一般协调(或者,更好的是,一种避免需要协调他们的方法)。

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

进行反向不兼容的更改是一个常见的问题,特别是在异构环境中,特别是当多个应用程序试图以自己的方式声明相同的实体(具有特定的设置)时。在多个应用程序中同时更改队列声明没有简单的方法,它高度依赖于整个工作过程的组织方式,应用程序的关键程度,基础设施等。

快捷方式:

虽然发布者不处理队列声明和绑定(至少他们不应该这样做),但您可以关注消费者。在try-except块中包装队列声明可能是一种快速而肮脏的选择。此外,大多数项目,甚至很多项目都可以在小的停机时间内存活下来,所以你可以在一个shell中阻塞rabbitmq用户,按照你的意愿改变队列(创建一个新的队列,让你的消费者使用它,而不是旧的队列),然后解除阻塞用户,让消费者像以前一样工作(你的工人在监督或监控之下,对吗?)然后手动将消息从旧队列迁移到新队列。

快速安全解决方案:

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

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

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

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

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

更新:

你可能会发现非常有用的Shovel插件,特别是动态Shovel,它可以在交换和队列之间移动消息,甚至在不同的vhost和服务器之间。这是在队列/交换器之间迁移消息的最快和最安全的方法。

最新更新