NATS 是 CNCF 毕业的轻量级高性能消息系统,单二进制即可支撑百万级连接,内存常驻仅几十 MB。它用极简的发布/订阅与请求/应答模型,解决了微服务间的异步通信、事件分发与流式处理难题。相比 Kafka 的厚重,NATS 更轻、更易运维;相比 Redis Pub/Sub,它又多了持久化与可靠投递。它是中小团队落地消息中间件的优质选择,也是边缘与 IoT 场景的常客。
NATS 是什么:一张表看懂核心定位
NATS 最早由 Derek Collison 设计,现由 Synadia 维护并捐献给 CNCF。它包含核心 NATS(纯内存、极低延迟)与 JetStream(持久化、流式、KV、对象存储)两层能力。下面用表格对比它与常见方案的边界:
| 能力 | 核心 NATS | JetStream |
|---|---|---|
| 消息持久化 | 不支持(fire-and-forget) | 支持(落盘 + 重放) |
| 消费模型 | 发布/订阅、队列组 | 持久订阅、流式消费 |
| 延迟 | 亚毫秒 | 毫秒级 |
| 附加能力 | 请求/应答 | KV 存储、对象存储、去重 |
快速开始:用 Docker 一键跑起 NATS
最省事的方式是直接用官方镜像。下面这条命令启动一个带 JetStream 的单节点,并映射到本机 4222(客户端)与 8222(监控):
docker run -d --name nats \
-p 4222:4222 -p 8222:8222 \
nats:latest -js -m 8222
# 访问 http://localhost:8222 可看到监控面板
# 查看 JetStream 状态
docker exec nats nats server check jetstream
生产环境建议用 docker-compose 把配置与数据卷固化下来:
version: "3.8"
services:
nats:
image: nats:latest
command: "-js -m 8222 --config /etc/nats/nats.conf"
ports:
- "4222:4222"
- "8222:8222"
volumes:
- ./nats.conf:/etc/nats/nats.conf
- nats-data:/data/jetstream
volumes:
nats-data:
三种核心通信模式
1. 发布 / 订阅(Pub-Sub)
最基础的广播模型。发布者向主题发消息,所有订阅该主题的服务都会收到。下面用官方 Go 客户端演示:
// 订阅端
nc, _ := nats.Connect(nats.DefaultURL)
nc.Subscribe("orders.created", func(m *nats.Msg) {
fmt.Printf("收到订单: %s\n", m.Data)
})
nc.Flush()
// 发布端
nc.Publish("orders.created", []byte(`{"id":1001}`))
2. 请求 / 应答(Request-Reply)
把消息系统当 RPC 用。请求方发出后同步等待应答,超时可控,非常适合服务间轻量调用:
// 应答服务
nc.Subscribe("svc.auth", func(m *nats.Msg) {
nc.Publish(m.Reply, []byte(`{"ok":true}`))
})
// 调用方(500ms 超时)
msg, err := nc.Request("svc.auth", []byte(user), 500*time.Millisecond)
3. 队列组(Queue Group)
多个实例加入同一队列组后,消息只会被组内一个实例消费,天然实现负载均衡与水平扩容:
nc.QueueSubscribe("tasks", "worker-group", func(m *nats.Msg) {
process(m.Data)
m.Ack() // JetStream 持久订阅才需要 Ack
})
JetStream:让消息可持久化
核心 NATS 是即发即弃,进程重启消息就丢。开启 JetStream 后,可以像 Kafka 一样创建 Stream 与消费者组,支持重放、去重与至少一次投递:
# 创建 Stream 持久化 orders.>
nats stream add ORDERS \
--subjects "orders.>" \
--storage file \
--retention limits \
--max-msgs=-1 --max-age=72h
# 发布一条会被持久化的消息
nats pub orders.created '{"id":1001}'
# 用持久消费者重放最近 100 条
nats consumer add ORDERS worker --pull --deliver all --max-pending 100
NATS 的两种部署形态:单机与超聚类
NATS 的部署可以很轻,也可以很稳。开发或边缘场景用单节点即可;生产环境则要上「超聚类(Super Cluster)」,把多个集群跨可用区连起来,既保证本地区低延迟,又能在集群间做全局消息路由。客户端只要配置多个种子节点,任意一个可达就能重连:
# 单集群 3 节点(nats.conf 片段)
cluster {
name: eu-cluster
port: 6222
routes: [
nats://nats-1:6222
nats://nats-2:6222
nats://nats-3:6222
]
}
# 客户端连多个种子,自动故障转移
nc, _ := nats.Connect("nats://nats-1:4222,nats://nats-2:4222,nats://nats-3:4222")
超聚类通过「网关(Gateway)」连接不同集群,消息可跨集群投递而无需应用感知拓扑变化,这对多活架构非常友好。
客户端重连、超时与背压
生产环境网络抖动不可避免,健壮的客户端必须配置重连策略、请求超时与背压处理。下面这组 Go 选项覆盖了最常见的三个坑:连接闪断后自动重连、请求超时不永久阻塞、以及订阅端用带缓冲的 channel 解耦,防止慢消费者拖垮发布方:
nc, _ := nats.Connect(nats.DefaultURL,
nats.RetryOnFailedConnect(true),
nats.MaxReconnects(-1), // 无限重连
nats.ReconnectWait(2*time.Second),
nats.Timeout(3*time.Second), // 连接超时
)
// 慢消费者保护:用带缓冲的 channel 解耦
ch := make(chan *nats.Msg, 1024)
sub, _ := nc.ChanSubscribe("orders.>", ch)
JetStream 还提供显式的「最大待确认(max_ack_pending)」与「最大投递(max_deliver)」参数,能在消费者处理不过来时主动限流,避免消息在内存里无限堆积。
NATS vs Kafka vs Redis:选型一张表
| 维度 | NATS | Kafka | Redis Pub/Sub |
|---|---|---|---|
| 持久化 | JetStream 支持 | 强(分区日志) | 不支持 |
| 延迟 | 亚毫秒~毫秒 | 毫秒级 | 亚毫秒 |
| 运维复杂度 | 极低(单二进制) | 高(ZK/KRaft+分区) | 低 |
| 适用规模 | 中小~大 | 超大规模 | 小~中 |
| 典型场景 | 微服务通信、边缘 | 海量日志、事件溯源 | 缓存失效、轻通知 |
如果你已经在用 Redpanda 这类 Kafka 兼容平台,可以把 NATS 当作「轻量控制面 + 实时信令」的补充:Redpanda 扛海量数据流,NATS 负责低延迟的服务间调用与事件广播。
NATS 的典型落地场景
理解一个中间件,最好从它能解决什么实际问题出发。NATS 在三类场景里尤为顺手。
事件驱动的微服务
订单服务下单后发一条 orders.created,库存、积分、通知服务各自订阅,互不直接调用。这种「发布即忘」的弱耦合让团队可以独立扩缩容,新增消费者也不影响发布方。结合队列组,单个服务还能横向多实例分摊负载,从容应对流量峰值。
边缘与 IoT 信令
NATS 单二进制、内存占用极低,非常适合部署在边缘网关或嵌入式设备上做实时信令与遥测汇聚。JetStream 的离线缓冲能力,还能在断网期间暂存数据、网络恢复后自动重放,保证边缘事件不丢。
实时通知扇出
需要把一个事件同时推给成千上万在线客户端时,NATS 的发布/订阅扇出延迟稳定在亚毫秒级。配合 WebSocket 网关切面,可以轻松把后端事件桥接成前端实时推送,做出股票行情、物流追踪这类强实时体验。
生产落地检查清单
| 检查项 | 建议 |
|---|---|
| 高可用 | 至少 3 节点超聚类(cluster),避免单点 |
| 安全 | 启用 TLS + 账号令牌,禁止匿名 |
| 持久化 | JetStream 使用 file 存储并挂独立数据卷 |
| 监控 | 开启 Prometheus 指标(/metrics),接入 Grafana |
| 客户端 | 设置重连策略与超时,避免雪崩 |
| 容量 | 设 max-msgs / max-age 防磁盘打满 |
性能与容量规划
官方基准下,单节点 NATS 可轻松达到百万级消息/秒的吞吐。实际规划时关注三个杠杆:增大 socket 缓冲区与连接池应对突发流量;JetStream 的 file 存储要单独挂载高性能磁盘,避免与系统盘争抢 I/O;给每个 Stream 设好 max-msgs 与 max-age,让旧消息自动清理,防止磁盘被缓慢打满。监控上直接抓取 8222 暴露的 Prometheus 指标,就能在 Grafana 里看到吞吐、订阅数与 JetStream 待确认堆积,提前发现瓶颈。
在 Kubernetes 上运行与暴露
在 K8s 中通常用官方 Helm Chart 部署 NATS 集群,配合 Kubernetes 的 StatefulSet 保证有序启动。对外暴露时,用 Nginx 反向代理 做 TLS 终止与限流,比直接暴露 4222 更安全。CI 阶段可在 GitHub Actions 里起一个 ephemeral 容器跑集成测试,确保发布/订阅链路可用。
若需要把 NATS 与既有 Docker 微服务编排,compose 文件里加一条 depends_on 即可保证启动顺序。对实时性要求极高的场景,也可以用 Redis 做热路径缓存、NATS 做事件广播,二者各司其职。
小结
NATS 用极简的设计换来了极低的延迟与运维成本,核心 NATS 负责实时通信,JetStream 补齐持久化短板。对于大多数中小团队,它比 Kafka 更轻、比 Redis Pub/Sub 更可靠。先把 Docker 单节点跑起来,再用队列组实现负载均衡,接着用超聚类兜住高可用,最后开启 JetStream 做持久化——四步就能在生产站稳脚跟。



