NSQ 本地服务需按顺序启动 nsqlookupd、nsqd、nsqadmin,且 nsqd 必须设置 --broadcast-address=127.0.0.1;Producer 连接失败会直接报错而非重试;Publish 同步阻塞并返回 error,PublishAsync 异步需显式处理错误回调;消费者直连用 ConnectToNSQD,服务发现用 ConnectToNSQLookupd;消息需调 m.Finish() 否则重发;JSON 解析前须校验 m.Body 长度及编码。

NSQ 本地服务必须先跑起来,否则 nsq.Producer 一连就报错
Go 程序里调用 nsq.NewProducer("127.0.0.1:4150", config) 时,如果 NSQD 没启动,会直接 panic 或返回 connection refused 错误,而不是优雅重试。这不是 Go 客户端的问题,是 NSQ 本身不提供自动发现或 fallback 机制。
- 必须按顺序启动三个组件:
nsqlookupd(注册中心)、nsqd(消息服务)、nsqadmin(可视化) - 典型启动命令(终端各开一个):
nsqlookupd nsqd --lookupd-tcp-address=127.0.0.1:4160 --broadcast-address=127.0.0.1 nsqadmin --lookupd-http-address=127.0.0.1:4161
-
nsqd的--broadcast-address很关键:不设或设错会导致消费者连到内网 IP(比如192.168.x.x),本地调试时务必填127.0.0.1 - 验证是否就绪:访问
http://localhost:4171能打开管理页,且 Topics 列表为空但无报错,说明服务已活
Publish 和 PublishAsync 别混用,同步阻塞 vs 异步回调语义完全不同
很多新手以为 PublishAsync 是“更快的 Publish”,其实它只是把网络 I/O 放进 goroutine,但错误不会抛出,必须靠回调捕获——漏写回调等于静默丢消息。
-
producer.Publish("topic", []byte("msg")):同步调用,返回 error 可直接判断;适合低频、强一致场景(如关键日志) -
producer.PublishAsync("topic", []byte("msg"), nil):异步,第三个参数是chan *nsq.Error或func(*nsq.Error);若传nil,错误直接被吞掉 - 高频生产时建议用异步 + 自定义错误 channel:
errChan := make(chan *nsq.Error, 100) producer.PublishAsync("topic", []byte("msg"), errChan) // 单独 goroutine 消费 errChan 防止阻塞 - 别在循环里反复
NewProducer:每个nsq.Producer维护 TCP 连接,短生命周期创建会耗尽本地端口
消费者连不上?大概率是 ConnectToNSQD 和 ConnectToNSQLookupd 用反了
NSQ 有两种连接模式:直连 nsqd(简单,适合单机调试),或通过 nsqlookupd 发现(生产推荐)。但 Go 客户端的 API 名字非常容易误导人——ConnectToNSQD 是直连,ConnectToNSQLookupd 才是走服务发现。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 本地开发用直连:
consumer, _ := nsq.NewConsumer("my_topic", "my_channel", config) consumer.ConnectToNSQD("127.0.0.1:4150") // ✅ 不要写 4160(那是 lookupd 的端口) - 生产环境应走 lookupd:
consumer.ConnectToNSQLookupd("127.0.0.1:4161") // ✅ HTTP 端口,不是 TCP这样 consumer 才能自动感知新增的 nsqd 实例 - channel 名字不是随便起的:同一个 topic 下,不同 channel 之间消息完全隔离;相同 channel 的多个 consumer 会负载分摊同一批消息
- 忘记调
m.Finish()?消息会不断重发——NSQ 默认 60 秒超时未 finish 就 requeue,不是“发完即弃”
nsq.Message.Body 是字节切片,JSON 解析前记得检查空值和编码
NSQ 不管你传的是字符串、JSON 还是 protobuf,一律当 []byte 原样透传。Go 消费端如果直接 json.Unmarshal(m.Body, &v),遇到空消息、乱码或 UTF-8 编码异常就会 panic。
立即学习“go语言免费学习笔记(深入)”;
- 安全解包 JSON 的最小防护:
if len(m.Body) == 0 { m.Finish() return nil } var data MyStruct if err := json.Unmarshal(m.Body, &data); err != nil { log.Printf("invalid json in msg %s: %v", m.ID, err) m.Requeue(-1) // 延迟重试,避免死循环 return nil } - 别用
string(m.Body)直接打印二进制内容(比如图片 base64),可能触发终端渲染异常或卡死 - 大消息注意内存:NSQ 默认单条消息上限 1MB,若业务需传文件,请改配置
--max-msg-size并在 consumer 侧做流式处理,而非全量加载到内存
Finish 就不停重发,没设 broadcast-address 就连不上自己。这些不是 bug,是设计取舍,得按它的逻辑来。

















