NATS详解

一、NATS 配置详解

1.1 命令行参数功能

nats-server \
  -a 0.0.0.0 \        # 监听地址,默认“0.0.0.0”
  -p 4222 \           # 客户端通信端口,默认 4222
  -m 8222 \           # HTTP 监控端口,默认不开启
  -js \               # 启用 JetStream 功能
  --store_dir ./data  # JetStream 存储目录
  -c /path/nats.conf \# 指定配置文件
  -l /var/log/nats.log \ # 日志文件
  -V \                # 输出协议级调试信息
  -t                  # 验证配置文件语法并退出

参数优先级:命令行 > 配置文件 > 默认值(如 -t 为语法校验后退出)

1.2 配置文件详解(以 YAML/CONF 格式书写)

支持注释、include 引用、变量替换 ($VAR 或环境变量),适合正式环境部署。

############################################################
# 基础配置
############################################################

# NATS Server 名称,可用于集群状态监控和日志识别
server_name: "nats-main"

# 客户端连接监听地址与端口(默认 0.0.0.0:4222)
listen: 0.0.0.0:4222

# HTTP 管理监控接口(默认关闭,常用于 Prometheus/健康检查)
http_port: 8222

# 启用调试和追踪(仅开发环境建议开启)
debug: false
trace: false

# 日志文件路径(也可以在命令行通过 -l 指定)
log_file: "./logs/nats.log"

# PID 文件路径
pid_file: "./nats-server.pid"

# 写日志到 stdout(默认 true)
logtime: true

############################################################
# 安全认证配置
############################################################

authorization {
  # 用户列表(用户名 + 密码 + 权限)
  users = [
    {
      user: "admin"
      password: "admin123"
      permissions: {
        publish: { allow: ["admin.>"] }
        subscribe: { allow: ["admin.>"] }
      }
    },
    {
      user: "reader"
      password: "reader123"
      permissions: {
        publish: { deny: [">"] }
        subscribe: { allow: ["public.>"] }
      }
    }
  ]

  # 允许匿名访问(默认 false,设置为 true 表示不需要认证)
  # no_auth_user: "guest"
}

# 认证配置-token认证
#authorization {
  # Token 认证(所有客户端使用相同的 token 连接)
#  token: "your-secret-token-here" # 替换为你的实际 token
#}


############################################################
# JetStream 持久化配置
############################################################

jetstream {
  enabled: true                      # 启用 JetStream 功能
  store_dir: "./data/jetstream"     # JetStream 存储路径(文件或内存)
  domain: "default"                 # 多租户 JetStream 域名(可选)
  max_mem_store: 2Gb                # JetStream 使用的最大内存
  max_file_store: 10Gb              # JetStream 使用的最大文件空间
}

############################################################
# WebSocket 支持(可选)
############################################################

websocket {
  enabled: false				# 启用 WebSocket 支持
  host: "0.0.0.0"
  port: 9222					# WebSocket 监听端口
  same_origin: true                # 是否要求前端连接来源一致
  # 支持的协议(一般为 "nats")
  # allowed_origins: ["https://example.com"]
}

############################################################
#  集群配置(支持横向扩展)
############################################################

cluster {
  name: "nats-cluster"
  listen: "0.0.0.0:6222"              # 当前节点监听的 cluster 端口
  # 当前节点的路由列表(可连接到其它集群节点)
  routes: [
    "nats://nats-node-2:6222"
    "nats://nats-node-3:6222"
  ]

  connect_timeout: 2s
  # 如果开启认证,集群间连接也需要账号密码
  authorization {
    user: "cluster"
    password: "clusterpass"
  }
}

############################################################
# Gateway 跨数据中心连接(多集群联邦)
############################################################

gateway {
  name: "dc1"
  listen: "0.0.0.0:7522"
  advertise: "nats-dc1:7522"          # 本地 gateway 对外地址

  gateways: [
    {
      name: "dc2"
      urls: ["nats://nats-dc2:7522"]
    }
  ]
}

############################################################
# LeafNodes(连接边缘节点/前端集群)
############################################################

leafnodes {
  listen: "0.0.0.0:7422"
  remotes = [
    {
      url: "nats://leafnode-user:leafpass@leafhost:7422"
    }
  ]
}

############################################################
# 资源限制(防止滥用)
############################################################

# 限制每个客户端的最大连接数/订阅数/消息大小等
limits {
  max_connections: 10000             # 最大连接数
  max_subscriptions: 100000          # 最大订阅数
  max_payload: 1Mb                   # 最大消息负载
  write_deadline: 2s                 # 写入超时时间
}

