必须关闭autoack,因开启后消息立即被删除,未处理完即丢失;应设autoack=false,成功后调msg.ack(false),失败时用msg.nack(false, true)重入队,并及时拷贝msg.body、配置心跳与qos。

Go 消费 RabbitMQ 消息,最常出问题的不是代码写错,而是 autoAck 设成 true、消息体没拷贝就进 goroutine、连接没心跳被服务端断开——这三处一踩就丢消息。
ch.Consume() 的 autoAck 必须设为 false
默认 true 表示 RabbitMQ 一发完就删消息,哪怕你的业务逻辑 panic 或还没解析 msg.Body,消息也永久消失。
-
autoAck: false是手动确认的前提,必须显式传入,不能依赖默认值 - 设为
false后,必须在业务处理成功后调msg.Ack(false),失败时用msg.Nack(false, true)重入队(或msg.Reject(true)丢弃) - 别在
defer里调Ack:panic 可能导致误确认;也别在 for range 外层直接调,msg结构体生命周期只到本次循环结束
收到 msg.Body 后必须立即拷贝
msg.Body 是底层复用的字节切片,下一次 range 迭代会覆盖内容。不拷贝就扔进 goroutine 处理,大概率读到乱码或空数据。
- 安全做法:
data := append([]byte(nil), msg.Body...)或data := make([]byte, len(msg.Body)); copy(data, msg.Body) - 所有耗时操作(HTTP 请求、DB 写入、sleep 模拟)必须放在新 goroutine 中,但确认动作仍要回到原 goroutine 执行
- 不要把
msg整个传进 goroutine ——Ack/Nack方法不是线程安全的,只能由接收它的 goroutine 调用
连接和 channel 必须配心跳与重连
RabbitMQ 默认 60 秒无响应就主动断连,而 Go 客户端不会自动发心跳帧。长耗时处理或网络抖动后,常见 read: connection timed out 或 Broken pipe。
- 连接 URL 加
heartbeat=30参数:"amqp://user:pass@host:5672/%2F?heartbeat=30" - 监听
conn.NotifyClose()和ch.NotifyClose(),触发后主动重建连接和 channel - 避免用
conn.IsClosed()判断状态——它只是本地缓存,不可靠;真要检测,得靠通知通道事件 - 推荐使用新 SDK
github.com/rabbitmq/amqp091-go,对 context 取消和 TLS 配置更清晰,旧库streadway/amqp已归档
QoS 设置不当会导致消费卡死
不设 ch.Qos,RabbitMQ 会尽可能多发消息给 consumer,但若处理慢、又没及时 Ack,未确认消息堆积,channel 可能被服务端限流甚至阻塞。
- 必须在
ch.Consume()前调ch.Qos(1, 0, false):限制最多 1 条未确认消息,避免积压 - 数字 1 不是性能瓶颈,而是可靠性底线;想提并发,靠横向扩 consumer,而不是单个 consumer 接太多 unacked 消息
-
ch.Qos对已建立的 channel 生效,每次重连后都要重新设置
真正难的不是写通一条消费逻辑,而是让 msg.Ack 在正确时机、由正确 goroutine、对正确拷贝的数据执行——这三者错一个,消息就可能静默丢失,且日志里几乎不报错。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











