深入理解: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 实现可靠消息的核心机制。

相关推荐
她说可以呀5 小时前
RabbitMQ Publisher_Confirms(发布确认)
分布式·rabbitmq·ruby
2603_9547083119 小时前
全维度容错设计,打造微电网安全运行屏障
服务器·网络·数据库·人工智能·分布式·安全
国服第二切图仔20 小时前
HarmonyOS APP《画伴梦工厂》开发第60篇-分布式软总线2.0——多设备协同新范式
分布式·wpf·harmonyos
9523621 小时前
RabbitMQ-基础操作
java·spring boot·分布式·后端·spring·rabbitmq
worilb1 天前
Spring Cloud 学习与实践(13):使用 Seata 解决分布式事务问题
分布式·学习·spring cloud
风若飞1 天前
Tomcat 9.0.118 分布式 Redis Session 配置指南
redis·分布式·tomcat
六bring个六1 天前
文件互传在传输层的操作分析
分布式·p2p·c/c++·open harmony
豆瓣鸡1 天前
Leaf 分布式 ID 生成实战——美团 Leaf 从原理到落地
java·spring boot·分布式
牛奶咖啡131 天前
大数据Hadoop运维应用实践——Hadoop大数据平台基础与架构(hadoop伪分布式的安装部署)
大数据·hadoop·分布式·hadoop是什么?有啥用?·hadoop的适用与不适用场景·hadoop伪分布式的安装部署·hadoop分布式存储操作