############################################################
# TLS 加密配置(生产环境推荐开启)
############################################################

# tls {
#   cert_file: "./certs/server-cert.pem"
#   key_file: "./certs/server-key.pem"
#   ca_file: "./certs/ca.pem"
#   verify: true
#   timeout: 5
# }

############################################################
#  Include 支持(引用其他配置)
############################################################

# include "./more.conf"   # 支持引入多个配置文件模块化管理

注意事项

  • JetStream 配置必须在启用了 jetstream.enabled = true 的前提下生效。
  • gatewayleafnodescluster 三者用途不同,不可混淆。
  • 所有单位支持:k, m, g,如 512Mb, 1Gb,或 10k
  • 注释支持 #,不支持 //
  • WebSocket 是为了让 NATS 更好支持“浏览器 / Web / 移动 / 小程序 / 受限网络环境”下的实时通信需求

1.3 集群配置

LeafNodesGatewayCluster 是 NATS 构建分布式系统的三种关键网络拓扑机制,它们解决的是不同层级的连接、同步和隔离问题

  ┌─────────┐        ┌─────────────┐        ┌───────────────┐
  │ LeafNode│ 连接到 │  Cluster 主节点 │ ↔  │ Gateway 到其他集群 │
  └─────────┘        └─────────────┘        └───────────────┘
     👇                    👇                          👇
  边缘设备           同一集群内节点         不同集群之间同步
  轻量NATS           服务注册 + 状态一致    跨 DC  / 多租户 / 环境联通
  1. Cluster —— 基本集群,同一个“集群名”

    作用: 把多个 NATS 节点组成一个逻辑上的“集群”,用于:

    • 负载均衡(客户端连接到任一节点)
    • 状态共享(连接信息 / JetStream stream 分布)
    • 高可用(某节点挂了,客户端自动连其他)

    用法:

    # 每个节点都要配置
    cluster {
      name: "nats-cluster"
      listen: "0.0.0.0:6222"    # 本地集群通信端口
      routes: [
        "nats://nats-node2:6222",
        "nats://nats-node3:6222"
      ]
    }
    

    所有节点共享相同 cluster name,构成同一个集群。

    应用场景:

    • 一个系统内部横向扩展多节点
    • 客户端通过 VIP 接入 NATS
    • JetStream 的 stream 可选复制、分片到各个节点
  2. LeafNodes —— “子节点”,边缘设备直连中心

    作用:

    LeafNode 是一个 小型 NATS 实例,连接到中心节点(Hub):

    • 转发本地客户端连接到主节点
    • 不共享状态(如 JetStream、连接列表等)
    • 不需要集群名、也不参与 cluster 协议

    Leaf 配置:

    leafnodes {
      listen: "0.0.0.0:7422"  # Leaf节点监听端口
    }
    

    Hub 配置(中心服务器):

    leafnodes {
      remotes = [
        {
          url: "nats://leaf-user:pass@leafhost:7422"
        }
      ]
    }
    

    LeafNode 是一种轻量级“接入点”或“代理”,适合边缘设备、内网小节点、IoT 设备接入总部。

    应用场景:

    • IoT 网关:边缘设备只部署 leaf NATS 实例
    • VPC 接入:私网内只跑 leaf,再转发到公网节点
    • Dev → Prod 中转:开发环境通过 Leaf 连接生产只读
  3. Gateway —— 多个集群之间的桥梁连接

    作用: Gateway 是用于连接 不同集群 的桥梁(集群之间同步消息):

    • 各自 cluster 内保持一致性
    • Gateway 之间同步 subject、订阅
    • 支持权限隔离、JetStream 隔离
    • 避免跨集群大量 route 消息造成冗余

    配置:

    gateway {
      name: "dc1"
      listen: "0.0.0.0:7522"         # 本地 gateway 端口
      advertise: "nats-dc1:7522"     # 外部看到的地址
    
      # 远程集群
      gateways: [
        {
          name: "dc2"
          urls: ["nats://dc2-nats1:7522"]
        }
      ]
    }
    

    每个集群必须起码有一个节点配置该 gateway,即可建立多集群同步网络。

    应用场景:

    • 多数据中心(dc1, dc2)同步用户事件流
    • SaaS 平台:租户隔离后做 gateway 互通
    • 流量治理:A 只允许 pub,B 只 sub
    • “多活架构”中连接各活跃集群

1.3 动态 reload 配置功能

修改配置后,可用命令或发送信号动态重加载(不中断连接)

nats-server -signal reload

支持多次 reload,例如新增用户、改变 limits 参数,非常适合线上运维。

1.4 常用子模块介绍

