生产环境NATS连接必须显式配置重连策略(如nats.MaxReconnects(60)、ReconnectJitter)、凭证加载(nats.UserCredentials)和JetStream初始化(js, err := nc.JetStream()),否则默认裸连会导致断连、丢消息;纯Pub/Sub模式下消费者掉线即丢消息,需启用JetStream并正确创建Stream、设置RetentionPolicy、Durable订阅及MsgID去重。

裸调 nats.Connect(nats.DefaultURL) 在本地能发第一条消息,但一上生产就断连、丢消息、重复消费——这不是 NATS 有问题,是客户端根本没配对。
怎么连才不算“裸连”
默认连接不重试、无超时、不验健康,网络抖动一次就卡死或静默断开。必须显式加策略:
-
nats.MaxReconnects(60):别用 -1(无限重连),容易压垮服务端 -
nats.ReconnectWait(2 * time.Second):固定间隔太生硬,得配合 jitter -
nats.ReconnectJitter(100*time.Millisecond, time.Second):避免所有实例同一时刻重连 - 密码别写在 URL 里,比如
"nats://user:pass@host:4222";改用nats.UserCredentials("user.creds") - 启用了 TLS?URL 必须用
tls://前缀,且服务端证书要可信,否则Connect()会卡住不报错
JetStream 初始化必须在启动阶段做
js.Publish() 第一次 panic 不是代码写错,是 jetstream.Context 根本没初始化。NATS 客户端不会自动准备 JetStream 能力。
- 连接成功后立刻执行:
js, err := jetstream.New(nc),并检查err - 建议加健康检查:
js.AccountInfo(),失败则提前退出 - 流(Stream)创建是一次性动作,改配置得删了重建,不是热更新
为什么发了消息,订阅者收不到
纯 NATS 是内存转发,消费者掉线期间发布的消息就是会丢——这不是 bug,是设计。想“不丢”,三件事缺一不可:
立即学习“go语言免费学习笔记(深入)”;
- 启用 JetStream 后,必须调
js.AddStream()显式创建流,光连上没用 -
RetentionPolicy得选对:jetstream.InterestPolicy(只存活跃订阅者需要的)或jetstream.WorkQueuePolicy(每条只投一次) - 订阅时用
nats.DeliverPolicy(nats.DeliverAll)才能从头消费;默认行为是跳过已有消息 - 加
nats.Durable("processor-name")才能记住消费位置,否则每次都是全新 offset
去重和顺序不是 JetStream 自动保证的
开了 JetStream ≠ 天然幂等、天然有序。重复扣款、状态乱序,八成是没按规范发消息。
- 去重要生效,发布时必须带
nats.WithMsgID("order-12345"),ID 必须是业务唯一标识(如order_id),不是uuid.New() - 流配置里的
Duplicates: 2*time.Minute是去重窗口,不是保留时间;若处理耗时超 2 分钟,重复仍会发生 - 多 goroutine 并发
js.Publish()同一 subject,顺序无法保证;关键顺序场景必须用单 goroutine + channel 缓冲,或按业务逻辑加序列号 - 哪怕只做日志打印,也得包一层
defer msg.Ack(),否则只要 handler 提前 return,消息就进重试队列


















