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

相关推荐
予枫的编程笔记15 小时前
【Java进阶2】Java常用消息中间件深度解析:特性、架构与适用场景
java·kafka·rabbitmq·rocketmq·activemq
better_liang15 小时前
每日Java面试场景题知识点之-RabbitMQ消息重复消费问题
java·分布式·消息队列·rabbitmq·幂等性
無欲無为17 小时前
Spring Boot 整合 RabbitMQ 详细指南:从入门到实战
spring boot·rabbitmq·java-rabbitmq
程序员阿鹏2 天前
RabbitMQ持久化到磁盘中有个节点断掉了怎么办?
java·开发语言·分布式·后端·spring·缓存·rabbitmq
TT哇3 天前
【RabbitMQ】@Autowired private RabbitTemplate rabbitTemplate;
java·分布式·rabbitmq
AC赳赳老秦4 天前
企业级人工智能平台选型深度分析:天翼云 DeepSeek 与开源解决方案的部署考量与成本博弈
人工智能·elasticsearch·zookeeper·rabbitmq·github·时序数据库·deepseek
csdn_aspnet4 天前
实现 RabbitMQ 实现 .NET 6 Web API 之间的通信
rabbitmq·.net6
csdn_aspnet4 天前
.NET Core 8 与 RabbitMQ 和 MassTransit
rabbitmq·.net8