子模块 配置内容 注释说明
authorization 用户 / JWT / permissions 客户端连接认证与访问控制
tls TLS/SSL 证书路径等 通信加密
monitoring HTTP Stats & profiling 性能监控端点
websocket WebSocket 支持 用于前端实时通信(如 WebSocket 客户端)
cluster 节点互联参数 用于大型服务部署
websocket 支持 WebSocket 客户端接入

二,编写Nats配置类

2.1 基础配置bean

在 Spring Boot 中使用 NATS 需要配置 Connection Bean 来建立与 NATS 服务器的连接,在 @Configuration 类中定义 NATS 连接 Bean:

@Configuration
public class NatsConfig {
    @Value("${nats.server}")
    private String natsServer;
    @Value("${nats.name}")
    private String serverName;

    @Bean
    public Connection natsConnection() throws IOException, InterruptedException {
        Options options = new Options.Builder()
                .server(natsServer) // 服务器地址数组
                .connectionTimeout(Duration.ofSeconds(5)) // 连接超时时间
                .reconnectWait(Duration.ofSeconds(1)) // 重连等待时间
                .maxReconnects(10) // 最大重连次数,-1代表无限重连
                .connectionName(serverName) // 连接名称(服务端可见)
                .errorListener(new CustomErrorListener()) // 错误监听器
                .connectionListener(new CustomConnectionListener())//连接状态监听器
                .reconnectBufferSize(1024 * 1024 * 1024)//重连缓冲区大小
                .build();
        return Nats.connect(options);
    }

}

2.2 监听器配置

在 NATS Java 客户端中,有两个核心监听器用于监控连接状态和处理错误:ConnectionListenerErrorListener

2.2.1 ConnectionListener - 连接状态监听器

代码示例

package org.example.natsdemo.Listener;

import io.nats.client.Connection;
import io.nats.client.ConnectionListener;
import lombok.extern.slf4j.Slf4j;
import java.util.Collection;

/**
 * 自定义NATS连接监听器
 *
 * <p>实现ConnectionListener接口,用于监听NATS连接的生命周期事件。
 * 注意:根据官方接口定义,只包含connectionEvent一个回调方法。</p>
 *
 * <p>事件类型说明:</p>
 * <ul>
 *   <li>CONNECTED - 首次成功建立连接</li>
 *   <li>DISCONNECTED - 连接意外断开</li>
 *   <li>RECONNECTED - 成功重新连接</li>
 *   <li>CLOSED - 连接永久关闭</li>
 *   <li>RESUBSCRIBED - 重新建立所有订阅</li>
 *   <li>DISCOVERED_SERVERS - 发现新的服务器节点</li>
 *   <li>LAME_DUCK - 服务器进入优雅关闭模式</li>
 * </ul>
 */
@Slf4j
public class CustomConnectionListener implements ConnectionListener {
    

    /**
     * 连接事件统一处理回调
     *
     * <p>所有连接状态变化都通过此方法通知,需根据事件类型进行不同处理</p>
     *
     * @param conn 当前连接对象
     * @param event 发生的事件类型(枚举值)
     */
    @Override
    public void connectionEvent(Connection conn, Events event) {
        // 获取连接标识信息
        String connName = conn.getOptions().getConnectionName();
        String server = conn.getConnectedUrl();

        // 根据事件类型执行不同处理逻辑
        switch (event) {
            case CONNECTED:
                onConnected(connName, server);
                break;
            case DISCONNECTED:
                onDisconnected(connName, server);
                break;
            case RECONNECTED:
                onReconnected(connName, server);
                break;
            case CLOSED:
                onClosed(connName);
                break;
            case RESUBSCRIBED:
                onResubscribed(connName);
                break;
            case DISCOVERED_SERVERS:
                onDiscoveredServers(conn, connName);
                break;
            case LAME_DUCK:
                onLameDuck(connName);
                break;
            default:
                log.warn("[{}] 收到未处理事件: {}", connName, event);
        }
        // 记录连接事件指标
        recordConnectionMetrics(event, connName);
    }

    //=== 事件处理私有方法 ===//
    private void onConnected(String connName, String server) {
        log.info("[{}] 成功连接到服务器: {}", connName, server);
        // 连接建立后恢复消息处理
    }

    private void onDisconnected(String connName, String server) {
        log.warn("[{}] 连接断开! 最后连接服务器: {}", connName, server);
        // 断开时暂停消息处理防止积压

        // 记录断开连接的服务器
    }

    private void onReconnected(String connName, String server) {
        log.info("[{}] 成功重连到服务器: {}", connName, server);
        // 重连后恢复处理并重发未确认消息

        // 更新活动服务器
    }

