如何确保只有一个消费者实际使用已发布的消息?

Posted

技术标签:

【中文标题】如何确保只有一个消费者实际使用已发布的消息?【英文标题】:How do I ensure that only one consumer actually consumes a published message? 【发布时间】:2018-12-09 11:59:23 【问题描述】:

我使用带有微服务架构的 Rabbitmq。我对我的许多用例使用主题和直接交换,并且效果很好。但是我有一个用例,我必须从数据库中删除一条记录。当我删除记录时,需要调用其他几个服务并维护/删除引用的记录。我可以通过简单地通过直接交换调用这些服务来实现这一点,但我读到它是编排首选而不是编排。这意味着我应该实现发布/订阅模式(rabbitmq 中的扇出)。 我的问题是,如果我在分布式系统中使用发布/订阅模式,如何确保只有一个服务实例使用发布的消息?

【问题讨论】:

【参考方案1】:

您的问题与发布-订阅有关,而与基本消息处理有关。根本问题是您能否保证某项操作将被准确执行一次。简短的回答是,您可能希望使用直接交换,以便消息进入一个队列并由一个(可能是多个)消费者处理。

长答案是不能保证“恰好一次”,因此您需要将这部分作为您的设计。

背景

让消息处理成为幂等操作是最佳实践。事实上,幂等性是几乎所有外部接口的关键设计假设(我认为它在内部接口中同样重要)。

此外,您应该意识到无法保证“仅一次”交付。从数学上讲,不能做出这样的保证。相反,您可以拥有以下两种情况之一(相互排斥):

最多一次交付 (0 至少一次交付 (1

来自 RabbitMQ 文档:

使用确认保证至少一次交付。如果没有确认,则在发布和消费操作期间可能会丢失消息,并且只能保证最多一次传递。

在发布和使用消息时会发生一些事情。由于消息处理系统的异步特性,特别是 AMQP 协议,没有办法保证只处理一次,同时仍然产生消息系统所需的性能(基本上,试图确保精确一次会强制一切都通过重复数据删除点的串行过程进行)。

设计意义

鉴于上述情况,您的设计依赖于“至少一次”交付非常重要。对于删除操作,这涉及重写该操作的定义,使其具有断言性而非程序性(例如,“删除 this”变为“确保 this 不存在”。 )。不同之处在于您描述的是最终状态而不是过程。

【讨论】:

【参考方案2】:

我认为你应该为每个服务有一个单独的队列,该实例应该被通知数据库记录删除。交换器将消息的副本放入所有队列中。服务实例竞争访问专用队列(只有一个得到消息)。

【讨论】:

以上是关于如何确保只有一个消费者实际使用已发布的消息?的主要内容,如果未能解决你的问题,请参考以下文章

确保已使用 REST 代理从 Kafka 主题读取所有消息

如何确保消息正确地发送至RabbitMQ? 如何确保消息接收方消费了消息?

RabbitMQ / AMQP 中的消息组

Kafka - 使用高级消费者的延迟队列实现

RabbitMQ面试题:如何确保消息不丢失? --- 2022-04-03

如何使用rabbitmq同步消费者