生产环境必须显式配置重连、超时和凭证三要素,JetStream需连接后立即初始化并手动ACK,否则必panic或消息静默丢失;去重靠业务唯一MsgID,顺序靠单goroutine发布。

直接上生产环境跑 nats.Connect("nats://localhost:4222"),十有八九会 panic 或消息静默丢失——不是 NATS 不行,是默认配置根本没为分布式场景准备。
连接必须带重连、超时、凭证三要素
本地单节点能通,不代表线上能活。NATS 客户端默认不设连接超时、不控制重试次数、也不加载凭据,遇到 DNS 慢、防火墙拦截、服务未就绪,就会卡死或无限重连。
-
nats.Timeout(5 * time.Second):单次连接最多等 5 秒,超时立刻报错,避免 goroutine hang 住 -
nats.MaxReconnects(-1):设为-1才是“一直重试到通”,0表示不重试,正数容易断连后放弃 -
nats.ReconnectWait(2 * time.Second)+nats.ReconnectJitter(100*time.Millisecond, time.Second):防抖动冲击,别让所有实例同一时刻重连 - 密码绝不硬编码:
nats.UserCredentials("nats.creds")加载凭证文件,而非拼接nats://user:pass@host:4222 - 集群地址用逗号分隔:
"nats://n1:4222,nats://n2:4222",客户端自动轮询并剔除不可用节点
JetStream 必须在连接后立即初始化
JetStream 不是开关,是上下文。如果等到第一次 js.Publish() 才调 jetstream.New(nc),会 panic——因为底层 JS client 还没准备好。
- 连接成功后立刻执行:
js, err := jetstream.New(nc),别封装进某个 handler 里延迟创建 - Stream 创建必须指定
RetentionPolicy:jetstream.WorkQueuePolicy()(任务队列)或jetstream.InterestPolicy()(按订阅保留),否则即使开了 JS,消息也不持久 - Subject 配置要显式写全:
Subjects: []string{"order.created", "user.updated"},通配符*和>不参与 Stream 匹配逻辑
高吞吐消费端不能用 Subscribe()
Subscribe() 是单 goroutine 串行处理,QPS 上不去、CPU 利用率低、延迟飙升——这不是性能瓶颈,是误用。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 微服务多实例部署,必须用
QueueSubscribe(),且所有实例传相同queue name,NATS 自动负载分摊 - 必须配
nats.MaxInflight(256)(或按业务压测调整),否则默认 inflight=1,等于又串行了 - JetStream 订阅要手动 ACK:
nats.ManualAck()+msg.Ack(),漏掉就等于没开持久化——哪怕配了nats.DeliverAll,消息也会投完即删 -
nats.AckWait(30 * time.Second)要大于业务处理最坏耗时,否则超时重发,造成重复
消息不丢不重的关键控制点
NATS 默认就是“即发即忘”,丢消息不是 bug,是设计;重复/乱序也不是服务端问题,是客户端没按规范用。
- 去重要靠两端配合:发布端加
nats.WithMsgID("order-123")(ID 必须业务语义唯一,不能用uuid.New()),Stream 配Duplicates: 2*time.Minute -
Duplicates是窗口期,不是保留时间——若订单处理耗时 3 分钟,那这 1 分钟的重复仍会被接受,得同步调大窗口或优化逻辑 - 顺序只由发布者保证:同一 subject 下,多 goroutine 并发
js.Publish()必然乱序;强顺序场景必须串行发,或用单 goroutine + channel 缓冲 - 幂等必须自己做:
order_id去重 + 数据库唯一索引兜底,JetStream 只提供 at-least-once,不承诺 exactly-once
最容易被忽略的是初始化时机和 ACK 控制——Kratos 等框架里,nats.Connect() 写在 main() 开头或 init() 里,conf.Nats.URL 还是空字符串,连的是 nats://,直接 panic;而漏掉 msg.Ack(),等于白配 JetStream。

