    private void onClosed(String connName){
        log.info("[{}] 连接已永久关闭", connName);
        // 释放连接相关资源
    }

    private void onResubscribed(String connName) {
        log.debug("[{}] 所有订阅已重新建立", connName);
        // 确保消息处理状态同步
    }

    private void onDiscoveredServers(Connection conn, String connName) {
        // 获取发现的服务器列表
        Collection<String> servers = conn.getServers();
        log.info("[{}] 发现 {} 个服务器节点: {}",
                connName, servers.size(), String.join(", ", servers));
        // 更新服务器集群信息
    }

    private void onLameDuck(String connName) {
        log.warn("[{}] 服务器进入维护模式(Lame Duck Mode)", connName);
        // 准备优雅停止消息处理
    }

    //=== 辅助方法 ===//
    private void recordConnectionMetrics(Events event, String connName) {
        // 实现指标记录逻辑
    }
}

2.2.2 ErrorListener - 错误事件监听器

用于处理连接中的错误和异常:

代码示例

package org.example.natsdemo.Listener;
import io.nats.client.ErrorListener;
import io.nats.client.*;
import lombok.extern.slf4j.Slf4j;
import io.nats.client.support.Status;

/**
 * NATS错误事件监听器实现
 * 处理NATS客户端运行过程中可能发生的各类错误和异常事件
 */
@Slf4j
public class CustomErrorListener implements ErrorListener {

    /**
     * 通用错误事件处理
     * @param conn 当前NATS连接对象
     * @param error 错误描述信息
     */
    @Override
    public void errorOccurred(Connection conn, String error) {
        log.info("[通用错误] 连接 {}: {}", conn.getServerInfo(), error);
        // 此处可添加:错误计数器、通知系统等逻辑
    }

    /**
     * 未捕获异常处理
     * @param conn 当前NATS连接对象
     * @param exp 发生的异常对象
     */
    @Override
    public void exceptionOccurred(Connection conn, Exception exp) {
        log.error("[未捕获异常] 连接 {}: {} - {}",
                conn.getServerInfo(), exp.getClass().getSimpleName(), exp.getMessage());
        // 此处可添加:异常恢复、重连机制等
    }

    /**
     * 慢消费者检测(消费者处理速度跟不上消息生产速度)
     * @param conn 当前NATS连接对象
     * @param consumer 相关的消费者对象
     */
    @Override
    public void slowConsumerDetected(Connection conn, Consumer consumer) {
        log.warn("[慢消费者] 连接 {}: 消费者类型 - {}",
                conn.getServerInfo(), consumer.getClass().getSimpleName());
        // 此处可添加:自动扩容消费者实例、优化处理逻辑等
    }

    /**
     * 消息丢弃事件(通常因队列满或超时导致)
     * @param conn 当前NATS连接对象
     * @param msg 被丢弃的消息对象
     */
    @Override
    public void messageDiscarded(Connection conn, Message msg) {
        log.warn("[消息丢弃] 连接 {}: 主题 '{}' | 消息大小: {} 字节",
                conn.getServerInfo(), msg.getSubject(), msg.getData().length);
        // 此处可添加:死信队列处理、丢弃消息统计等
    }

    /**
     * 心跳告警(JetStream心跳包未按时到达)
     * @param conn 当前NATS连接对象
     * @param sub 相关的JetStream订阅
     * @param lastStreamSequence 最后接收的流序列号
     * @param lastConsumerSequence 最后接收的消费者序列号
     */
    @Override
    public void heartbeatAlarm(Connection conn, JetStreamSubscription sub,
                               long lastStreamSequence, long lastConsumerSequence) {
        log.error("[心跳告警] 连接 {}: 订阅主题 '{}' | 流序列号: {} | 消费者序列号: {}",
                conn.getServerInfo(), sub.getSubject(), lastStreamSequence, lastConsumerSequence);
        // 此处可添加:连接健康检查、订阅重建等
    }

    /**
     * 未处理状态码(服务器返回未处理的状态)
     * @param conn 当前NATS连接对象
     * @param sub 相关的JetStream订阅
     * @param status 服务器返回的状态对象
     */
    @Override
    public void unhandledStatus(Connection conn, JetStreamSubscription sub, Status status) {
        log.warn("[未处理状态] 连接 {}: 状态码 {} - {}",
                conn.getServerInfo(), status.getCode(), status.getMessage());
        // 此处可添加:特定状态码的自定义处理
    }

