nats
NATS
1、简介
NATS 是一个简单、安全、高性能的开源消息系统,专为云原生应用、IoT 消息和微服务架构设计。它提供轻量级的发布/订阅、请求/响应和队列组等核心消息模式,并通过 JetStream 提供持久化与流处理能力。
NATS 是一个开源、高性能、轻量级的分布式消息系统。它的核心设计哲学围绕着几个关键原则:
- 极致的简单性: API 和协议设计都非常简洁明了,易于理解和使用。这使得开发、部署和维护的成本较低。
- 极致的高性能: 核心 NATS (通常称为 “Core NATS” 或 “NATS Server”) 被设计为极其快速和低延迟,尤其是在发布/订阅场景下。它通常能达到每秒处理数百万条消息的吞吐量。
- 极致的轻量级: NATS Server 本身非常小巧(单个二进制文件,内存占用少),并且其客户端库也非常轻量,对资源消耗要求低。
- 云原生: NATS 天生就是为现代分布式系统和云环境设计的,支持弹性伸缩、高可用性,非常适合容器化和微服务架构。
- 基于主题的发布/订阅: 这是 NATS 最核心和最强大的通信模式。
- “At Most Once” 交付 (核心模式): 默认情况下,核心 NATS 提供尽力而为的交付保证。这意味着消息要么被成功传递一次,要么在失败时丢失(不重试)。这种设计是其实现高性能和低延迟的关键。
2、核心架构组件与概念
NATS Server: 消息传递的核心枢纽。负责接收、路由和分发消息。可以以单机模式运行,但更常见的是组成集群模式以实现高可用性和水平扩展。
NATS Client: 连接到 NATS Server 的应用程序。使用各种编程语言(如 Go, Java, JavaScript/Typescript, Python, Ruby, C#, C, Rust 等)的官方或社区维护的客户端库。
Subject: 消息传递的核心抽象。它是一个由点分隔的字符串,用于标识消息的目的地或主题。
-
例子:
orders.new,sensors.temperature.room1,user.profile.updated -
通配符:
NATS 提供了强大的主题通配符机制,使得订阅者可以灵活地订阅感兴趣的消息集合。
*(单级通配符):匹配一个层级的任意部分。例如:orders.*匹配orders.new和orders.cancel,但不匹配orders.digital.new。>(多级通配符):匹配零个或多个层级的任意部分。例如:sensors.>匹配sensors.temperature,sensors.temperature.room1,sensors.humidity等,多级通配符只能在最后一级使用。
消息: 在 NATS 中传递的数据单元。包含:
- Subject: 标识消息应被路由到哪里。
- Payload (可选): 要传输的实际数据(通常是字节数组,可以是 JSON, Protobuf, 文本等任何格式)。
- Reply Subject (可选): 用于请求/回复模式。
- Headers (可选): 键值对形式的元数据(较新的特性)。
发布者: 创建消息并将其发送到特定主题的客户端。它不需要知道谁(如果有的话)会接收这些消息。
订阅者: 对一个或多个主题(可以使用通配符)表达兴趣的客户端。它会接收发布到这些主题上的消息。一个主题可以有多个订阅者(广播),也可以没有订阅者(消息被丢弃)。
队列组: 一种负载均衡机制。
- 多个订阅者使用相同的队列组名称订阅同一个主题。
- 当消息发布到该主题时,NATS Server 会随机选择队列组中的一个订阅者来接收该消息。
- 这允许你将工作负载分布到多个消费者实例上,实现水平扩展和处理能力提升。
3、核心通信模式
Nats为subject提供了两种通配符,订阅者可通过通配符订阅多个主题的消息(subject用法会在代码中展示)。
time.us
time.us.east
time.us.east.newyork
time.cn
time.cn.beijing
1)* 会匹配单个字符串
a、可在同一个subject中多次出现
b、如time.*.east可匹配time.us.east或time.eu.east但不可匹配time.us.east.newyork
2) > 会匹配多个字符串
a、只能在同一个subject中出现一次
b、如time.us.>可匹配time.us.east和time.us.east.newyork等
1、发布/订阅(publish-subscribe):最基本和最核心的模式
- 发布者将消息发送到一个主题。
- 所有订阅了该主题(或匹配通配符模式)的订阅者都会收到该消息的副本。
- 特点:一对多广播。
nc, _ := nats.Connect(nats.DefaultURL)
// 发布者 "foo"即为主题
nc.Publish("foo", []byte("Hello World"))
// 异步订阅者
nc.Subscribe("foo", func(m *nats.Msg) {
fmt.Printf("Received a message: %s\n", string(m.Data))
}
2、请求/回复(request-reply):基于发布/订阅构建的常用模式
-
请求者发布一条消息到一个主题(通常是服务名称),并在消息中包含一个
Reply-to主题(通常是临时生成的唯一主题)。 -
响应者订阅请求主题。当收到请求消息时,处理请求,并将响应发布到请求消息中指定的
Reply-to主题。 -
请求者订阅它自己的
Reply-to主题以接收响应。
// 请求 等待10毫秒
msg, err := nc.Request("help", []byte("help me"), 10*time.Millisecond)
// 响应 收到消息后回复
nc.Subscribe("help", func(m *nats.Msg) {
nc.Publish(m.Reply, []byte("I can help!"))
})
3、队列订阅(Queue Groups):此模式下nats服务器会随机的将消息分配至订阅者,而不是广播出去。
// 将消息随机分配至 "foo"和"job_workers"中不在队列组中的订阅者依然会接收到所有消息。
nc.QueueSubscribe("foo", "job_workers", func(_ *Msg) {
received += 1;
})
4、关键特性和优势
-
卓越的性能: 极低的延迟和极高的吞吐量,尤其在核心发布/订阅模式下。
-
极简的协议: NATS 服务器和客户端之间的通信协议 (
nats://) 是文本协议(可扩展为二进制nats://),设计简洁高效,易于实现和调试。 -
无代理依赖: NATS Server 是自包含的,不需要依赖外部的数据库、ZooKeeper 或 etcd 来运行(集群协调是内置的)。
-
灵活的订阅模型: 强大的主题和通配符系统提供了极其灵活的消息路由能力。
-
内置负载均衡: 通过队列组轻松实现消费者负载均衡。
-
客户端自动重连和故障转移: 客户端库内置了强大的重连逻辑,在服务器故障或网络中断时能自动重连到集群中的其他可用节点。
-
安全性: 支持 TLS/SSL 加密通信、基于 Token 或用户名/密码的认证、NKey 认证以及更复杂的基于 JWT 和配置分发的认证授权系统。
-
多语言支持: 广泛的官方和社区支持的客户端库。
-
云原生友好: 轻量级、易于容器化、支持动态扩展,完美契合 Kubernetes 和微服务架构。
5、NATS 2.0 与 JetStream:解决持久化需求
虽然核心 NATS 的高性能源于其 “at most once” 和无持久化设计,但许多应用场景需要更强的消息保证(至少一次、正好一次)和持久化能力。为此,NATS 2.0 引入了 JetStream。
-
JetStream 是什么? 它是内置于 NATS Server 中的持久化引擎。它不是独立的服务器,而是与核心 NATS 协同工作。
-
解决的问题:
-
消息持久化: 将消息存储到磁盘(或内存),防止服务器重启导致消息丢失。
-
增强的交付语义: 支持 “At Least Once” 交付(通过确认机制确保消息被处理)和 “Exactly Once” 处理(通过消费者序列号去重)。
-
流处理: 将消息组织成持久的流。流定义了消息的存储策略(如保留策略:基于时间、消息数、大小等;存储后端:文件或内存)。
-
消费者:
从流中拉取或推送消息的实体。支持多种消费模式:
- 推模式: JetStream 主动将消息推送给消费者(类似核心订阅)。
- 拉模式: 消费者主动从流中请求一批消息(批处理友好)。
- 支持多种确认机制: Ack (成功), Nak (重试), Term (不再重试), AckNext (处理中并要求下一条) 等。
- 支持消费者配置: 如重试策略、起始位置、过滤等。
-
-
核心 NATS 与 JetStream 的关系:
- 核心 NATS 负责高性能、低延迟的即时消息传递(发布/订阅、请求/回复)。
- JetStream 负责需要持久化和更强保证的流式消息处理。
- 它们共享相同的 NATS Server 基础设施、网络协议和客户端连接。客户端可以使用同一个连接同时进行核心 NATS 操作和 JetStream 操作。
- 发布到配置了 JetStream 的主题的消息会被自动持久化到流中。
6、与其他消息系统的比较
vs RabbitMQ:
- NATS 更轻量、性能更高(尤其在纯 pub/sub)。
- RabbitMQ 功能更丰富(多种交换类型、死信队列、更复杂的路由规则、插件体系),但相对更重。
- NATS 主题通配符比 RabbitMQ 的 Topic Exchange 更灵活。
- 核心 NATS 的持久化/保证不如 RabbitMQ,但 JetStream 弥补了这一点。
vs Apache Kafka:
- Kafka 是分布式流处理平台,核心是持久化、分区、有序的日志。吞吐量极高,适合大数据管道和流处理。
- NATS (Core) 更轻量、延迟更低,更适合实时消息传递和命令控制。
- NATS 的订阅模型比 Kafka 的 Consumer Group 更灵活(通配符订阅)。
- Kafka 的学习曲线和运维复杂度通常高于 NATS。
- JetStream 提供了类似 Kafka 的持久化流能力,但设计更简单,与核心 NATS 集成更紧密。
vs MQTT Brokers (如 Mosquitto, EMQX):
- MQTT 是标准协议,专注于 IoT,有 QoS 级别。
- NATS 原生协议 (
nats://) 性能通常更高,功能更丰富(如请求/回复、队列组)。NATS 也支持 MQTT 协议网关。 - NATS 的集群和高可用设计通常更易于管理和扩展。
docker compose
services:
nats:
image: nats:2.15-alpine3.22 # 使用 alpine 版便于调试,latest 版是 scratch 基础镜像
container_name: nats
restart: unless-stopped
ports:
- "4222:4222" # 客户端
- "8222:8222" # HTTP 监控
command: ["-js", "-sd", "/data", "-m", "8222"] # -js 开启 JetStream
volumes:
- nats_data:/data
volumes:
nats_data:
2、核心概念
subject 主题
Subject 是 NATS 中消息的路由标识,采用点分层级命名(如 orders.created、users.login)。发布者向 Subject 发送消息,订阅者订阅 Subject 接收消息。支持通配符:* 匹配单个 token,> 匹配一个或多个 token。
消息模式
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 发布/订阅 | 一条消息分发给所有订阅者 | 事件广播、通知 |
| 队列组 | 同一队列组内的订阅者负载均衡消费 | 任务分发、工作队列 |
| 请求/响应 | 客户端发送请求并等待回复 | RPC 调用、服务查询 |
JetStream
JetStream 是 NATS 的持久化层,提供流(Stream)和消费者(Consumer)抽象,支持消息持久化、至少一次/恰好一次投递、消息重放等能力。
开发示例
发布者
package main
import (
"log"
"github.com/nats-io/nats.go"
)
func main() {
nc, err := nats.Connect(nats.DefaultURL)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
err = nc.Publish("hello", []byte("Hello NATS!"))
if err != nil {
log.Fatal(err)
}
nc.Flush()
log.Println("Message published")
}
订阅者
package main
import (
"log"
"runtime"
"github.com/nats-io/nats.go"
)
func main() {
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
nc.Subscribe("hello", func(msg *nats.Msg) {
log.Printf("Received: %s", string(msg.Data))
})
runtime.Goexit()
}