必须全局单例复用amqp.Connection并用sync.Once初始化,按需创建amqp.Channel且用完即ch.Close;严禁在HTTP handler中amqp.Dial或直调ch.Publish,否则高并发下端口耗尽、消息丢弃或panic。

直接复用 *amqp.Connection,按需创建 *amqp.Channel,别在 HTTP handler 里 amqp.Dial —— 否则三天内端口耗尽、连接卡死是常态。
amqp.Dial 为什么不能放在业务函数里
每次调用都新建 TCP 连接,会快速占满本地端口(尤其是高并发场景),最终触发 connect: cannot assign requested address。RabbitMQ 的 Connection 是重量级资源,设计上就该全局复用;Channel 才是轻量级的、可并发创建/关闭的通信单元。
- 用
sync.Once初始化全局*amqp.Connection,连接字符串必须带超时参数:"amqp://guest:guest@localhost:5672/?connect_timeout=5" - 每个 goroutine 调用
conn.Channel()获取新Channel,处理完立刻ch.Close() - 不复用
Channel:一个Channel被多个 goroutine 并发使用会触发channel error: too many channels
发送消息前必须设的三个关键参数
直连空 exchange("")看似简单,但默认配置下消息全在内存,Broker 重启即丢。生产环境必须显式控制持久化与确认行为。
-
DeliveryMode: amqp.Persistent:否则amqp.Transient(默认)只存内存 - 队列声明时设
durable: true,否则即使消息落盘,队列本身重启后消失 - 发送时设
mandatory: true,让 Broker 在路由失败时返回amqp.ErrPublish,而不是静默丢弃 - 别依赖
ch.Publish返回 err 就认为成功——它只是把消息发出去,没等 confirm;真要可靠,得开 publisher confirm 模式 +ch.NotifyPublish监听
消费者怎么避免重复消费和 Goroutine 泄漏
RabbitMQ 不会自动重试失败消息,也不会限制推送速率。consumer 一写错,轻则重复消费,重则 OOM 卡死。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
ch.Qos(1, 0, false)必须设:限制预取数为 1,防止 Broker 一口气推几百条压垮 consumer - Ack 必须在业务逻辑**完全成功后**才调,且包裹在
if err == nil或defer中;panic 前已Ack就彻底丢失 - 加幂等校验:比如用
redis.SetNX("processed:order_123", "1", time.Hour)判断是否已处理 - 整个 handler 用
context.WithTimeout包裹,防止单条消息卡死整个 consumer
Gin 等 HTTP 框架里怎么安全发消息
HTTP handler 生命周期极短,响应写出后连接可能被复用或关闭。此时若 ch.Publish 遇网络抖动或 channel 关闭,会 panic 或静默丢消息。
- 不要在 handler 里直接调
ch.Publish;改用带缓冲的chan []byte接收序列化后的消息 - 起独立 goroutine 消费该 channel,并内置重试逻辑(如指数退避)、连接可用性检查(
ch.IsClosed()) - 发送前必须检查
ch是否已关闭,不可用则重建Channel;别假设 connection 还活着 - 捕获
amqp.Error类型错误(如NOT_FOUND队列不存在),而不是直接panic
最易被忽略的点:不是“连上了就能发”,而是“连上之后每一步都要主动防御”——从连接初始化、channel 生命周期、publish 参数、到 consumer 的 QoS 和 ack 时机,漏掉任何一环,流量一上来就出问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










