Beego 不封装 RabbitMQ,必须用 amqp091-go;需异步重试初始化连接并设全局状态,避免阻塞启动;Consumer 必须独立长任务运行且自行 ACK,禁止在 Controller 中调用。

Beego 框架本身不提供 RabbitMQ 封装,直接用 amqp091-go 是唯一可行路径;硬套 Beego 生命周期或在 Controller 里启 Consumer,基本等于给消息 ACK 埋雷。
启动时调 amqp.Dial() 导致服务起不来
Beego 的 app.Run() 是阻塞调用,若在 main() 或 init() 中同步执行 amqp.Dial(),一旦 DNS 未就绪、网络不通或 RabbitMQ 未启动,整个服务会 panic 退出,而不是降级运行。
- 必须包装成带重试和超时的初始化函数,例如用
sync.Once+time.AfterFunc延迟执行 - 从
beego.AppConfig.String("rabbitmq::url")读取配置,不要写死连接字符串 - 连接失败时记录
warn日志,设置全局状态如rabbitmq.status = "unavailable",HTTP 服务照常启动 - 避免在
beego.AppInit()里直接panic(err)—— 这会让可观测性归零
ch.Consume() 写在 Controller 方法里必丢 ACK
常见错误是:在 Controller.Get() 中调用 ch.Consume(),然后用 for range msgs 循环处理。HTTP 请求一结束,goroutine 被回收,msg.Ack() 永远发不出去,消息持续重回队列。
- Consumer 必须作为独立长任务启动,推荐在
beego.AppInit()阶段用go func() { ... }()启动 - ACK 动作必须由该 goroutine 自己完成,不能跨 HTTP 上下文委托给任何 Controller 方法
- 若需将消费结果回传 Web 层(如更新订单状态),走
sync.Map、本地chan或 Redis 缓存,别试图 channel
日志没 msg.MessageId 就等于盲操作
Beego 默认日志不含 trace ID,而 RabbitMQ 每条 amqp.Delivery 都应有唯一标识。多消费者并行时,日志混在一起,根本分不清哪条 log 对应哪条消息。
- 在
msgs循环开头立即打结构化日志:log.WithFields(log.Fields{"msg_id": msg.MessageId, "delivery_tag": msg.DeliveryTag}) - 禁用全局
logs.SetLevel(logs.LevelDebug),改为对 RabbitMQ 模块单独启用 debug 级别 - 绝对不要用
fmt.Printf打印消息体 —— 没上下文、不可过滤、干扰 Prometheus metrics 抓取
最易被忽略的是 Channel 并发安全:一个 amqp.Channel 不可被多个 goroutine 同时调用 Publish() 或 Ack(),否则会 panic;生产者共用 channel 可以,但每个 Consumer 必须独占一个 channel —— 这点连很多封装库都踩过坑。


















