Pub/Sub 和消息队列中多个消费者的消息传递保证
Message Delivery Guarantee for Multiple Consumers in Pub/Sub and Messaging Queues
要求
一个系统经历了一些状态变化,系统的多个其他部分必须知道这一点(我们称它们为观察者),以便它们可以根据当前状态执行一些动作,观察者的动作很重要,如果部分观察者不在线(由于一些问题目前没有监听,但很快就会回来),在所有观察者都收到消息之前不要丢弃消息。
尝试用pub/sub模型完成这个,这是我的发现,(如果理解有误,请更正)-
发布者在特定主题上创建一个事件,多个订阅者可以使用相同的消息。该模型要么不提供交付保证(在 Redis 中),要么保证交付一次(使用消息队列),即。当其中一个消费者确认一条消息时,该消息将被丢弃(rabbitmq)。
例子
在数据库中创建了一个新的个人资料实体
现在,
- 后台验证服务必须知道这一点才能触发验证过程。
- 订阅服务必须知道这一点才能为用户添加默认订阅。
现在这两项任务都很重要,互不相关,可以 运行 并行进行。
现在在队列模型中,如果订阅服务由于某种原因关闭,BG 验证过程会确认消息,消息将从队列中删除,或者如果它像大多数 pub/sub, 无论如何不能保证这两种服务的交付。
还有一点,两个任务没有关联,不需要一个接一个触发。
简而言之,我的需要是确保所有消费者都收到相同的消息,并且他们应该能够单独确认 , 消息应该 只有在所有消费者确认后才被驱逐 上述任何一种方法都不会这样做。
我在这里遗漏了什么吗?我应该如何解决这个问题?
正如你提到的,这不是你可以用 Redis Pub/Sub 数据结构控制的东西。
但您可以使用 Redis Streams 轻松完成。
Streams 将允许您使用 XADD
命令 post 消息,然后控制哪些消费者正在处理消息并确认消息已被处理。
您可以查看这些提供(在 Java 中)示例的示例应用程序:
- post发送和消费消息
- 创建多个消费者组
- 管理异常
链接:
- Getting Started with Redis Streams and Java
- Redis Streams in Action (展示如何使用 ADD/ACK/PENDING/CLAIM 并使用 Redis Streams 和 SpringData 构建防错流应用程序的项目)
RabbitMQ's model 明确支持此方案,它将“交换”与“队列”分开:
- 发布者总是将消息发送到“交换”,它只是一个无状态的路由地址;它不需要知道消息应该在哪个队列中结束
- 消费者总是从“队列”中读取消息,无论消息来自何处,该队列都包含自己的消息副本
- 多个消费者可以订阅同一个队列,每条消息都会被投递给恰好一个消费者
- 至关重要的是,交换器可以将同一条消息路由到 多个 队列,每个队列都会收到 copy 消息
这里要理解的关键是,当我们谈论消费者“订阅”队列时,“pub-sub”设置的“订阅”部分实际上是从交换器到队列的路由.
因此 RabbitMQ pub-sub 系统可能如下所示:
- 在数据库中创建了一个新的个人资料实体
- 此事件作为消息发布到“事件”topic exchange,路由键为“entity.profile.created”
- 交换路由消息的副本到多个队列:
- “verification_service”队列已绑定到此交换以接收匹配“entity.profile.#”的所有消息的副本“
- “subscription_setup_service”队列已绑定到此交换,以接收与“entity.profile.created”匹配的所有消息的副本
- 消费脚本对此路由一无所知,它们只知道消息将出现在与它们相关的事件队列中:
- 验证服务在“verification_service”队列中获取消息副本,进行处理并确认
- 订阅设置服务在“subscription_setup_service”队列中获取消息的副本、处理并确认它
- 如果有多个消费脚本查看同一个队列,它们将在它们之间共享那个队列上的消息,但是仍然完全独立于任何其他队列。
这是来自 this interactive visualisation tool 的屏幕截图,显示了这种情况:
要求
一个系统经历了一些状态变化,系统的多个其他部分必须知道这一点(我们称它们为观察者),以便它们可以根据当前状态执行一些动作,观察者的动作很重要,如果部分观察者不在线(由于一些问题目前没有监听,但很快就会回来),在所有观察者都收到消息之前不要丢弃消息。
尝试用pub/sub模型完成这个,这是我的发现,(如果理解有误,请更正)-
发布者在特定主题上创建一个事件,多个订阅者可以使用相同的消息。该模型要么不提供交付保证(在 Redis 中),要么保证交付一次(使用消息队列),即。当其中一个消费者确认一条消息时,该消息将被丢弃(rabbitmq)。
例子
在数据库中创建了一个新的个人资料实体
现在,
- 后台验证服务必须知道这一点才能触发验证过程。
- 订阅服务必须知道这一点才能为用户添加默认订阅。
现在这两项任务都很重要,互不相关,可以 运行 并行进行。
现在在队列模型中,如果订阅服务由于某种原因关闭,BG 验证过程会确认消息,消息将从队列中删除,或者如果它像大多数 pub/sub, 无论如何不能保证这两种服务的交付。
还有一点,两个任务没有关联,不需要一个接一个触发。
简而言之,我的需要是确保所有消费者都收到相同的消息,并且他们应该能够单独确认 , 消息应该 只有在所有消费者确认后才被驱逐 上述任何一种方法都不会这样做。
我在这里遗漏了什么吗?我应该如何解决这个问题?
正如你提到的,这不是你可以用 Redis Pub/Sub 数据结构控制的东西。
但您可以使用 Redis Streams 轻松完成。
Streams 将允许您使用 XADD
命令 post 消息,然后控制哪些消费者正在处理消息并确认消息已被处理。
您可以查看这些提供(在 Java 中)示例的示例应用程序:
- post发送和消费消息
- 创建多个消费者组
- 管理异常
链接:
- Getting Started with Redis Streams and Java
- Redis Streams in Action (展示如何使用 ADD/ACK/PENDING/CLAIM 并使用 Redis Streams 和 SpringData 构建防错流应用程序的项目)
RabbitMQ's model 明确支持此方案,它将“交换”与“队列”分开:
- 发布者总是将消息发送到“交换”,它只是一个无状态的路由地址;它不需要知道消息应该在哪个队列中结束
- 消费者总是从“队列”中读取消息,无论消息来自何处,该队列都包含自己的消息副本
- 多个消费者可以订阅同一个队列,每条消息都会被投递给恰好一个消费者
- 至关重要的是,交换器可以将同一条消息路由到 多个 队列,每个队列都会收到 copy 消息
这里要理解的关键是,当我们谈论消费者“订阅”队列时,“pub-sub”设置的“订阅”部分实际上是从交换器到队列的路由.
因此 RabbitMQ pub-sub 系统可能如下所示:
- 在数据库中创建了一个新的个人资料实体
- 此事件作为消息发布到“事件”topic exchange,路由键为“entity.profile.created”
- 交换路由消息的副本到多个队列:
- “verification_service”队列已绑定到此交换以接收匹配“entity.profile.#”的所有消息的副本“
- “subscription_setup_service”队列已绑定到此交换,以接收与“entity.profile.created”匹配的所有消息的副本
- 消费脚本对此路由一无所知,它们只知道消息将出现在与它们相关的事件队列中:
- 验证服务在“verification_service”队列中获取消息副本,进行处理并确认
- 订阅设置服务在“subscription_setup_service”队列中获取消息的副本、处理并确认它
- 如果有多个消费脚本查看同一个队列,它们将在它们之间共享那个队列上的消息,但是仍然完全独立于任何其他队列。
这是来自 this interactive visualisation tool 的屏幕截图,显示了这种情况: