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

相关推荐
开心码农1号28 分钟前
Java rabbitMQ如何发送、消费消息、全套可靠方案
java·rabbitmq·java-rabbitmq
leo_messi943 小时前
RabbitMq(五) -- SpringBoot整合 RabbitMQ 完整实现
spring boot·rabbitmq·java-rabbitmq
Arva .5 小时前
RabbitMQ消费者处理失败
分布式·rabbitmq
开心码农1号2 天前
mq是什么,常用mq的使用场景有哪些?
中间件·rabbitmq
Bohemian—Rhapsody2 天前
麒麟v10-arm架构部署rabbitmq
arm开发·架构·rabbitmq
總鑽風2 天前
数据一致性springcloud+rabbitmq+mysql+redis
mysql·spring cloud·rabbitmq
William Dawson3 天前
【实战分享】DTU设备高并发数据接入全流程(Redis + RabbitMQ + 数据库)
数据库·redis·rabbitmq
Albert Edison4 天前
【RabbitMQ】核心概念|工作流程|界面操作
分布式·rabbitmq·ruby
少许极端4 天前
消息队列5-RabbitMQ的高级特性和MQ的应用问题与解决方案-事务、消息分发的应用、幂等性保证、顺序性保证、消息积压的解决
分布式·消息队列·rabbitmq
Arva .4 天前
RabbitMQ
网络·分布式·rabbitmq