RabbitMQ的ACK

ACK 是英文单词 Acknowledgement(确认、致谢)的缩写。

在计算机网络和 RabbitMQ 这样的消息队列中,它的核心含义就是:"收到且确认"

为什么手动 ACK 能防止消息丢失? 带着这个问题继续阅读

我们需要对比一下自动 ACK 的风险,并结合"快递员送货"的例子来理解。

1. 核心区别:消息什么时候被删除?

RabbitMQ 作为一个消息中间件,它的主要任务是把消息从 A 传给 B。但是,它需要知道什么时候可以把原本存在内存或磁盘里的这条消息删掉

❌ 自动 ACK (Auto Acknowledge) ------ "扔在门口就跑"

如果设置 noAck: true (自动 ACK),RabbitMQ 的逻辑是这样的:

  1. RabbitMQ 把消息发送给消费者(你的代码)。

  2. 只要消息发出了,RabbitMQ 瞬间就认为"任务完成",并立即从内存/磁盘中删除这条消息。

  3. 风险点: 此时你的代码刚收到消息,还没开始处理(或者处理到一半)。如果这个时候:

    • 你的机器断电了。

    • 你的 Node.js 进程崩溃了 (Exception)。

    • 数据库连接断开导致报错。

结果: 你的代码挂了,没处理完业务;而 RabbitMQ 那边已经把消息删了。这条消息就彻底消失了,无法找回。


✅ 手动 ACK (Manual Acknowledge) ------ "签收才算完"

如果设置 noAck: false (手动 ACK),流程就变了:

  1. RabbitMQ 把消息发送给消费者。

  2. RabbitMQ 不会删除消息 ,而是将这条消息标记为 Unacked (未确认) 状态。

  3. 消费者开始运行业务逻辑(查数据库、计算、调接口...)。

  4. 关键时刻:

    • 如果成功: 你的代码显式调用 channel.ack(msg)。RabbitMQ 收到信号,这才把消息删除

    • 如果崩溃(未发送 ACK): 如果你的消费者在调用 ack() 之前断开了连接(比如进程挂了),RabbitMQ 会发现:"哎,这个消费者断连了,但它手里还有条消息没确认!"

结果: RabbitMQ 会自动把这条消息重新放回队列头部 (Re-queue),发给下一个可用的消费者(或者是重启后的同一个消费者)。

2. 它是如何解决丢失问题的?(情景模拟)

让我们看一个具体的**"崩溃恢复"**场景:

  • 场景: 这是一个订单处理系统。

  • 动作: 收到消息 { "orderId": 100, "action": "deduct_stock" }

如果没有手动 ACK(消息丢失):

RabbitMQ 发送消息 -> RabbitMQ 删除消息 -> 消费者收到 -> 开始扣减库存 -> 代码报错:数据库连接超时,程序崩溃

结局: 库存没扣减,消息也没了。这就是严重的数据不一致

如果有手动 ACK(消息找回):

RabbitMQ 发送消息 -> 标记为 Unacked -> 消费者收到 -> 开始扣减库存 -> 代码报错:数据库连接超时,程序崩溃 (注意:此时没有执行 ack())。

RabbitMQ 监测到 TCP 连接断开 -> 将消息状态从 Unacked 改回 Ready -> 重新投递给备用的消费者 B

消费者 B 收到消息 -> 扣减库存成功 -> 发送 ACK -> RabbitMQ 删除消息。

结局: 业务最终被成功处理。

3. 关于 ACK 与 NACK 的区别

既然是为了防止丢失,为什么还需要 NACK (Negative Acknowledgement)?

  • ACK (BasicAck): 告诉 RabbitMQ:"我处理成功了,你可以删了。"

  • NACK (BasicNack) / Reject: 告诉 RabbitMQ:"我处理不了这条消息。"

在手动模式下,显式调用 NACK 通常用于以下两种情况:

  1. 处理失败,需要重试 (requeue: true):

    • 比如"远程接口暂时超时"。你调用 nack(msg, false, true)。RabbitMQ 会把消息放回队列,稍后再次尝试投递。
  2. 无法处理,直接丢弃 (requeue: false):

    • 比如"消息格式错误"或"恶意数据"。你调用 nack(msg, false, false)。RabbitMQ 会直接删除它(或者转发到死信队列 Dead Letter Exchange),防止这条坏消息无限循环卡死消费者。

总结

手动 ACK 机制之所以能解决消息丢失,是因为它将"消息删除的控制权"从 RabbitMQ 转移到了你的业务代码手中

  • 只要你没说"OK" (ack),RabbitMQ 就永远不敢删这条消息。

  • 只要连接断开时你没说"OK",RabbitMQ 就默认你"挂了",并帮你把消息救回来重发。

这就是所谓的 At-least-once(至少一次投递) 保证。

相关推荐
alonewolf_9919 小时前
RabbitMQ应用开发实战:从基础编程到SpringBoot集成全面指南
spring boot·消息队列·rabbitmq·java-rabbitmq
alonewolf_991 天前
RabbitMQ高级功能全面解析:队列选型、死信队列与消息分片实战指南
分布式·消息队列·rabbitmq·ruby
小北方城市网1 天前
SpringBoot 集成 RabbitMQ 实战(消息队列):实现异步通信与系统解耦
java·spring boot·后端·spring·rabbitmq·mybatis·java-rabbitmq
小北方城市网1 天前
SpringBoot 集成 Redis 实战(缓存与分布式锁):提升系统性能与并发能力
spring boot·python·rabbitmq·java-rabbitmq·数据库架构
信创天地1 天前
国产化消息中间件双雄:东方通TongLINK/Q与华为RabbitMQ的运维核心技术全解析
运维·华为·rabbitmq
alonewolf_991 天前
RabbitMQ快速上手与核心概念详解
分布式·消息队列·rabbitmq
野犬寒鸦1 天前
从零起步学习RabbitMQ || 第四章:RabbitMQ的延迟消息在项目中的运用及实现剖析
java·服务器·数据库·后端·mysql·rabbitmq
CV_J3 天前
RabbitMQ安装
rabbitmq
独自破碎E3 天前
RabbitMQ怎么实现延迟消息?
rabbitmq
小北方城市网3 天前
SpringBoot 集成 RabbitMQ 实战(消息队列解耦与削峰):实现高可靠异步通信
java·spring boot·python·微服务·rabbitmq·java-rabbitmq·数据库架构