Go语言用nats.go实现消息通信的核心是确保连接稳定和消息不丢:生产环境必须显式配置重连参数、连接后立即初始化JetStream、发布时带业务唯一MsgID,否则易panic或丢消息。

Go 语言用 nats.go 实现消息通信,核心就两件事:别连不上、别丢消息。本地跑通不难,但一上生产,connect() 不加重连参数、JetStream 不提前初始化、js.Publish() 忘加 MsgID,三个动作就能让服务在半夜报警。
怎么连上 NATS 服务器才不 panic
本地开发用 nats-server 单节点,默认监听 0.0.0.0:4222,nats.Connect(nats.DefaultURL) 能通——但这只是“能连”,不是“能用”。
- 生产必须显式配置重连:
nats.MaxReconnects(60)、nats.ReconnectWait(2*time.Second)、nats.ReconnectJitter(100*time.Millisecond, time.Second) - 密码别硬编码:
nats.UserCredentials("nats.creds")加载凭证文件,而非拼nats://user:pass@host:port - 如果启用了 JetStream,连接后立刻调
jetstream.NewContext(nc);等第一次js.Publish()才初始化,会 panic
发布订阅为什么消息总丢?不是 bug 是默认策略
NATS 默认纯内存转发,消费者掉线期间发布的消息直接丢弃。这不是故障,是设计取舍。想“不丢”,必须启用 JetStream 并配好流(Stream)。
- 创建流时必须指定
Subjects和RetentionPolicy,比如jetstream.InterestPolicy(按兴趣保留)或jetstream.WorkQueuePolicy(工作队列模式) - 订阅要用
js.Subscribe("orders.created", handler, nats.DeliverPolicy(nats.DeliverAll))才能从头消费;用DeliverLastPerSubject可能跳过中间事件 - NATS 只保证 at-least-once,不保证 exactly-once;幂等必须由业务做,比如用
order_id去重 + 数据库唯一索引
request reply 模式下回执收不到?检查这三点
Request() 方法看似自动,其实隐含三处关键依赖:发布者超时控制、订阅者主动回传、reply subject 生命周期管理。
立即学习“go语言免费学习笔记(深入)”;
- 发布方调
nc.Request("orders.process", msg, 5*time.Second)后,NATS 自动创建一个一次性 reply subject,并监听它;超时未收到响应就返回 error - 订阅方收到消息后,必须手动提取
msg.Reply字段,再调nc.Publish(msg.Reply, response)回传,不能只处理完就结束 - 多个订阅者监听同一 subject 时,
Request()默认只收第一个回执(one-to-one);要收多个,得自己用nc.Subscribe("reply.*")+ 计数逻辑实现
JetStream 去重和乱序问题出在哪
重复和乱序不是 NATS 服务端的问题,而是客户端没按规范用。JetStream 本身支持去重,但得手动开,且窗口期得设对。
- 发消息必须带
nats.WithMsgID("order-123"),ID 要业务唯一;用uuid.New()这类无意义随机值,Duplicates 配置完全无效 - 流配置里的
Duplicates: 2*time.Minute是去重窗口,不是“保留时间”;若业务处理耗时超 2 分钟,重复消息仍可能被接受 - 别在循环里并发调
js.Publish()而不控制顺序;NATS 不保证多 goroutine 发布的时序,关键顺序场景应串行发
最常被忽略的是:JetStream 流的 Subjects 配置必须和实际发布的 subject 完全匹配,包括大小写和层级;少一个点、多一个空格,消息就进不了流,也留不住。


















