基于NATS的消息总线实战_高性能微服务内部通信

2次阅读

连接 nats 服务器失败时,应将客户端地址从 “nats://localhost:4222” 改为 “nats://host.docker.internal:4222″(docker desktop)或 “nats://172.17.0.1:4222″(docker 默认网关),kubernetes 下则使用 service dns 名如 “nats://nats-cluster:4222″。

基于NATS的消息总线实战_高性能微服务内部通信

连接 NATS 服务器时,connect() 超时或报 no servers available 怎么办

本地开发最常卡在这一步:明明 docker run -p 4222:4222 nats:latest 跑起来了,但 Node.js 或 Python 客户端死活连不上。根本原因不是服务没启,而是客户端默认只连 "nats://localhost:4222",而 Docker Desktop(尤其 macos/windows)里 localhost 指向宿主机,容器内网络却走的是桥接网关,localhost 在容器内部不等于宿主机。

  • Node.js 开发时,把地址换成 "nats://host.docker.internal:4222"(Docker Desktop 支持),或直接用 "nats://172.17.0.1:4222"(Docker 默认网桥网关 IP)
  • Python 使用 nats-py 时,同样要改地址;如果用 asyncio 启动多个协程共用一个连接,记得用 await nc.flush() 主动刷缓冲,否则首次 publish 可能静默失败
  • Kubernetes 环境下,别硬写 localhost,应通过 Service DNS 名访问,比如 "nats://nats-cluster:4222",且确保 NatsCluster CR 已就绪(kubectl get natsclusters 看 STATUS 是 Running

发布消息后订阅者收不到,subscribe() 为什么像“没生效”

这不是 bug,是 NATS 的主题匹配机制在起作用:它严格区分大小写、空格和点号层级,"orders.created""orders.created "(尾部空格)完全不匹配,也不会报错,只是静默丢弃。

  • 所有主题名统一用小写 + 英文点分隔,例如 "user.Event.created",避免用下划线或大写字母
  • Node.js 中 nc.subscribe("orders.created", handler) 必须在 nc.publish("orders.created", ...) 之前调用;如果先 publish 再 subscribe,那条消息就永远丢失了(基础 NATS 不缓存未订阅主题的消息)
  • Python 的 nats-py 默认使用异步回调,若 handler 函数里有阻塞操作(比如同步 http 请求),会卡住整个连接的事件循环——必须用 await asyncio.to_thread(...) 或改用线程

需要消息不丢?别只开 JetStream,先确认这三件事

JetStream 不是开关一按就“自动持久化”,它是一套需显式配置的流式存储系统。启用后若仍丢消息,大概率栽在这几个环节上。

  • 创建流(Stream)时,必须指定 subjects 匹配你实际 publish 的主题,比如流配置 subjects: ["orders.*"],你就不能往 "payments.completed" 发——它压根不进流
  • 消费者(Consumer)要设 deliver_policy: "all" 才能从头重放;默认是 "last_received",重启后只收新消息
  • .NET 项目用 NATS.Client 时,Request() 方法底层仍是 publish + reply subject,它本身不走 JetStream;想持久化请求响应链,得手动把请求发到带 JetStream 的主题,并由服务端用 msg.Reply 发回——这个 reply 主题也得单独建流

queue group 用于负载均衡,但为什么两个实例还在抢同一消息

Queue Group 是 NATS 实现服务水平扩展的关键,但它只在“同组内”做竞争消费。如果看到两个实例都处理了同一条消息,基本是名字没对齐或连接隔离了。

  • 所有实例订阅时必须用**完全相同的** queue 名,比如 nc.subscribe("logs", queue="log-processor", cb=...),拼错一个字母(如 "log-processor ")就变成独立组
  • Go 客户端默认开启 reconnect,但 Node.js 的 nats.js 需手动传 reconnect: true, max_reconnect_attempts: -1,否则断连后旧 queue group 成员不会自动退出,新实例加入时可能短暂出现重复消费
  • 测试时别用同一个进程起多个 subscriber —— 它们共享连接上下文,NATS 会当成一个消费者;必须用不同进程或容器模拟真实实例

JetStream 的流配置、queue group 的命名一致性、客户端重连策略——这些地方不细看文档就动手,十次部署九次要返工。尤其是主题命名和流绑定,一旦上线再改,就得停服重建流,数据还可能丢。

text=ZqhQzanResources