【问题标题】:Message Ordering Across Queues跨队列的消息排序
【发布时间】:2015-04-09 18:11:01
【问题描述】:

我的 RabbitMQ 设置中有一个场景,我很好奇如何解决。下图说明了它(为简洁起见,删除了交换和大多数队列):

场景

  1. 生产者创建消息 A(1),它被顶级消费者接收,并开始处理该消息。
  2. 生产者创建消息 A(2),它被底层消费者接收(假设两个消费者都在循环交换中)。
  3. 底层消费者发布消息B(2),进入消息B消费者队列
  4. 可怜的慢速顶级消费者终于完成并发出它的消息 B(1)。

问题

如果我们假设 B 消费者不能是幂等的,我们如何确保两个 B 消息的结果都以正确的顺序应用?

我曾考虑使用应用于消息 A 的初始发布的时间戳,并让消费者维护上次更改的时间戳,拒绝该时间之前的任何时间戳,但这只有在每条消息导致完全相同的情况下才有效一种变化,需要大量跟踪。

我们将不胜感激有关如何解决此问题的其他想法。谢谢!

【问题讨论】:

标签: rabbitmq message-queue distributed-computing


【解决方案1】:

我不确定这里的 RabbitMQ 有什么特别之处,但是如果您有一个 single 生产者,使用时间戳的想法听起来是一个好的开始。

生产者在消息 A 上附加一个时间戳,每条消息 B 采用与其各自消息 A 相同的时间戳。

使用您的方法,某些消息将不会被处理,例如消息 B(1)。如果所有消息都应该由消费者 B 处理,但它们应该按确定性顺序进行处理,那么您可以进行确定性合并

消费者 B 配备了两个队列,每个消费者 A 一个队列。消费者 B 总是检查两个队列的顶部:

  • 如果两个队列都不为空,消费者 B 会弹出时间戳最低的消息。
  • 如果至少有一个队列为空,则消费者 B 等待。

使用这种方法,消费者 B 处理消息的顺序由生产者的时间戳给出,并且不会丢弃任何消息。假设是:

  • 队列是先进先出的
  • 没有进程崩溃
  • 总是这样,最终每个消费者 A 都会处理一条消息
  • 消费者 B 可以以非阻塞方式检查队列的顶部

【讨论】:

  • 感谢这个,非常有趣的想法。不幸的是,在我们的特定情况下实现起来非常困难,因为我们不能保证假设 3(最终每个消费者 A 都会产生一条消息)——上面的模型被稍微简化了,消费者 A 的数量可以动态扩展,所以消费者 B 的队列必须与此同步扩展。
  • 这确实让事情变得更难了。一些建议:(1)如果消费者 A 的集合是固定的,但它们有时是静默的(不发送消息),您可以通过定期从生产者发出心跳并强制消费者下游控制消息来填充它的消息来解决这个问题在消费者 B 中排队。
  • (2) 如果消费者 A 的集合发生变化,则此答案的方法将要求您在某处保持一致的成员资格集,例如,在生产者本身或 ZooKeeper 集合中。必须通知消费者 B 哪组消费者 A 处于活动状态的时间戳。同样,从生产者到消费者 B 的心跳可以携带这样的成员资格和时间戳信息。 (3) 关于 B 中的扩展队列:B 仍然可以有一个输入队列,并为从输入队列中出列但尚未处理的消息保留一组内部缓冲区。
  • 在我看来,唯一可行的解​​决方案是创建一个聚合器来聚合和重新排序消息 B(X)。聚合器应该由数据存储(内存和/或磁盘,例如 redis、rdbms)支持。为了能够聚合和重新排序消息,标识符 (UUID)、顺序和计数应该与消息 A(X) 相关联。聚合器将消息 B(X) 发布给消费者 B 并保证订购
猜你喜欢
  • 2014-11-01
  • 2014-08-23
  • 1970-01-01
  • 1970-01-01
  • 2013-10-14
  • 1970-01-01
  • 1970-01-01
  • 2014-01-22
  • 1970-01-01
相关资源
最近更新 更多