【问题标题】:RabbitMQ NACK messagesRabbitMQ NACK 消息
【发布时间】:2019-06-21 17:19:38
【问题描述】:

我的经验:在发布/订阅场景中,如果订阅者 nacks 一条消息,则该 nacks 消息会立即在队列的前面重新排队,这将是订阅者获得的下一条消息。

有没有办法避免这种情况?是否有可能以某种方式对一条消息进行 nack,使下一条消息肯定不是刚刚收到的消息?

我正在使用 Node.js 和 amqp.node 与 RabbitMQ 进行通信

【问题讨论】:

    标签: node.js rabbitmq amqp


    【解决方案1】:

    作为一般规则,您可以考虑,如果您的消费者正常运行,您应该始终确认消息,无论处理消息的结果如何(无论是错误还是成功)。

    您可以使用 requeue = false 拒绝/确认格式错误的消息,以将其路由到死信交换并对其执行操作(绑定队列并使用例如事后分析),或者选择确认并执行任何操作是必要的(例如发送其他消息,如 BadRequest 消息,...)。

    (几乎)永远不会出现问题的最佳方法是确认作为处理消息的最后一个操作,并准备好处理重新传递(幂等性可能会有所帮助)。您可以在交付时立即确认,但如果消费者崩溃,则消息会丢失(您需要自己处理这种情况 - 有很多模式)。

    希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      Nack 是 RabbitMQ 对 AMQP 协议的特定增强。它允许消息的消费者在消息没有成功处理时通知服务器。我想你已经走到这一步了。

      这里有趣的是,AMQP 最初并不认为Nack 是必要的。为什么?因为当消费者无法处理消息时,AMQP 行为是让消费者在没有ack-ing 消息的情况下关闭连接。在这种情况下,消息会自动按照原来的顺序重新排队,并传递给设置了redelivered 标志的下一个可用消费者。

      为什么会这样?

      因为Nack 表示消费者有问题,而不是消息。如果消息本身是错误的,AMQP 假设消费者会足够聪明地识别这一点,ack 消息,然后采取程序员设计的任何其他步骤来处理错误消息。

      RabbitMQ 添加了Nack 函数,该函数采用requeue 参数。默认情况下,requeue 为 true - 表示在 nack 后,代理将重新排队消息。这符合 AMQP 设计的初衷。但是,如果您将 requeue 传递为 false,则代理将死信消息。这是通常由智能应用程序架构师使用 AMQP 设计的行为的捷径,因此您可以将其视为对程序员的一种方便。

      两者的区别

      在第一种情况下,Nack 表明消息消费者存在问题。消费者在解决问题时应该让自己离线。在第二种情况下,Nack 表示消息存在问题。第一种情况是暂时性问题,而第二种情况是永久性故障。

      如果我不能现在处理某条特定消息,但以后可能会怎样?

      如果您的消息传递结构设计得当,这将永远不会是真的。如果一条特定消息需要与另一条消息不同的处理路径或资源,则该消息类型应该有自己的队列。当处理这些消息的依赖项脱机或不可用时,该队列的消费者可以停止消费。

      【讨论】:

      • “如果我现在不能处理特定的消息,但以后可能会怎样?如果你的消息结构设计得当,这永远不会是真的”。你是对的:在一个设计合理的系统中,这不会发生。但是,过去的遗留代码和错误可能会让我们今天不得不处理这些问题,而无法修复设计。我的情况是,处理消息可能会导致对 DB 的并发访问,从而由于设计不佳而导致序列化错误。不幸的是,重新设计现在不是一种选择(尽管我很想这样做),所以这就是我们必须处理的事情。
      • @smellyarmpits - 在某种程度上,您的实现将不得不考虑原始遗留设计并修复它。由于我不知道细节,我不能说你应该如何去做,但至少,你需要为每种消息类型设置一个队列。您不必同时从队列中读取,但您确实需要分离出不同类型的消息。除此之外,大多数数据库通过锁定处理并发。这可能不是一个容易解决的问题!
      • > 在某种程度上,您的实现将不得不考虑原始遗留设计并修复它。我完全同意,但我不负责这些决定:D
      • 我真的不知道该告诉你什么 - 你已经将不可变的行为描述为一个问题。如果行为不能改变,遗留代码也不能改变,那么可以改变什么?
      • 问题是消息已经在不同的队列上按类型划分了,但是不同的消息可能要在同一个数据库行上工作,导致并发问题(在多个消费者的情况下):2个消费者可能拿起 2 条不同的消息,它们引用相同的 db 行。如果发生这种情况,我想处理失败的消费者,以便重新排队稍后处理的消息,因为这种错误很可能会通过延迟处理来解决
      【解决方案3】:

      问题:我的经验:在发布/订阅场景中,如果订阅者 nacks 一条消息,则该 nacks 消息会立即重新排在队列的最前面,这将是订阅者收到的下一条消息。

      是的,这是正确的。当消息被重新排队时 channel.basicNack,它将被放置到它的原始位置 排队,如果可能的话。如果不是(由于同时交付和 当多个消费者共享一个消费者时,来自其他消费者的确认 queue),消息将被重新排队到更接近队列的位置 头。来源:https://www.rabbitmq.com/nack.html

      问题:有没有办法避免这种情况?是否有可能以某种方式对一条消息进行 nack,使下一条消息肯定不是刚刚收到的消息?

      nack 无法实现这一点。实现此目的的一种方法是将消息重新发布到需要 nack 的队列。

      • 获取消息并立即发回 ack。
      • 处理消息,如果成功则什么也不做。
      • 如果失败不发送 nack 而是重新发布消息回 排队。

      【讨论】:

      • 这是一个可能的解决方案,但是如果消费者在确认之后和重新发布之前失败,则可能会丢失数据。我想重新发布然后确认可能是一种可能的解决方法,但我不知道这是否是一个好的设计,或者它是否会导致明显的性能问题。
      • +1 表示关于重新发布的注释 - 虽然它可能不是 OP 正在寻找的内容,并且如果队列已经为空也无济于事,但它确实实现了将消息重新定位到末尾不管有什么线。
      • 重新排序 try { process message; } finally { if failed then republish; ack message } -- 有一个额外的好处,即处理中的异常仍然会做正确的事情。
      猜你喜欢
      • 2015-04-20
      • 1970-01-01
      • 1970-01-01
      • 2017-03-16
      • 2013-04-02
      • 2015-05-01
      • 2017-09-01
      • 2018-12-17
      • 1970-01-01
      相关资源
      最近更新 更多