rabbitmq服务需预先启动并开放5672/15672端口,go客户端依赖streadway/amqp,须手动声明exchange/queue、监听连接关闭、避免channel并发误用,并实现重连机制以防网络波动导致消息中断。

确认RabbitMQ服务已就绪且端口可访问
Go 本身不提供 RabbitMQ 服务,它只是客户端;你必须先确保 RabbitMQ 实例在本地或远程运行,并开放了 AMQP 端口(默认 5672)和管理端口(15672,用于 Web 控制台)。常见错误是:代码里写了 "amqp://guest:guest@localhost:5672/",但实际 RabbitMQ 没启动,或被防火墙拦截,或 guest 用户被禁用(新版 RabbitMQ 默认禁用 guest 远程登录)。
- 检查服务状态:
systemctl status rabbitmq-server(Linux)或rabbitmqctl status - 验证端口连通性:
telnet localhost 5672或nc -zv localhost 5672 - 若用 Docker,确保启动时暴露了端口:
-p 5672:5672 -p 15672:15672 - 如需远程连接,务必创建非
guest用户并赋权:rabbitmqctl add_user myuser mypass+rabbitmqctl set_permissions -p / myuser ".*" ".*" ".*"
引入 streadway/amqp 并初始化连接与 channel
Go 客户端依赖只有一个主流选择:github.com/streadway/amqp。它轻量、稳定,但不支持自动重连——这点极易被忽略,导致生产环境连接断开后程序静默失败。
- 安装:
go get github.com/streadway/amqp - 连接字符串格式必须严格:
"amqp://user:pass@host:port/vhost",其中vhost默认为/,但需 URL 编码,即写成%2F(例如:amqp://myuser:mypass@localhost:5672/%2F) -
amqp.Dial()返回的*amqp.Connection应复用,不要每次发消息都新建;但Connection断开后不会自动恢复,需自行监听conn.NotifyClose()做重建 -
conn.Channel()创建的*amqp.Channel是轻量对象,可按需创建,但注意:每个 Channel 是独立的 AMQP 会话上下文,不能跨 goroutine 并发使用(除非加锁)
声明 Exchange/Queue 并绑定,避免“队列不存在”错误
很多新手直接调用 channel.Publish() 就报错 NOT_FOUND - no exchange 'xxx' in vhost '/' 或 NO_ROUTE,本质是没提前声明资源。RabbitMQ 不会自动创建未声明的 Exchange 或 Queue,哪怕你只用了路由键。
- 发布前务必调用
channel.ExchangeDeclare(),参数durable=true才能持久化(否则重启丢配置) - 消费前务必调用
channel.QueueDeclare(),同理设durable=true;若用随机队列(queue: ""),则返回实际生成的队列名,需保存 - 绑定用
channel.QueueBind(),注意routingKey必须与Publish()时一致(在 direct 模式下)或符合模式(topic/fanout) - 简单模式(直连队列)可跳过 Exchange,直接
channel.Publish("", queueName, ...),但前提是该队列已存在且声明为非排他、非自动删除
处理连接中断与 channel 异常关闭
网络抖动、Broker 重启、超时踢出都会导致 *amqp.Connection 或 *amqp.Channel 进入关闭状态,而 Go 客户端不会 panic,只会让后续操作返回 io.ErrClosedPipe 或类似错误——这正是线上最隐蔽的故障源。
立即学习“go语言免费学习笔记(深入)”;
- 监听连接关闭:
conn.NotifyClose(make(chan *amqp.Error, 1)),收到通知后应停止使用该 conn,并触发重建逻辑 - 监听 channel 关闭:
ch.NotifyClose(make(chan *amqp.Error, 1)),尤其在消费者循环中,一旦 channel 关闭,ch.Consume()会立即返回空 - 不要在 defer 中直接
ch.Close(),因为 channel 可能已被关闭;应加判空:if ch != nil { ch.Close() } - 建议封装一个带重试/重建机制的
mq.ConnPool,而不是裸用全局conn和channel
真正难的不是连上 RabbitMQ,而是让连接在波动网络下不哑火、让队列声明不遗漏、让 channel 关闭不被忽略——这些点没兜住,程序上线后可能跑几天才突然停收消息,查日志还看不到明显 panic。










