【发布时间】:2019-06-21 17:19:38
【问题描述】:
我的经验:在发布/订阅场景中,如果订阅者 nacks 一条消息,则该 nacks 消息会立即在队列的前面重新排队,这将是订阅者获得的下一条消息。
有没有办法避免这种情况?是否有可能以某种方式对一条消息进行 nack,使下一条消息肯定不是刚刚收到的消息?
我正在使用 Node.js 和 amqp.node 与 RabbitMQ 进行通信
【问题讨论】:
我的经验:在发布/订阅场景中,如果订阅者 nacks 一条消息,则该 nacks 消息会立即在队列的前面重新排队,这将是订阅者获得的下一条消息。
有没有办法避免这种情况?是否有可能以某种方式对一条消息进行 nack,使下一条消息肯定不是刚刚收到的消息?
我正在使用 Node.js 和 amqp.node 与 RabbitMQ 进行通信
【问题讨论】:
作为一般规则,您可以考虑,如果您的消费者正常运行,您应该始终确认消息,无论处理消息的结果如何(无论是错误还是成功)。
您可以使用 requeue = false 拒绝/确认格式错误的消息,以将其路由到死信交换并对其执行操作(绑定队列并使用例如事后分析),或者选择确认并执行任何操作是必要的(例如发送其他消息,如 BadRequest 消息,...)。
(几乎)永远不会出现问题的最佳方法是确认作为处理消息的最后一个操作,并准备好处理重新传递(幂等性可能会有所帮助)。您可以在交付时立即确认,但如果消费者崩溃,则消息会丢失(您需要自己处理这种情况 - 有很多模式)。
希望这会有所帮助。
【讨论】:
Nack 是 RabbitMQ 对 AMQP 协议的特定增强。它允许消息的消费者在消息没有成功处理时通知服务器。我想你已经走到这一步了。
这里有趣的是,AMQP 最初并不认为Nack 是必要的。为什么?因为当消费者无法处理消息时,AMQP 行为是让消费者在没有ack-ing 消息的情况下关闭连接。在这种情况下,消息会自动按照原来的顺序重新排队,并传递给设置了redelivered 标志的下一个可用消费者。
为什么会这样?
因为Nack 表示消费者有问题,而不是消息。如果消息本身是错误的,AMQP 假设消费者会足够聪明地识别这一点,ack 消息,然后采取程序员设计的任何其他步骤来处理错误消息。
RabbitMQ 添加了Nack 函数,该函数采用requeue 参数。默认情况下,requeue 为 true - 表示在 nack 后,代理将重新排队消息。这符合 AMQP 设计的初衷。但是,如果您将 requeue 传递为 false,则代理将死信消息。这是通常由智能应用程序架构师使用 AMQP 设计的行为的捷径,因此您可以将其视为对程序员的一种方便。
两者的区别
在第一种情况下,Nack 表明消息消费者存在问题。消费者在解决问题时应该让自己离线。在第二种情况下,Nack 表示消息存在问题。第一种情况是暂时性问题,而第二种情况是永久性故障。
如果我不能现在处理某条特定消息,但以后可能会怎样?
如果您的消息传递结构设计得当,这将永远不会是真的。如果一条特定消息需要与另一条消息不同的处理路径或资源,则该消息类型应该有自己的队列。当处理这些消息的依赖项脱机或不可用时,该队列的消费者可以停止消费。
【讨论】:
问题:我的经验:在发布/订阅场景中,如果订阅者 nacks 一条消息,则该 nacks 消息会立即重新排在队列的最前面,这将是订阅者收到的下一条消息。
是的,这是正确的。当消息被重新排队时 channel.basicNack,它将被放置到它的原始位置 排队,如果可能的话。如果不是(由于同时交付和 当多个消费者共享一个消费者时,来自其他消费者的确认 queue),消息将被重新排队到更接近队列的位置 头。来源:https://www.rabbitmq.com/nack.html
问题:有没有办法避免这种情况?是否有可能以某种方式对一条消息进行 nack,使下一条消息肯定不是刚刚收到的消息?
nack 无法实现这一点。实现此目的的一种方法是将消息重新发布到需要 nack 的队列。
【讨论】:
try { process message; } finally { if failed then republish; ack message } -- 有一个额外的好处,即处理中的异常仍然会做正确的事情。