    /**
     * Pull模式警告(非致命性问题)
     * @param conn 当前NATS连接对象
     * @param sub 相关的JetStream订阅
     * @param status 警告状态对象
     */
    @Override
    public void pullStatusWarning(Connection conn, JetStreamSubscription sub, Status status) {
        log.warn("[Pull警告] 连接 {}: 状态码 {} - {}",
                conn.getServerInfo(), status.getCode(), status.getMessage());
        // 此处可添加:警告级别监控告警
    }

    /**
     * Pull模式错误(致命性问题)
     * @param conn 当前NATS连接对象
     * @param sub 相关的JetStream订阅
     * @param status 错误状态对象
     */
    @Override
    public void pullStatusError(Connection conn, JetStreamSubscription sub, Status status) {
        log.error("[Pull错误] 连接 {}: 状态码 {} - {}",
                conn.getServerInfo(), status.getCode(), status.getMessage());
        // 此处可添加:错误恢复或订阅重置
    }

    /**
     * 流控处理完成(JetStream流量控制机制触发)
     * @param conn 当前NATS连接对象
     * @param sub 相关的JetStream订阅
     * @param subject 触发流控的主题
     * @param source 流控来源(HEARTBEAT或FLOW_CONTROL)
     */
    @Override
    public void flowControlProcessed(Connection conn, JetStreamSubscription sub,
                                     String subject, FlowControlSource source) {
        log.info("[流控处理] 连接 {}: 来源={} | 主题={}",
                conn.getServerInfo(), source.name(), subject);
        // 此处可添加:流量监控指标收集
    }
}
  1. 核心错误处理

    方法 触发场景 关键处理
    errorOccurred() 协议级错误 权限监控、安全告警
    exceptionOccurred() 系统级异常 熔断触发、网络监控
    slowConsumerDetected() 消息积压 自动扩容、流控调整
  2. JetStream 专用处理

    方法 触发场景 关键处理
    heartbeatAlarm() 心跳超时 消费者重启、心跳重置
    pullStatusWarning() 拉取警告 策略调整
    pullStatusError() 拉取错误 消费者重建
    flowControlProcessed() 流控生效 参数优化
  3. 消息生命周期

    方法 触发场景 关键处理
    messageDiscarded() 消息丢弃 死信队列处理
    unhandledStatus() 未知状态 协议监控

2.3 安全认证配置

Token 认证

Options options = new Options.Builder()
    .token("your-auth-token")
    .build();

用户名/密码认证

Options options = new Options.Builder()
    .userInfo("username", "password")
    .build();

TLS 加密

Options options = new Options.Builder()
    .sslContext(createCustomSslContext()) // 自定义 SSLContext
    .build();

2.4 自定义SSLContext

二、NATS 消息模型

2.1 发布/订阅 (Pub-Sub)

  1. 创建消息发布客户端

    @Slf4j
    @Component
    public class NatsPublisherClient {
    
        @Autowired
        private Connection natsConnection;
    
        public void publishMessage(String subject, String message) {
            natsConnection.publish(subject, message.getBytes());
            log.info("已发布消息到主题[{}]: {}", subject, message);
        }
    }
    
  2. 创建消息订阅服务

    @Slf4j
    @Service
    public class NatsSubscriberService {
    
        private final String subject = "subject.pubsub";
    
        @Autowired
        private Connection natsConnection;
    
    
        @PostConstruct
        public void subscribe() {
            Dispatcher dispatcher = natsConnection.createDispatcher(msg -> {});
            dispatcher.subscribe(subject, (msg) -> {
                String message = new String(msg.getData());
                log.info("接收到来自主题[{}]的消息: {}", subject, message);
                // 这里可以添加消息处理逻辑
            });
    
            log.info("已订阅主题: {}", subject);
        }
    }
    
  3. 创建测试控制器

    @RestController
    public class TestController {
    
        @Autowired
        NatsPublisherClient natsPublisherClient;
    
        /**
         * NATS消息模型-pub_sub
         */
        @PostMapping("/pubsub")
        public String pubSub(String msg) {
            natsPublisherClient.publishMessage("subject.pubsub", msg);
            return "ok";
        }
    }
    

2.2 请求/响应 (Request-Reply)

Request-Reply 是 NATS 提供的另一种通信模式,它允许客户端发送请求并等待响应,类似于 HTTP 的请求/响应模型,但基于消息系统实现。

