NATS 实战:轻量级高性能消息系统

NATS 是 CNCF 毕业的轻量级高性能消息系统,单二进制即可支撑百万级连接,内存常驻仅几十 MB。它用极简的发布/订阅与请求/应答模型,解决了微服务间的异步通信、事件分发与流式处理难题。相比 Kafka 的厚重,NATS 更轻、更易运维;相比 Redis Pub/Sub,它又多了持久化与可靠投递。它是中小团队落地消息中间件的优质选择,也是边缘与 IoT 场景的常客。

NATS 是什么:一张表看懂核心定位

NATS 最早由 Derek Collison 设计,现由 Synadia 维护并捐献给 CNCF。它包含核心 NATS(纯内存、极低延迟)与 JetStream(持久化、流式、KV、对象存储)两层能力。下面用表格对比它与常见方案的边界:

能力核心 NATSJetStream
消息持久化不支持(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:选型一张表

维度NATSKafkaRedis 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 做持久化——四步就能在生产站稳脚跟。

上一篇 OceanBase 实战:分布式数据库部署与性能调优
下一篇 Benthos 实战:用 YAML 搭建流式数据管道
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]