confirmSelect() 必须在 channel 创建后立即调用,否则 confirm 模式失效;应只选 waitForConfirmsOrDie 或 addConfirmListener 之一;handleNack 需区分失败类型并限制重试;multiple=true 时需批量确认 deliveryTag。

confirmSelect() 必须在 channel 创建后立即调用
不调用 channel.confirmSelect(),后续所有 basicPublish 都不会触发确认回调,也不会分配 deliveryTag。这不是可选配置,而是开启 Confirm 模式的强制前置动作。
常见错误是先发消息、再调 confirmSelect,或者在 channel 复用时漏掉这步(比如从连接池取 channel 后直接 publish)。Node.js 的 amqplib 不会报错,但你的确认逻辑完全失效 —— 看似正常运行,实则裸奔。
正确顺序必须是:
- 创建 channel
- 立即调用
await channel.confirmSelect() - 再声明队列/交换机、再 publish
不要混用 waitForConfirmsOrDie 和 addConfirmListener
waitForConfirmsOrDie() 是同步阻塞式等待,会卡住当前 event loop;addConfirmListener() 是异步事件驱动。两者底层共享同一套确认机制,但行为互斥:一旦你注册了 listener,waitForConfirmsOrDie 就不再返回,或抛出 Operation interrupted 错误。
实际使用中只应选其一:
根据 package.json 中的 engines 字段自动检测 Node.js 版本需求,未安装 nvm 时自动安装并切换至对应版本。支持 Windows、macOS、Linux。适用于 Node.js 项目开发、版本切换和环境配置。
- 批量发 10–50 条且允许短暂停顿 → 用
waitForConfirmsOrDie(5000),简单可靠 - 持续高吞吐(如每秒百条以上)→ 必须用
addConfirmListener,配合内存缓存deliveryTag与业务 ID 映射 - 绝不能在一个 channel 上先调 listener,又在某次 publish 后调 waitForConfirmsOrDie
handleNack 不等于消息丢失,但需主动处理重试边界
handleNack 触发只说明 broker 拒绝了该 deliveryTag 对应的消息,原因可能是:队列已满、磁盘不足、路由失败、持久化写入异常等。它不区分临时性失败(如瞬时资源紧张)和永久性失败(如 routingKey 错误)。
因此不能无条件重发。建议:
- 记录
deliveryTag和cause字段(如果可用),用于诊断 - 对同一消息最多重试 2 次,之后转入死信队列或告警通道
- 避免在
handleNack回调里直接调basicPublish—— 可能引发递归或 channel 竞态,应交由外部任务队列调度
multiple = true 时 deliveryTag 的含义容易被误解
当 broker 返回的 basic.ack 中 multiple 为 true,表示“所有小于等于该 deliveryTag 的未确认消息均已成功”。但 Node.js 的 amqplib 默认不暴露这个 flag 细节,只通过 handleAck(deliveryTag, multiple) 参数传递。
这意味着:如果你缓存了 100 条消息的 deliveryTag,收到 handleAck(80, true),应批量标记前 80 条为 success,而非只标第 80 条。漏掉 multiple 判断会导致大量“假失败”日志和无效重试。
真实项目里,必须检查 multiple 参数并做范围清理,而不是只删单个缓存项。

