Request-Reply 模型特点

  1. 同步通信:发送方会等待响应
  2. 临时主题:NATS 自动创建临时主题用于响应
  3. 超时机制:可以设置等待响应的超时时间
  4. 一对一通信:一个请求对应一个响应

  1. 添加 Reply 服务端

    @Slf4j
    @Service
    public class NatsReplyService {
    
        private String subject = "subject.request";
    
        private final Connection natsConnection;
    
        @Autowired
        public NatsReplyService(Connection natsConnection) {
            this.natsConnection = natsConnection;
        }
    
        @PostConstruct
        public void init() {
            Dispatcher dispatcher = natsConnection.createDispatcher(msg -> {
                String request = new String(msg.getData());
                String replySubject = msg.getReplyTo();
    
                if (replySubject != null) {
                    String response = "处理后的响应: " + request.toUpperCase();
                    natsConnection.publish(replySubject, response.getBytes());
                    log.info("已处理请求并返回响应 - 请求内容: {}, 响应内容: {}", request, response);
                }
            });
    
            dispatcher.subscribe(subject);
            log.info("Request-Reply 服务已启动,监听主题: {}", subject);
        }
    }
    
  2. 添加 Request 客户端

    @Slf4j
    @Component
    public class NatsRequestClient {
        @Autowired
        private Connection natsConnection;
    
    
        public String sendRequest(String subject, String requestData)throws InterruptedException {
            log.info("发送请求到主题[{}], 请求内容: {}", subject, requestData);
            // 发送请求并等待响应(设置5秒超时)
            Message response = natsConnection.request(subject, requestData.getBytes(), Duration.ofSeconds(5));
            if (response != null) {
                String responseData = new String(response.getData());
                log.info("接收到响应: {}", responseData);
                return responseData;
            } else {
                log.warn("请求超时,未收到响应");
                return "请求超时";
            }
        }
    
    }
    
  3. 创建测试控制器

    @RestController
    public class TestController {
    
        @Autowired
        NatsRequestClient natsRequestClient;
    
        /**
         * NATS消息模型-Request-Reply
         */
        @PostMapping("/requestReply")
        public String requestReply(String msg) throws InterruptedException {
            return natsRequestClient.sendRequest("subject.request", msg);
        }
    }
    

2.3 队列组 (Queue Groups)

队列组是 NATS 提供的一个强大功能,它允许多个订阅者组成一个组,共同消费同一个主题的消息,实现负载均衡和消息的分布式处理。

队列组核心概念

  1. 负载均衡:消息会自动在组内成员间分配
  2. 消息独占性:每条消息只会被组内的一个订阅者接收
  3. 高可用性:组内成员可以动态加入或离开
  4. 同组竞争:同一组的订阅者竞争消费消息
  5. 不同组独立:不同组的订阅者各自独立接收全部消息

队列组 vs 普通订阅

特性 普通订阅 队列组订阅
消息分发 所有订阅者都收到相同消息 每条消息只发给组内一个订阅者
负载均衡 自动负载均衡
订阅者关系 独立 属于同一个逻辑组
适用场景 广播通知 任务分发、工作队列

实现步骤

  1. 创建队列组消费者服务

    @Slf4j
    @Service
    public class NatsQueueGroupService {
    
        private static final String QUEUE_SUBJECT = "subject.queue";
        private static final String QUEUE_GROUP = "order-processors";
    
        @Autowired
        private Connection natsConnection;
    
    
        @PostConstruct
        public void init() {
            // 创建第一个队列消费者
            createQueueConsumer("消费者-1");
            // 创建第二个队列消费者
            createQueueConsumer("消费者-2");
        }
    
        private void createQueueConsumer(String consumerName) {
            Dispatcher dispatcher = natsConnection.createDispatcher(msg -> {
                String message = new String(msg.getData());
                log.info("[{}] 处理消息: {}", consumerName, message);
                // 模拟处理耗时
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
    
                log.info("[{}] 完成处理消息: {}", consumerName, message);
            });
            dispatcher.subscribe(QUEUE_SUBJECT, QUEUE_GROUP);
            log.info("[{}] 已加入队列组 {}, 监听主题 {}", consumerName, QUEUE_GROUP, QUEUE_SUBJECT);
        }
    }
    
  2. 消息发布客户端

    @Slf4j
    @Component
    public class NatsPublisherClient {
    
        @Autowired
        private Connection natsConnection;
    
        public void publishToQueue(String subject, String message) {
            natsConnection.publish(subject, message.getBytes());
            log.info("已发布消息到队列主题[{}]: {}", subject, message);
        }
    }
    
  3. 测试控制器

    @RestController
    public class TestController {
    
        @Autowired
        NatsPublisherClient natsPublisherClient;
    
        @PostMapping("/queue")
        public String sendToQueue(String msg) {
            natsPublisherClient.publishToQueue("subject.queue", msg);
            return "ok";
        }
    
    }
    

三,NATS核心组件

3.1 Message对象

在 Java 中,Message 对象是 io.nats.client.Message 类,主要包含以下内容:

