Go中RabbitMQ连接必须复用单例、配置心跳、独立创建Channel;队列需durable=true且autoDelete=false;消费者禁用autoAck并设Qos;Publish时DeliveryMode和Exchange须匹配业务语义。

amqp.Dial() 不能每次发消息都调用
Go 程序里反复 amqp.Dial() 是最常见也最危险的错误——连接数暴涨、端口耗尽、RabbitMQ 主动断连,全由此起。微服务长期运行,连接必须复用,不是“能连上就行”。
- 单例管理:
*amqp.Connection应全局唯一,封装在internal/mq/rabbitmq.go或 DI 容器中,启动时初始化一次 - 心跳必配:
amqp.Config{Heartbeat: 10 * time.Second},否则 K8s 网络策略或云厂商 LB 会在空闲 30 秒后静默掐断连接 - 别共享 Channel:
conn.Channel()每次消费/发布前独立创建,用完立刻ch.Close();Channel 不是线程安全的,跨 goroutine 复用会 panic
ch.QueueDeclare() 的 durable 和 autoDelete 参数怎么选
队列声明参数直接决定消息是否“真可靠”。设错一个,RabbitMQ 重启后队列消失、消息全丢,线上事故就来了。
-
durable: true→ 必须设。队列元数据落盘,服务重启不丢失(但消息是否持久还要看Publishing.DeliveryMode) -
autoDelete: false→ 必须关。否则最后一个消费者断开,队列被自动删掉,新实例启动后找不到队列,NOT_FOUND错误直接炸出来 -
exclusive: false→ 必须关。排他队列只允许一个连接使用,多实例部署时第二个服务根本连不上
消费者为什么总重复消费或消息卡死
根本原因就两个:autoAck: true + 没设 Qos。看似省事,实则埋雷。
- 禁用自动确认:
ch.Consume(..., false, ...),否则进程崩溃时未处理消息直接被标记为“已投递”,永久丢失 - 手动 ACK/NACK:
msg.Ack(false)成功后调,msg.Nack(false, true)失败时调(requeue=true让消息回队尾) - 限流防积压:
ch.Qos(1, 0, false)控制预取数,避免单个消费者扛下全部消息,其他实例饿死 - ACK 必须同 Channel:
msg.Ack()要在创建该msg的同一个ch上调,跨 goroutine 传msg时别漏传ch
ch.PublishWithContext() 的 deliverymode 和 exchange 怎么填
发不出消息?发出去却没人收到?大概率栽在这俩字段上。Exchange 名字和 DeliveryMode 不匹配业务语义,等于写了张无效支票。
立即学习“go语言免费学习笔记(深入)”;
-
DeliveryMode: amqp.Persistent→ 核心消息(如订单创建、支付成功)必须设,否则 RabbitMQ 内存 crash 后消息蒸发 - Exchange 名称要对得上绑定关系:直连模型用
"direct"类型交换机,ch.Publish()第一个参数填交换机名,第二个参数填 routing key;若填空字符串"",就是走默认交换机,只认队列名当 routing key - 别信“exchange 可以空着”:生产环境必须显式声明并绑定,靠默认交换机扛不住多服务解耦场景
RabbitMQ 在 Go 里不是“连上就能用”的玩具,每个参数背后都是分布式系统里的权衡点——连不上、丢消息、重复消费、资源打满,全是参数没对齐业务语义的结果。最麻烦的不是写代码,是把配置项、重试逻辑、连接生命周期和业务可靠性要求一条条钉死。










