深入理解:RabbitMQ 中的 ACK / NACK 有什么区别?

文章目录

  • [一、为什么 RabbitMQ 需要 ACK / NACK?](#一、为什么 RabbitMQ 需要 ACK / NACK?)
  • [二、ACK 是什么?](#二、ACK 是什么?)
  • [三、NACK 是什么?](#三、NACK 是什么?)
  • [四、NACK 之后消息会发生什么?](#四、NACK 之后消息会发生什么?)
  • [五、ACK vs NACK 对比总结](#五、ACK vs NACK 对比总结)
  • [六、ACK / NACK 与自动确认(autoAck)](#六、ACK / NACK 与自动确认(autoAck))
  • [七、ACK / NACK 与"至少一次投递"](#七、ACK / NACK 与“至少一次投递”)
  • 八、总结

在使用 RabbitMQ 的过程中, ACK / NACK 是保证消息可靠性最核心的机制之一

很多人知道"ACK 是确认、NACK 是拒绝",但如果继续追问:

  • ACK / NACK 到底解决了什么问题?
  • NACK 之后消息一定会重试吗?
  • 和自动确认(autoAck)有什么本质区别?
  • 为什么 RabbitMQ 只能做到"至少一次投递"?

一、为什么 RabbitMQ 需要 ACK / NACK?

RabbitMQ 是一个 异步消息系统,生产者发送消息后,并不知道消费者什么时候、是否能成功处理消息。

如果没有确认机制,会出现严重问题:

  • 消费者刚拿到消息就宕机
  • 消息已经从队列移除
  • 业务还没执行完

👉 消息直接丢失

因此,AMQP 协议引入了 消息确认机制(Acknowledgement),让消费者明确告诉 Broker:

这条消息,我处理得怎么样了。


二、ACK 是什么?

ACK(Acknowledge)

ACK 表示:消息已经被成功消费

当消费者处理完一条消息,并确认没有任何问题时,会向 Broker 发送 ACK。

工作流程

  1. Broker 将消息投递给 Consumer
  2. Consumer 执行业务逻辑
  3. Consumer 发送 ACK
  4. Broker 永久删除这条消息

Java 示例

java 复制代码
channel.basicAck(deliveryTag, false);

参数说明:

  • deliveryTag:消息唯一标识
  • false:只确认当前这一条(不是批量)

📌 ACK 的本质:

告诉 RabbitMQ:

"这条消息我已经处理完成了,你可以删掉。"


三、NACK 是什么?

NACK(Negative Acknowledge)

NACK 表示:消息消费失败

当消费者在处理消息时出现异常,例如:

  • 数据库不可用
  • 下游接口超时
  • 业务校验失败

就应该使用 NACK。

Java 示例

java 复制代码
channel.basicNack(deliveryTag, false, true);

参数说明:

参数 含义
deliveryTag 消息标识
multiple 是否批量
requeue 是否重新入队

四、NACK 之后消息会发生什么?

这是 ACK / NACK 最核心、也最容易被问到的点

requeue = true(重新入队)

java 复制代码
channel.basicNack(tag, false, true);

结果:

  • 消息重新回到队列
  • 等待下一次投递
  • 可能被同一个 Consumer 再次消费

⚠️ 风险:

  • 如果逻辑一直失败,会 无限重试
  • 可能造成 CPU 飙高、队列阻塞

requeue = false(不重新入队)

java 复制代码
channel.basicNack(tag, false, false);

结果:

  • 消息被丢弃
  • 如果队列配置了 死信队列(DLQ)

👉 消息进入死信队列

📌 生产环境常用策略:

场景 做法
临时异常 NACK + requeue
业务错误 NACK + DLQ
数据非法 直接进入死信队列

五、ACK vs NACK 对比总结

维度 ACK NACK
含义 消费成功 消费失败
消息结果 立即删除 重试 / 丢弃 / DLQ
是否可靠
是否可能重复
使用时机 业务成功 业务异常

六、ACK / NACK 与自动确认(autoAck)

autoAck = true(不推荐)

java 复制代码
channel.basicConsume(queue, true, consumer);

特点:

  • 消息一投递就算"成功"
  • Consumer 宕机也不会重发
  • 存在消息丢失风险

手动 ACK(推荐)

java 复制代码
channel.basicConsume(queue, false, consumer);

特点:

  • 业务成功 → ACK
  • 业务失败 → NACK
  • 保证至少一次投递

📌 结论:

生产环境一定使用手动 ACK


七、ACK / NACK 与"至少一次投递"

RabbitMQ 的投递语义是:

At-Least-Once(至少一次)

原因:

  • Consumer 在 ACK 之前宕机
  • Broker 不知道消息是否处理完成
  • 消息会被重新投递

因此:

  • 消息可能重复
  • 消息不会丢失

👉 幂等性必须由业务层保证。

就像快递必须等你签收才算完成,没签收前你要是出事了快递员就会再送一次,所以快递可能送两次,但绝不会不送,重复要你自己处理。


八、总结

ACK 能保证消息不重复吗?

不能。

RabbitMQ 只能保证至少一次投递,

幂等性需要业务侧保证。


basicReject 和 basicNack 有什么区别?

方法 说明
basicReject 只能拒绝一条消息
basicNack 支持批量,更灵活

👉 实际开发中 优先使用 basicNack


什么情况下会用 NACK + requeue?

  • 数据库短暂不可用
  • 网络抖动
  • 下游系统瞬时故障

ACK 表示消息已成功消费并可删除;
NACK 表示消费失败,消息可重试或进入死信队列。
手动 ACK + 合理 NACK 是 RabbitMQ 实现可靠消息的核心机制。

相关推荐
Rain的Java大神之路20 小时前
介绍一下分布式事务
java·分布式·后端·spring·spring cloud·架构·springcloud
大菠萝爱上小西瓜1 天前
【大数据实战】Spark 2.2.2 完全分布式集群搭建
大数据·分布式·spark
肠畔码农1 天前
深度解析 RocketMQ 消费端限流与重平衡(Rebalance):分布式队列分配与流控防雪崩本质
分布式·rocketmq
海兰1 天前
【 Kafka进阶3】Apache Kafka 分布式事件流平台:架构原理与微服务解耦机制简要分析
分布式·架构·kafka
Rain的Java大神之路1 天前
如何避免订单重复提交
java·redis·后端·面试·架构·rabbitmq·rocketmq
麻瓜记录Jackson2 天前
深入剖析 ZooKeeper 分布式 互斥锁:Curator 之 InterProcessMutex 源码全解(附图解)
java·spring boot·分布式·后端·zookeeper·java-zookeeper
Keystone_Onion2 天前
跨境电商架构演进:基于美国海外仓的中大件分布式仓网路由优化方案
分布式·架构
盛世宏博智慧档案2 天前
分布式机房环境监测硬件,以太网温湿度采集终端如何应用
分布式·传感器·机房·温湿度
NJCloud2 天前
ELK企业级日志分析平台(四)——基于 ELFK + Kafka 的日志采集与传输平台部署实践
linux·运维·分布式·elk·kafka
国科安芯2 天前
星载网络化控制的总线脊梁:四路CANFD在分布式载荷管理中的架构优势
分布式·单片机·嵌入式硬件·架构·系统架构·canfd·低轨卫星星座