RabbitMQ不主动透传deliveryCount,需显式启用x-delivery-count队列参数(3.8.0+、manual ack模式下),启用后该值以Buffer形式存在于message.properties.headers中,首次投递为0。
为什么 deliveryCount 在 Node.js AMQP 客户端里拿不到?
amqp 协议本身不定义 deliverycount 字段,rabbitmq 也**不会通过标准消息属性(如 properties.headers 或 properties.deliverymode)主动下发重投次数**。你用 amqplib 收到的 message.fields 或 message.properties 里压根没有这个字段——不是你漏读了,是 rabbitmq 根本没塞进去。
常见误解是以为开启 requeue: true 后,RabbitMQ 会像 JMS 那样自动更新并透传一个计数器。实际上,RabbitMQ 只在内部维护该计数(用于 TTL、死信判定等),但默认不暴露给消费者。
怎么让 RabbitMQ 把真实投递次数带过来?
必须启用 RabbitMQ 的 x-delivery-count 头部支持,并在声明队列时显式开启:arguments: { 'x-delivery-count': true }。这是个服务端特性开关,不配就永远为空。
实操要点:
- 队列必须是 新声明的(或删除后重建),已存在的队列不会自动追加该参数
- 消费者连接的 Exchange 和 Queue 绑定关系无需改动,只影响队列元数据
- 启用后,每次消息被 requeue 再投递,RabbitMQ 会在
message.properties.headers中注入'x-delivery-count'字段
示例声明(amqplib):
await channel.assertQueue('my_queue', {
durable: true,
arguments: { 'x-delivery-count': true }
});
收到消息后怎么安全读取 x-delivery-count?
它存在于 message.properties.headers,但类型是 Buffer(AMQP 0-9-1 规范要求),不能直接当数字用。而且字段可能不存在(比如首次投递且未启用该特性时),必须做防御性检查。
正确读取方式:
- 先判断
message.properties.headers是否为对象,再检查'x-delivery-count'键是否存在 - 用
Number()转换,不要用parseInt()(避免隐式进制转换) - 注意:值为
0表示首次投递,1表示第一次 requeue 后再次投递,以此类推
简短示例:
const deliveryCount = message.properties.headers?.['x-delivery-count'];
const count = Number(deliveryCount);
if (count >= 3) {
channel.nack(message, false, false); // 拒绝且不 requeue,进死信
} else {
channel.nack(message, false, true); // requeue,下次投递 count+1
}
容易被忽略的兼容性与行为细节
这个机制依赖 RabbitMQ 3.8.0+,旧版本(如 3.7.x)即使配置了 x-delivery-count: true 也不会生效,headers 里始终没有该字段。
另一个关键点:x-delivery-count **只对 manual ack 模式有效**。如果你用的是 autoAck(noAck: true),RabbitMQ 不会跟踪投递状态,该字段必然缺失。
还有:如果消息被多个不同队列消费(通过 fanout exchange 分发),每个队列独立维护自己的 x-delivery-count,别误以为是全局计数。
真正麻烦的是——一旦你依赖这个值做业务逻辑(比如重试三次后告警),就必须确保所有下游消费者都按同样规则声明队列,且 RabbitMQ 版本统一。漏掉任意一环,count 就是 NaN,后续逻辑可能静默失败。


















