生产环境NATS连接必须配置有限重试(如nats.MaxReconnects(60))与抖动策略(nats.ReconnectWait+Jitter),禁用无限重连;JetStream需显式初始化、创建流并设WorkQueuePolicy,发布须带业务唯一MsgID,消费端用QueueSubscribe+ManualAck+MaxInflight。

连接必须带重连抖动与有限重试,不能用 nats.MaxReconnects(-1)
集群里所有实例同时断连后一起重试,会瞬间打爆 NATS 服务端连接队列。默认 nats.MaxReconnects(-1) 是无限重连,生产环境必须设为有限值(比如 60),配合抖动策略错开重连时间。
-
nats.MaxReconnects(60):最多重试 60 次,之后放弃并让监控告警 -
nats.ReconnectWait(2 * time.Second)+nats.ReconnectJitter(100*time.Millisecond, time.Second):每次等 2 秒 ± 最多 1 秒随机偏移,避免雪崩式重连 - 集群地址写成逗号分隔:
"nats://n1:4222,nats://n2:4222,nats://n3:4222",客户端自动轮询并剔除不可用节点 - 别用
nats://user:pass@...这种明文 URL,改用nats.UserCredentials("nats.creds")加载凭证文件
JetStream 流必须显式创建,且 RetentionPolicy 要选 jetstream.WorkQueuePolicy
秒级高并发下,“消息只投一次”比“存所有历史”更关键。默认的 InterestPolicy 会清理掉没有活跃订阅者的消息,而 WorkQueuePolicy 才能保证每条消息至少被一个消费者处理一次。
- 流创建是一次性动作,改配置得删流重建,不是热更新
- 必须调
js.AddStream()显式创建,不能依赖 publish 自动触发;否则第一次js.Publish()可能 panic - 配
Duplicates: 30 * time.Second,窗口期要略大于最长业务处理耗时,否则去重失效 - 主题名严格匹配,
"event.order.created"和"event.order.created.v1"完全无关
消费端必须用 QueueSubscribe() + nats.MaxInflight() + 手动 ACK
单 Subscribe() 在高并发下是性能黑洞,所有消息串行进一个 goroutine,QPS 上不去还容易卡死。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 用
QueueSubscribe("events", "worker-group", handler, nats.Durable("worker-1"))实现多实例负载分摊 - 必须设
nats.MaxInflight(256)(或按压测结果调优),否则默认 inflight=1,等于又串行了 - 必须开
nats.ManualAck(),并在业务逻辑处理完后显式调msg.Ack();漏掉就等于没启用持久化 - 失败用
msg.NakWithDelay(5 * time.Second)+nats.MaxDeliver(3)控制重试节奏,避免高频冲击下游
发布端防乱序:同一 subject 必须串行发布,ID 必须是业务唯一标识
NATS 不保证多 goroutine 并发发到同一 subject 的顺序。秒级事件流里状态错乱(比如“已支付”在“已创建”之前到达)往往就源于此。
立即学习“go语言免费学习笔记(深入)”;
- 关键路径不要在循环里直接起 goroutine 调
js.Publish(),改用单 goroutine + channel 缓冲串行发布 - 每条消息必须带
nats.WithMsgID("order-20260630-001"),ID 是订单号、用户 ID 这类可追溯业务主键,不是uuid.New() - 没
WithMsgID,就算流开了Duplicates也白搭 - 如果真需要跨 subject 保序(比如创建→支付→发货),得靠业务层加全局序列号或时间戳校验,NATS 不管这事
最常被忽略的一点:JetStream 初始化不是“连上就自动可用”,必须在 app.Run() 前完成 jetstream.New(nc) 并验证上下文——比如调一次 js.AccountInfo()。否则第一次 publish 就 panic,而且错误堆栈不提示真正原因。

