public class Message {
    public String getSubject();      // 获取消息主题
    public String getReplyTo();     // 获取回复地址
    public byte[] getData();        // 获取原始字节数据
    public Headers getHeaders();    // 获取消息头(NATS 2.0+)
    public Subscription getSubscription(); // 获取关联的订阅
}

常用用法:

// 创建带头的消息
Headers headers = new Headers();
headers.add("Priority", "high");
headers.add("User", "alice","tom","jack");//消息头可以是数组

// 使用MessageBuilder构建消息
Message msg = NatsMessage.builder()
    .subject("orders.new")//必填
    .data("order content")//必填
    .headers(headers)//选填
    .replayTo("replay.Subject")// 指定回复主题,不建议手动指定。
    .build();
nc.publish(msg);



// 读取消息头
Dispatcher dispatcher = natsConnection.createDispatcher();
dispatcher.subscribe(subject, (msg) -> {
    Headers header = msg.getHeaders();
    List<String> header1 = header.get("User");//获取所有匹配的键值
    String header2 = header.getFirst("Priority");//获取第一个匹配的键值
    String header3 = header.getLast("User");//获取最后一个匹配的键值
    List<String> header4 = header.getIgnoreCase("User");//忽略大小写获取所有键值,忽略的是key的大小写
    byte[] serialized = header.getSerialized();//获取消息头的原始字节流表示
    String message = new String(msg.getData());
    log.info("接收到来自主题[{}]的消息: {}", subject, message);  
});

3.2 Subscription

Subscription(订阅)是 NATS 客户端中接收消息的核心机制,在 jnats (Java NATS 客户端) 中有多种订阅类型和使用方式。

Subscription 类型

  1. 同步订阅 (SyncSubscription)

    // 创建同步订阅
    Subscription sub = nc.subscribe("updates");
    
    // 接收消息(阻塞式)
    Message msg = sub.nextMessage(Duration.ofSeconds(5));
    
    // 接收消息(无限等待)
    Message msg = sub.nextMessage(Duration.ZERO); // 或 sub.nextMessage(null)
    
  2. 异步订阅 (通过Dispatcher)

    // 创建Dispatcher订阅
    Dispatcher dispatcher = nc.createDispatcher((msg) -> {
        System.out.println("收到消息: " + new String(msg.getData()));
    });
    
    // 添加订阅到Dispatcher
    dispatcher.subscribe("updates");
    
  3. 队列订阅

    // 同步队列订阅
    Subscription sub = nc.subscribe("tasks", "worker-pool");
    
    // 异步队列订阅
    dispatcher.subscribe("tasks", "worker-pool");
    

Subscription 核心方法

  1. 消息接收

    // 同步接收(带超时)
    Message msg = sub.nextMessage(Duration.ofSeconds(1));
    
    // 检查是否有待处理消息
    long pending = sub.getPendingMessageCount();
    if (pending > 0) {
        Message msg = sub.nextMessage(Duration.ZERO);
    }
    
  2. 订阅管理

    // 取消订阅
    sub.unsubscribe();
    
    // 带最大消息数的自动取消
    Subscription sub = nc.subscribe("updates");
    sub.unsubscribe(100); // 收到100条消息后自动取消
    
    // 检查订阅是否有效
    if (sub.isActive()) {
        // 订阅仍然活跃
    }
    
  3. 流量控制

    // 设置待处理消息限制
    sub.setPendingLimits(1000, 10 * 1024 * 1024); // 1000条或10MB
    
    // 获取当前待处理消息状态
    long pendingMessageCount = sub.getPendingMessageCount();//当前待处理的消息数量
    long pendingMessageLimit = sub.getPendingMessageLimit();//订阅允许的最大待处理消息数
    long pendingByteCount = sub.getPendingByteCount();//当前待处理消息的总字节数   
    long pendingByteLimit = sub.getPendingByteLimit();//订阅允许的最大待处理字节数
    
  4. 订阅监控

    // 获取订阅统计信息
    long droppedCount = subscribe.getDroppedCount();//已丢弃的消息数
    long deliveredCount = subscribe.getDeliveredCount();//已处理的消息数
    // 获取关联的主题
    String subject = sub.getSubject();
    // 获取队列组名称(如果有)
    String queue = sub.getQueueName();
    

3.3 MessageHandler

MessageHandler 是 jnats (Java NATS 客户端) 中用于异步处理消息的核心接口,它定义了如何处理接收到的 NATS 消息。

基本概念

MessageHandler 是一个函数式接口,只有一个抽象方法:

@FunctionalInterface
public interface MessageHandler {
    void onMessage(Message msg) throws InterruptedException;
}

