结论:Go应用通过Deployment部署并连接NATS JetStream,关键在于连接稳、Stream建对、重启不丢状态;必须显式启用JetStream服务端、初始化js.Context后幂等创建Stream、readinessProbe同步校验连接与Stream存在,Consumer用固定Durable名保障进度续接。

直接上结论:用 Deployment 部署 Go 应用,用 NATS JetStream 的官方 Go 客户端(nats.go)连接启用了 JetStream 的 NATS 集群,关键不是“能不能连”,而是“连得稳不稳、流建得对不对、重启后状态丢不丢”。
怎么让 Go 应用自动连接 JetStream 并创建 Stream
JetStream 不是开箱即用的——NATS 服务器默认不启用 JetStream,客户端也不会自动帮你建 Stream。必须显式调用 js.AddStream,且该操作需幂等(重复执行不能报错)。
- 在 Go 应用启动时,先用
nats.Connect连 NATS,再通过nats.JetStream获取JetStreamContext - 调用
js.AddStream传入&nats.StreamConfig{...},注意设置Name和Subjects;若 Stream 已存在,nats-gov1.29+ 会返回nats.ErrStreamNameAlreadyInUse,需捕获并忽略 - 不要在每次请求里建 Stream,只在初始化阶段做一次;建议加
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)防卡死 - Kubernetes 中若多个 Pod 同时启动,可能并发建 Stream,所以必须容忍
ErrStreamNameAlreadyInUse,否则 Pod 会 CrashLoopBackOff
为什么 Deployment 的 readinessProbe 不能只查 HTTP 健康端点
Go 应用可能 HTTP 服务已就绪,但 JetStream 连接还没建立成功,或 Stream 创建失败——这时流量进来,js.Publish 就会 panic 或阻塞。
- readinessProbe 的
httpGet路径(如/ready)必须同步检查:nats.Conn.Status() == nats.CONNECTED、js.AccountInfo()能返回、关键 Stream 存在(用js.StreamInfo("mystream")) - 避免把
js.Publish放在 probe handler 里——它默认带重试,probe 超时会导致 Pod 卡在 NotReady - 如果 JetStream 临时不可用(比如 NATS 集群滚动升级),probe 应返回 503,让 Kubernetes 暂停流量,而不是让应用硬扛失败
StatefulSet 还是 Deployment?JetStream Consumer 怎么持久化
普通消息消费用 Deployment 完全够用;但如果你用的是 OrderedConsumer 或需要严格保证单实例消费(如事件溯源),就得考虑 StatefulSet + 显式指定 Durable 名字。
-
Consumer的状态(如Delivered、Ack位置)由 JetStream 服务端保存,和 Pod 生命周期无关——只要Durable名字固定,Pod 重建后自动续接进度 - 别用随机生成的
Durable名(如uuid.New().String()),否则每次重启都新建 Consumer,丢失 ack 状态 - 如果用
ephemeralConsumer(不设Durable),那必须确保业务能容忍重复投递——Kubernetes 重启 Pod 后,这个 Consumer 就彻底消失 - JetStream 的 Consumer 不依赖 PVC,不需要 StatefulSet 的稳定存储;除非你同时存本地 offset 文件(不推荐),否则 Deployment 更轻量
容易被忽略的三个点
一是 NATS 服务端必须开启 JetStream:Helm Chart 里要设 jetstream.enabled=true,Operator 配置里得加 jetstream: { enabled: true };二是 Go 应用里所有 js.Publish 和 js.Subscribe 必须用同一个 JetStreamContext 实例,别每次 new;三是 js.PublishAsync 返回的 PubAckFuture 必须显式 .Wait() 或 .Ok(),否则错误静默丢失——Kubernetes 日志里根本看不到 Publish 失败。


















