Nacos生产环境高可用与最佳实践

大家好,我是joker,希望你快乐。

上一篇Nacos2.x核心原理分析中从源码层面分析了 Nacos 的工作机制,这篇我们聚焦生产环境,看看 Nacos 在实际落地中会遇到哪些问题,以及如何构建一个高可用、安全、可运维的 Nacos 体系。

集群部署与高可用

单节点 Nacos 存在单点故障风险,生产环境必须集群部署。

集群架构

                    +-----------+
                    |   Nginx   |
                    |    SLB    |  <-- 负载均衡
                    +-----------+
                   /      |      \
          +--------+ +--------+ +--------+
          | Nacos1 | | Nacos2 | | Nacos3 |  <-- 至少3节点
          +--------+ +--------+ +--------+
               \        |        /
                +-------+-------+
                |     MySQL     |  <-- 主从或集群
                +---------------+

集群配置要点

  1. conf/cluster.conf 配置所有节点
192.168.1.101:8848
192.168.1.102:8848
192.168.1.103:8848
  1. conf/application.properties 配置 MySQL 持久化
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://192.168.1.100:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=nacos
db.password.0=nacos_password
  1. Nginx 负载均衡配置
upstream nacos_cluster {
    server 192.168.1.101:8848;
    server 192.168.1.102:8848;
    server 192.168.1.103:8848;
}

server {
    listen 8848;
    location / {
        proxy_pass http://nacos_cluster;
    }
}
  1. 启动集群
sh startup.sh -m cluster

MySQL 高可用

Nacos 集群的数据一致性依赖 MySQL,MySQL 本身的高可用同样关键:

  • 主从复制:读写分离,主库故障时切换到从库
  • 分片集群:大规模数据场景,按行或列分片
  • 多主多从:高可用与负载均衡兼顾

生产环境建议 MySQL 至少主从部署,定期全量+增量备份。

端口规划

Nacos 2.x 需要开放以下端口:

端口 用途
8848 HTTP API / Web 控制台
9848 gRPC 客户端通信(8848 + 1000)
9849 gRPC 集群内部通信(8848 + 1001)
7848 Raft 协议通信(8848 - 1000)

服务治理进阶

保护阈值

当服务健康实例比例低于保护阈值时,Nacos 会将所有实例(包括不健康的)返回给消费者,避免流量全部涌向少量健康实例导致雪崩。

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: prod
        cluster-name: BJ
        weight: 1.0

在 Nacos 控制台的服务详情页面可以设置保护阈值,建议值 0.3-0.5。

保护阈值是一种宁可返回不健康的实例,也不让所有请求失败的兜底策略。消费者拿到不健康的实例后,可能会调用失败,但至少有重试的机会,而不是直接无实例可用。

同集群优先调用

Nacos 支持按集群(Cluster)分组服务实例,消费者优先调用同集群的实例,降低网络延迟。

  1. 服务提供者指定集群名
spring:
  cloud:
    nacos:
      discovery:
        cluster-name: BJ
  1. 服务消费者也指定集群名,调用时优先匹配同集群实例

  2. 同集群无可用实例时,才会调用其他集群的实例

这种策略在多机房部署场景下非常实用:北京机房的消费者优先调用北京机房的实例,只有北京机房全部不可用时才跨机房调用。

权重路由

Nacos 支持为实例设置权重,权重越高被选中的概率越大。常见应用场景:

  • 灰度发布:新版本实例设置低权重,逐步提高
  • 流量迁移:旧集群权重逐步降低,新集群逐步提高
  • 性能差异化:高配机器设置高权重,低配机器设置低权重

配置中心进阶

多环境隔离

Nacos 通过 Namespace + Group + Data ID 三级结构实现配置隔离:

维度 作用 示例
Namespace 环境隔离 dev / test / prod
Group 业务分组 ORDER_GROUP / USER_GROUP
Data ID 配置文件 order-service-prod.yaml

在 Nacos 控制台创建 Namespace 时会生成一个 ID,客户端配置时必须使用这个 ID 而非名称:

spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        namespace: a1b2c3d4-5678-90ab-cdef-1234567890ab
        group: ORDER_GROUP
        file-extension: yaml

配置版本管理与回滚

Nacos 自动保存配置的历史版本,包括修改人、修改时间和变更内容。在控制台的"历史版本"页面可以:

  • 查看配置变更记录
  • 对比不同版本的差异
  • 一键回滚到任意历史版本

建议在每次重要配置变更前,记录当前配置的关键参数,方便回滚时验证。

共享配置与扩展配置

当多个服务有相同的配置(如数据库连接、Redis 地址)时,可以使用共享配置避免重复维护:

spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        shared-configs:
          - data-id: common-db.yaml
            group: SHARED_GROUP
            refresh: true
          - data-id: common-redis.yaml
            group: SHARED_GROUP
            refresh: true
        extension-configs:
          - data-id: custom-config.yaml
            group: APP_GROUP
            refresh: true

配置加载优先级(从高到低):

  1. 带 profile 的配置:${spring.application.name}-${profile}.${file-extension}
  2. 不带 profile 的配置:${spring.application.name}.${file-extension}
  3. extension-configs(按数组顺序,后者优先级更高)
  4. shared-configs(按数组顺序,后者优先级更高)

权限控制

生产环境必须开启 Nacos 的鉴权,防止未授权访问。

