RabbitMQ 消费者过载

RabbitMQ consumer overload

我一直在阅读有关 AMQP 消息传递确认的原则。 (https://www.rabbitmq.com/confirms.html)。文章真的很有帮助而且写得很好,但是关于消费者知识的一件特别的事情真的很令人困惑,这里是引用:

Another things that's important to consider when using automatic acknowledgement mode is that of consumer overload.

消费者超载?消息队列由代理处理并保存在 RAM 中(如果我理解正确的话)。它是关于什么超载?消费者是否有某种第二队列? 该文章的另一部分更加令人困惑:

Consumers therefore can be overwhelmed by the rate of deliveries, potentially accumulating a backlog in memory and running out of heap or getting their process terminated by the OS.

什么积压?这一切如何协同工作?消费者完成了哪一部分工作(当然除了消费消息和处理消息)?我认为经纪人正在保持队列活跃并转发消息,但现在我正在阅读一些神秘的积压和消费者超载。这真的很令人困惑,有人可以解释一下或者至少给我指出好的来源吗?

能够发出背压信号是分布式系统中的一个基本问题。没有明确的确认,消费者无法对经纪人说 "Slow down"。在 auto-ack 开启的情况下,一旦代理收到 TCP 确认,它就会从其 memory/disk 中删除消息。

但是,这并不意味着消费应用程序已处理消息或有足够的内存来存储传入的消息。文章中的backlog只是一个用来存储未处理消息的数据结构(在消费者应用中)

我相信您所指的文档处理的内容在我看来是 AMQP 0-9-1 或 RabbitMQ 实现中的某种设计缺陷。

考虑以下场景:

  • 一个队列中有数千条消息
  • 单个消费者订阅队列 AutoAck=true 且未设置 pre-fetch 计数

会发生什么?

RabbitMQ 的实现是向没有 pre-fetch 计数的客户端传送任意数量的消息。此外,对于 Auto-Ack,预取计数无关紧要,因为消息在交付给消费者时得到确认。

In-memory 缓冲区: 消费者的默认客户端 API 实现有一个 in-memory 缓冲区(在 .NET 中,它是某种类型的阻塞集合(如果我没记错的话)。因此,在处理消息之前,但在处理消息之后从经纪人那里收到,它进入这个 in-memory 持有区域。现在,设计缺陷是这个持有区域。消费者别无选择,只能接受来自经纪人的消息,因为它已发布给客户端异步。这是 AMQP protocol specification(见第 53 页)的缺陷。

因此,此时队列中的每条消息都将立即传递给消费者,消费者将被消息淹没。假设每条消息都很小,但需要 5 分钟来处理,那么这个消费者完全有可能在任何其他消费者附加到它之前耗尽整个队列。由于 AutoAck 开启,代理将在传递后立即忘记这些消息。

显然,如果您希望处理这些消息,这不是一个好方案,因为它们已经离开了代理的相对安全性,现在位于消费端点的 RAM 中。假设遇到一个使消费端点崩溃的异常 - 噗,所有消息都消失了。

如何解决这个问题?

你必须关闭Auto-Ack,通常设置合理的pre-fetch计数也是一个好主意(通常2-3就足够了)。