RabbitMQ PRECONDITION_FAILED - 未知的交付标签

RabbitMQ PRECONDITION_FAILED - unknown delivery tag

我们有一个 PHP 应用程序,它通过 WebSocket 连接将消息从 RabbitMQ 转发到连接的设备(PHP AMQP pecl 扩展 v1.7.1 和 RabbitMQ 3.6.6)。

消息从队列数组中消费(每个 websocket 连接 1 个),并在我们通过 websocket 收到消息已收到的确认时由消费者确认(因此我们可以重新排队未传递的消息可接受的时间范围)。这是以非阻塞方式完成的。

99% 的情况下,这都能完美运行,但偶尔我们会收到错误 "RabbitMQ PRECONDITION_FAILED - unknown delivery tag "。这将关闭通道。据我了解,此异常是以下情况之一的结果:

  1. 邮件已经被确认或拒绝。
  2. 尝试通过未传递消息的通道进行确认。
  3. 在消息超时 (ttl) 到期后尝试确认。

我们对上述每种情况都实施了保护措施,但问题仍然存在。

我知道有很多实施细节可能会影响这一点,但在概念层面上,是否还有我们没有考虑但应该处理的其他失败案例?还是有更好的方法来实现上述功能?

"PRECONDITION_FAILED - unknown delivery tag"通常是因为双重确认、错误频道确认或确认不应确认的消息而发生。

因此,在相同的情况下,您需要执行 basic.ack 两次或 basic.ack 使用另一个通道

(下面的解决方案)

引自 Jan Grzegorowski 的博客:

If you are struggling with the 406 error message which is included in title of this post you may be interested in reading the whole story.

Problem

I was using amqplib for conneting NodeJS based messages processor with RabbitMQ broker. Everything seems to be working fine, but from time to time 406 (PRECONDINTION-FAILED) message shows up in the log:

"Error: Channel closed by server: 406 (PRECONDITION-FAILED) with message "PRECONDITION_FAILED - unknown delivery tag 1"

Solution <--

Keeping things simple:

  • You have to ACK messages in same order as they arrive to your system
  • You can't ACK messages on a different channel than that they arrive on If you break any of these rules you will face 406 (PRECONDITION-FAILED) error message.

Original answer

如果您两次确认同一条消息,就会出现此错误。

如果您将 Consumerno-ack 选项设置为 true 则可能会发生这种情况,这意味着您不能手动调用 ack 函数:

https://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.consume.no-ack

解决方法:设置no-ack标志为false

确保你有正确的 application.properties:

如果您在没有任何通道配置的情况下使用 RabbitTemplate,请使用“简单”:

spring.rabbitmq.listener.simple.acknowledge-mode=manual

在这种情况下,如果您使用“直接”而不是“简单”,您将收到相同的错误消息。另一个看起来像这样:

spring.rabbitmq.listener.direct.acknowledge-mode=manual

他们上面所说的关于确认它两次的变体:
有一种“晦涩”的情况,您不止一次确认一条消息,即当您确认 multiple 参数设置为 true 的消息时,这意味着您尝试确认的所有先前消息都将也被ack了。
因此,如果您尝试通过将 multiple 设置为 true 来确认其中一条被“自动确认”的消息,那么您将尝试多次“确认”它,因此错误会令人困惑,但希望您在阅读几次后能理解它。

如果您使用 docker 遇到此队列错误,重新启动相应的容器将修复错误。