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

cod*_*eme 2 distributed-system publish-subscribe rabbitmq spring-boot

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

the*_*yer 5

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

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

背景

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

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

  • 最多发送一次 (0 < n <= 1)
  • 至少交付一次 (1 <= n)

来自 RabbitMQ 文档:

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

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

设计意义

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