Iris 框架中 RabbitMQ 消费者必须用 goroutine 独立启动,复用 Connection 和 Channel,关闭 autoAck 并实现幂等处理,启用 delayed exchange 插件支持延迟消息,且需监听 notifyClose 重建连接。

Iris 框架本身不管理消息消费生命周期,RabbitMQ 消费者必须脱离 HTTP Server 启动流程独立运行,否则会阻塞请求响应。
消费者必须用 goroutine 启动,不能直接阻塞 main
很多人在 app.Run() 之后写 ch.Consume(),结果 HTTP 服务启动了但没响应——因为 Consume() 是阻塞调用,它会一直等待消息,主线程卡死。正确做法是立刻用 go func() { ... }() 包裹整个消费循环。
- 消费逻辑里别调
app.Get()或直接操作app.Router,它们不是并发安全的 - 所有业务处理下沉到 Service 层,Controller 只负责 HTTP,Consumer 只负责消息
- 用
sync.WaitGroup或context.WithCancel控制消费者退出,避免进程无法优雅关闭
channel 和 connection 必须复用,不能每次消费都新建
常见错误是在 ConsumeSimple() 里每次调用都 conn.Channel(),这会快速耗尽 channel ID(默认上限 2047),报错 channel id space exhausted。Connection 和 Channel 都应作为单例初始化。
- 应用启动时用
sync.Once建立*amqp.Connection,URL 格式为amqp://user:pass@host:port/%2F(注意 vhost 的/要 URL 编码) - Service 层持有该连接,并在需要时调用
conn.Channel()获取新 channel;每个消费者独占一个 channel,不要复用给多个 goroutine - 务必设置
amqp.Config{Dial: dialTimeout},超时建议 5s,防止启动卡在 DNS 或网络不可达
手动应答 + 幂等处理是生产环境底线
默认自动应答(autoAck=true)下,消费者一收到消息就确认,崩溃后消息永久丢失。真实场景必须关掉自动应答,并自己控制 Ack/Nack 时机。
- 调用
ch.Consume(..., false, ...)关闭 autoAck - 在业务逻辑执行成功后调
msg.Ack(false);失败时根据策略选msg.Nack(false, true)(重入队)或msg.Reject(false)(丢弃) - 消息体(
msg.Body)必须含唯一 ID(如订单号),消费者处理前先查 DB 或 Redis 判断是否已处理,避免重复消费 - 别依赖 RabbitMQ 的“只投递一次”——它只保证 at-least-once,幂等是应用层责任
延迟消息必须用插件,TTL+DLX 是备选方案
想实现 5 秒后发短信、30 分钟后关单?别碰 TTL+DLX 组合,它链路长、难调试、易丢消息。当前唯一推荐路径是启用 rabbitmq_delayed_message_exchange 插件。
- Docker 启动后进容器执行:
rabbitmq-plugins enable rabbitmq_delayed_message_exchange - 声明 Exchange 时指定类型
"x-delayed-message",并带参数amqp.Table{"x-delayed-type": "direct"} - 发消息时只认
Headers["x-delay"],且值必须是uint32(比如30000表示 30 秒),写成int64(30000)或字符串都会静默失效 - 消费者代码完全不用改,但幂等检查仍需保留——延迟不改变“可能重复投递”的本质
最常被忽略的是连接断开后的重建逻辑:notifyClose 通道必须监听,一旦触发就得重建 Connection 和所有 Channel,否则后续 Publish 或 Consume 会 panic。这不是可选项,是生产环境存活的前提。