开启鉴权

application.properties 中配置,生产环境不能使用默认值:

nacos.core.auth.enabled=true
nacos.core.auth.plugin.nacos.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789
nacos.core.auth.server.identity.key=serverIdentity
nacos.core.auth.server.identity.value=security

RBAC 权限模型

Nacos 提供用户、角色、权限三级授权:

  • 用户:登录控制台的账号
  • 角色:权限的集合
  • 权限:对某个 Namespace 下资源的读写权限

建议按环境分配权限:开发人员只能访问 dev 命名空间,运维人员才能访问 prod 命名空间。

监控与运维

关键监控指标

指标 说明 告警阈值建议
注册实例数 各服务的实例数量 突降超过 30%
配置变更频率 单位时间内配置变更次数 短时间大量变更
gRPC 连接数 当前活跃的客户端连接 接近最大连接数
JVM 堆内存 Nacos 进程的堆内存使用率 > 80%
MySQL 连接池 数据库连接池使用率 > 80%
Raft Leader 状态 当前 Leader 是否存在 Leader 丢失

Prometheus + Grafana 监控

Nacos 2.x 暴露了 Prometheus 指标端点,在 application.properties 中开启:

management.endpoints.web.exposure.include=*

配合 Grafana 仪表盘可以实时监控 Nacos 的运行状态。

日志管理

Nacos 的日志文件位于 logs/ 目录下:

日志文件 内容
nacos.log 主日志
config.log 配置中心日志
naming.log 注册中心日志
remote.log gRPC 通信日志

生产环境建议开启日志轮转,定期清理过期日志,或使用 ELK Stack 集中收集分析。

常见问题与排查

服务注册失败

排查步骤:

  1. 检查 Nacos Server 是否正常运行:curl http://localhost:8848/nacos/v1/ns/service/list?pageNo=1&pageSize=10
  2. 检查网络连通性和端口:8848(HTTP)、9848(gRPC)
  3. 检查客户端配置:server-addrnamespace 是否正确
  4. 检查依赖版本:Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者版本是否兼容

配置未生效

排查步骤:

  1. 检查 Data ID 是否正确:默认规则为 ${spring.application.name}.${file-extension}
  2. 检查 Namespace 是否使用了 ID 而非名称
  3. 检查是否需要 @RefreshScope 注解
  4. 检查 bootstrap.yml 是否生效:Spring Boot 2.4+ 需要引入 spring-cloud-starter-bootstrap
  5. 检查配置优先级:profile 配置 > 默认配置 > shared-configs

集群节点无法加入

排查步骤:

  1. 检查 cluster.conf 中 IP 是否正确,不能使用 127.0.0.1,必须使用实际 IP
  2. 检查 7848、8848、9848、9849 端口是否全部开放
  3. 检查所有节点的 MySQL 配置是否一致
  4. 检查节点时间是否同步(Raft 协议依赖时间)

@RefreshScope 与 @Scheduled 冲突

当一个 Bean 同时使用 @RefreshScope@Scheduled 时,配置刷新后定时任务会失效。这里提供两种解决方案。

方案一:配置类分离(推荐)

将动态配置抽取到单独的 @RefreshScope Bean 中,定时任务类不再加 @RefreshScope,通过注入配置类间接获取动态值。

@Component
@RefreshScope
public class AppConfig {

    @Value("${app.config.value}")
    private String configValue;

    public String getConfigValue() {
        return configValue;
    }
}
@Component
public class ScheduleTask {

    @Autowired
    private AppConfig appConfig;  // 注入的是代理,每次调用都拿最新值

    @Scheduled(cron = "*/5 * * * * ?")
    public void execute() {
        System.out.println("定时任务执行,配置值:" + appConfig.getConfigValue());
    }
}

这种方式的优点是结构清晰,定时任务类不会被销毁重建,任务调度不中断;@RefreshScope 只作用于配置类,刷新时只影响配置值的读取,不影响任务注册关系。

方案二:实现 ApplicationListener<RefreshScopeRefreshedEvent> 接口

如果确实需要在同一个 Bean 中同时使用 @RefreshScope@Scheduled,可以让该 Bean 实现 ApplicationListener<RefreshScopeRefreshedEvent> 接口:

@Component
@RefreshScope
public class RefreshScopeTask implements ApplicationListener<RefreshScopeRefreshedEvent> {

    @Value("${app.config.value}")
    private String configValue;

    @Scheduled(cron = "*/5 * * * * ?")
    public void execute() {
        System.out.println("定时任务执行,配置值:" + configValue);
    }

    @Override
    public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
        // 方法体可以为空
    }
}

关键在于 implements ApplicationListener<RefreshScopeRefreshedEvent> 这个接口声明本身。配置刷新完成后,Spring 会发布 RefreshScopeRefreshedEvent 事件,并调用监听器的 onApplicationEvent 方法。由于该 Bean 是 @RefreshScope,Spring 从容器中拿到的是代理对象,调用代理方法会触发懒加载重建真实实例,新实例在初始化过程中 @Scheduled 任务会被重新注册。

参考文档

Nacos 官方文档

Nacos 集群部署说明

Nacos 权限控制

StackOverflow: @RefreshScope stops @Scheduled task

posted @ 2026-07-30 08:28  Crazy_Joker  阅读(16)  评论(0)    收藏  举报