基本用法

  1. 使用 Lambda 表达式

    // 最简单的形式
    MessageHandler handler = (msg) -> {
        System.out.println("收到消息: " + new String(msg.getData()));
    };
    
    // 注册到Dispatcher
    Dispatcher dispatcher = nc.createDispatcher(handler);
    dispatcher.subscribe("updates");
    
  2. 使用方法引用

public class MessageProcessor {
    public void process(Message msg) {
        System.out.println("处理消息: " + new String(msg.getData()));
    }
}

MessageProcessor processor = new MessageProcessor();
Dispatcher dispatcher = nc.createDispatcher(processor::process);
dispatcher.subscribe("orders");
  1. 使用匿名类

    Dispatcher dispatcher = nc.createDispatcher(new MessageHandler() {
        @Override
        public void onMessage(Message msg) throws InterruptedException {
            System.out.println("主题: " + msg.getSubject());
            System.out.println("内容: " + new String(msg.getData()));
        }
    });
    dispatcher.subscribe("logs");
    

3.3 Dispatcher

Dispatcher 是 jnats (Java NATS 客户端) 中用于异步消息处理的核心组件,它允许高效地处理多个订阅的消息而无需手动管理线程。

Dispatcher 的主要特点:

  • 为异步消息处理提供统一的消息分发机制
  • 可以管理多个订阅
  • 自动处理消息回调的线程管理
  • 支持流量控制(节流)

创建和使用 Dispatcher

  1. 创建和使用 Dispatcher

    Connection nc = Nats.connect("nats://localhost:4222");
    
    // 创建 Dispatcher 并定义消息处理器
    Dispatcher dispatcher = nc.createDispatcher((msg) -> {
        System.out.println("收到消息 [" + msg.getSubject() + "] : " + new String(msg.getData()));
    });
    
    // 添加订阅
    dispatcher.subscribe("updates");
    dispatcher.subscribe("notifications");
    
    // 也可以取消订阅
    dispatcher.unsubscribe("updates");
    
  2. 带队列组的订阅

    Dispatcher dispatcher = nc.createDispatcher((msg) -> {
        System.out.println("Worker 处理: " + new String(msg.getData()));
    });
    
    // 加入队列组实现负载均衡
    dispatcher.subscribe("tasks", "worker-pool");
    
  3. 使用多个 Dispatcher

    // 创建专门处理订单的 Dispatcher
    Dispatcher orderDispatcher = nc.createDispatcher((msg) -> {
        // 处理订单逻辑
    });
    
    // 创建专门处理日志的 Dispatcher
    Dispatcher logDispatcher = nc.createDispatcher((msg) -> {
        // 处理日志逻辑
    });
    
    orderDispatcher.subscribe("orders.*");
    logDispatcher.subscribe("logs.*");
    

Dispatcher 高级功能

  1. 流量控制(节流)

    Options options = new Options.Builder()
        .server("nats://localhost:4222")
        .connectionListener((conn, type) -> System.out.println("连接状态: " + type))
        .build();
    
    Connection nc = Nats.connect(options);
    
    // 创建带节流的 Dispatcher (每秒最多处理100条消息)
    Dispatcher throttledDispatcher = nc.createDispatcher((msg) -> {
        // 处理消息
        System.out.println("处理消息: " + new String(msg.getData()));
    });
    
    // 设置节流参数 (消息数/时间间隔)
    throttledDispatcher.setPendingLimits(100, 1000); // 100条/秒
    throttledDispatcher.subscribe("high.volume");
    
  2. 错误处理

Dispatcher dispatcher = nc.createDispatcher(new MessageHandler() {
    @Override
    public void onMessage(Message msg) throws InterruptedException {
        try {
            // 处理消息
            processMessage(msg);
        } catch (Exception e) {
            System.err.println("处理消息失败: " + e.getMessage());
            // 可以选择重试或记录错误
        }
    }
});

dispatcher.subscribe("sensitive.data");

Dispatcher 生命周期管理

  1. 关闭 Dispatcher
// 关闭特定 Dispatcher 的所有订阅
dispatcher.unsubscribeAll();

// 或者通过关闭连接来关闭所有 Dispatcher
nc.close();
  1. 状态检测

    // 检查 Dispatcher 是否活跃
    if (!dispatcher.isActive()) {
        System.out.println("Dispatcher 已关闭");
    }
    
    // 获取 Dispatcher 管理的订阅数
    int subCount = dispatcher.getSubscriptionCount();
    
posted @ 2025-07-12 20:26  wdadwa  阅读(375)  评论(0)    收藏  举报