在微服务的 publish/subscribe 模型中,如何 receive/consume 每个服务类型只发送一次消息

In a publish/subscribe model in microservices, how to receive/consume a message only once per service type

我们正在设计一个微服务架构模型,其中服务 A 发布消息,服务 B 和 C 想要 receive/consume 消息。但是,对于高可用性,服务 B 和 C 的多个实例同时 运行。现在的问题是我们如何设计使得只有 B 的一个服务实例和 C 的一个服务实例接收消息,而不是所有其他服务实例。

据我对 RabbitMQ 的了解,要实现这种行为并不容易。不知道Kafka或者其他消息框架是否内置了这种场景的支持,我相信这在微服务架构中应该是很常见的。

Kafka 内置了对这种场景的支持。

您可以创建两个 Consumer Groups,一个用于 B,另一个用于 CConsumer Groups 都订阅了来自 A.

的消息

A 发布的任何消息都将发送到两个组。但是,每个组只有一个成员可以收到消息。

Kafka 有一个名为 Consumer Groups 的功能,它完全符合您的描述。

B 的每个相同实例都可以将其 group.id 声明为相同的字符串(比如 "serviceB"),Kafka 将确保为每个实例分配一组互斥的主题分区集它订阅的主题。

由于 C 的所有实例都将具有不同的 group.id(例如 "serviceC"),因此它们也将获得与 B 的实例相同的消息,但它们将位于一个独立的消费者组中,因此消息仅发送到 C 的 N 个实例中的 1 个,最多为最大实例数,即主题分区的总数。

您可以动态独立地增加或减少 B 和 C 的实例数量。如果任何实例死亡,其余实例将自动重新平衡分配给它们的主题分区并接管死亡实例的消息处理.

数据永远不必存储超过一次,因此所有这些服务实例仍然只有一个提交日志或 "source of truth"。

这是您需要执行的更改以实现与 Rabbit MQ 相同的效果

  • 创建 2 个单独的队列,每个队列用于 B 和 C 服务
  • 更改您的逻辑以从队列中读取消息,这样只有一个 实例将从队列中读取消息,使用阻塞 rabbitmq的连接物。

这样,当 B 和 C 的多个实例 运行 都将收到消息并且仍然是可扩展的。

您还可以使用命令行工具在 kafka 中测试此用例。

您使用

创建制作人
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test

然后,您可以使用

创建两个不同的消费者组(cgB、cgC)
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning --consumer-property group.id=cgB

bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning --consumer-property group.id=cgC

一旦您向主题发送消息,两组 (B,C) 都会收到该消息,但会保存他们独立处理的消息。

这里有更好的解释:Kafka quickstart