NATS JetStream KV 是原生分布式KV存储,需服务端启用JetStream(如docker run -js),Put为版本递增写入,Update为CAS操作,Watch基于推送且需持续消费,历史版本需主动Purge或限制数量。

直接用 nats.Connect 连上 NATS 服务器后,调用 jetstream.KV 就能拿到一个分布式、带版本、自动复制的轻量缓存——它不是“模拟 Redis”,而是原生 KV 存储,不依赖外部进程,也不需要自己维护一致性协议。
初始化 JetStream KV 前必须启用 JetStream
NATS 默认不开启 JetStream,nats.Connect 成功不代表 KV 可用。你得先确认服务端已启用 JetStream(比如启动时加 -js 参数),或连接后显式检查:
- 用
nats.JetStream()获取jetstream.JetStream实例,再调用js.CreateKeyValue;如果返回js.ErrJetStreamNotEnabled,说明服务端没开 JetStream,不是客户端代码问题 - 本地开发可快速起一个带 JetStream 的单节点:
docker run -p 4222:4222 nats:2.10.16 -js - 集群模式下,KV 存储会自动在可用节点间分片+复制,无需手动指定副本数(除非你调
WithReplicas(3))
KV.Put 和 KV.Update 的语义差异很关键
写入不是简单覆盖,而是带版本控制的操作:
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
-
KV.Put("user:1001", []byte(`{"name":"alice"}`)):总是成功,生成新版本号(revision),旧值保留 -
KV.Update("user:1001", []byte(`{"name":"alice2"}`), 1):仅当当前 revision == 1 时才成功,否则报nats.ErrKeyWrongLastSeq—— 这是 CAS 场景的基础 - 如果你需要“覆盖即生效”,就用
Put;需要“先读再比对再写”,就用Get拿到Revision后传给Update
Watch 机制不是轮询,但要注意 context 生命周期
KV.Watch 返回一个 KeyWatcher,底层是订阅一个内部流主题,变更时推送到 channel:
立即学习“go语言免费学习笔记(深入)”;
- watcher 的 channel 是阻塞接收,不消费就会卡住整个 watcher(包括后台心跳)
- 务必在 goroutine 中持续
range watch.Chan(),且不能让context提前 cancel,否则 watcher 会静默退出 - 每次收到的
KeyValueEntry包含Key、Value、Revision、Created和Operation(PUT/DEL),删掉的 key 也会通知,别漏处理
内存占用和 GC 友好性容易被低估
JetStream KV 数据默认存在内存里(可配磁盘),但每个 key 的历史版本都保留,revision 越多,内存越高:
- 不做清理的话,一个高频更新的 key(如计数器)可能积累几百个版本,
KV.History("key")能查到全部 - 用
KV.Purge("key")可清空历史,只留最新版;或建 bucket 时设WithHistory(5)限制最大版本数 - value 是
[]byte,传结构体记得json.Marshal,别直接传指针或 interface{},否则序列化失败还不报错

















