裸连 NATS 默认不持久、不重试、不幂等,需手动补全三块:加健康检查与重连策略、用凭证文件替代URL密码、启用JetStream并初始化;消息不丢须配Stream保留策略与DeliverAll;防重复需Publish带业务唯一MsgID且设Duplicates窗口;消费者必须幂等、显式ACK/Nak、去重校验。

裸连 NATS 默认不持久、不重试、不幂等——这不是 bug,是设计。想用它跑生产微服务,必须手动补全这三块。
怎么连上 NATS 才算“真正可用”
很多人写 nats.Connect(nats.DefaultURL) 就以为连上了,其实只是建立了一条 TCP 连接,没健康检查、没断线重连、没超时控制。网络抖一下,连接就挂死,后续所有 Publish 都静默失败。
- 必须显式加重连策略:
nats.MaxReconnects(60)、nats.ReconnectWait(2*time.Second)、nats.ReconnectJitter(100*time.Millisecond, time.Second) - 别把密码写在 URL 里,改用凭证文件:
nats.UserCredentials("user.creds") - 如果启用了 JetStream,连接后立刻初始化:
js, _ := jetstream.New(nc),否则第一次js.Publish()可能 panic
为什么消息总“丢”,以及怎么让它不丢
默认 NATS 是纯内存转发,消费者掉线期间发布的消息直接蒸发——不是你代码写错了,是它压根没存。要“不丢”,只有一条路:开 JetStream,并正确配流(Stream)。
- 创建 Stream 必须指定
RetentionPolicy,比如jetstream.InterestPolicy(按兴趣保留)或jetstream.WorkQueuePolicy(仅保留未确认) - 订阅时要用
nats.DeliverPolicy(nats.DeliverAll),否则默认只收新消息 - 没开 JetStream 就别指望
Subscribe能回溯,那是幻觉
JetStream 发布消息时重复和乱序怎么防
JetStream 本身支持去重和有序,但得你主动开、开对、用对。它不会替你做决定。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 发消息必须带 ID:
js.Publish("orders.created", data, nats.WithMsgID("order-123")),ID 要业务唯一(不能是随机 uuid) - 流配置里的
Duplicates: 2*time.Minute是去重窗口,不是存储时限;若处理耗时超 2 分钟,重复仍会进 - 多 goroutine 并发调
js.Publish()不保证顺序;关键路径应串行发布,或用单 goroutine + channel 缓冲
消费者怎么写才不算“裸奔”
没幂等、没 ACK、没错误重试的消费者,在 NATS 里等于定时炸弹。一次网络抖动,订单就扣两次库存。
- 事件结构体必须含
Type和Version字段,避免消费者无法路由或解析失败 - 处理完必须显式
msg.Ack();不调就会在AckWait超时后重投 - 失败时用
msg.NakWithDelay(10 * time.Second)+nats.MaxDeliver(3)模拟死信,别依赖“自动跳过” - 用
order_id去重表(Redis SETNX 或 DB 唯一索引)拦截重复,这是最后一道防线
JetStream 的配置项看着少,但每个都卡在关键路径上:Stream 没设 RetentionPolicy,消息就不落盘;Publish 没带 MsgID,Duplicates 就形同虚设;Subscribe 没传 group 名,消费者组负载分摊就失效。这些不是“可选优化”,是上线前必须核对的 checklist。

















