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(至少一次投递) 保证。

相关推荐
羑悻的小杀马特3 小时前
【仿RabbitMQ实现消息队列项目】交换机智能路由、队列流量隔离、绑定信息精准定向、消息可靠投递——四模块协同打造低耦合消息系统!
linux·rabbitmq·项目·消息·交换机·队列·绑定信息
苦学编程的谢1 天前
RabbitMQ_4_高级特性(1)
分布式·rabbitmq
小熊officer1 天前
RabbitMQ简介
分布式·rabbitmq
华仔啊1 天前
RabbitMQ 的 6 种工作模式你都掌握了吗?附完整可运行代码
java·后端·rabbitmq
华仔啊2 天前
RebbitMQ 入门教程看这一篇就够了
java·后端·rabbitmq
老虎06272 天前
RabbitMQ(RabbitMQ的消息收发的模板工具:SpringAMQP)
分布式·rabbitmq·ruby
..空空的人3 天前
C++基于protobuf实现仿RabbitMQ消息队列---接口介绍
开发语言·c++·rabbitmq
小坏讲微服务3 天前
Spring Boot 4.0 整合 RabbitMQ 注解方式使用指南
java·spring boot·rabbitmq·java-rabbitmq
JavaBoy_XJ3 天前
RabbitMQ 在 Spring Boot 项目中的完整配置指南
spring boot·rabbitmq·java-rabbitmq