Featured image of post Nacos 配置中心与服务发现的实战要点

Nacos 配置中心与服务发现的实战要点

Nacos 装起来容易,用对很难。本文从命名空间、配置组、灰度、监听、服务发现到生产部署,一篇讲透实战要点

为什么 Nacos 装起来容易,用对很难

Nacos 是当下国内最流行的"配置中心 + 注册中心"二合一组件——几乎所有用 Spring Cloud Alibaba 的项目都默认上它。

但用过的人会发现一些尴尬的事情:

  • 团队所有项目的配置全堆在一个 namespace 里,环境/项目/团队划分混乱
  • 一个微服务有十几个配置文件,谁也说不清哪个配置覆盖了哪个
  • 修改一个配置后有些实例生效,有些没生效——监听机制写错了
  • 重启 Nacos 后配置没了或者注册的服务全没了——存储模式选错了
  • 灰度发布想"只让一台实例生效新配置",根本做不到

Nacos 装起来 5 分钟,用对要几个月经验。本文把生产环境的关键决策点讲清楚——namespace 怎么划、配置怎么组织、监听怎么写对、服务发现怎么用稳


一、Nacos 的核心模型

三层模型:

  • Namespace:最高层级隔离——通常对应环境(dev/test/prod)
  • Group:第二层逻辑分组——通常对应项目微服务
  • Data ID:最具体的配置文件——application-{profile}.yaml

这个三层结构怎么划分,是 Nacos 用得好不好的最大决定因素。


二、Namespace / Group / DataId 的划分原则

不要这样划

1
2
3
4
namespace: public(默认)
group: DEFAULT_GROUP
data id: my-service-dev.yaml, my-service-test.yaml, my-service-prod.yaml
my-other-service-dev.yaml, ...

所有配置堆在 default 命名空间和 default 组里——搜起来一团乱,多团队协作时互相覆盖也不会知道。

推荐划法:环境隔离 + 服务分组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
namespace = 环境
   ├─ dev
   ├─ test
   ├─ pre   (预发布)
   └─ prod

group = 服务名 / 项目名
   ├─ user-service
   ├─ order-service
   └─ shared-config   (跨服务共享)

data id = application-{type}.yaml
   ├─ application-base.yaml    (业务基础配置)
   ├─ application-rocketmq.yaml (中间件配置)
   └─ application-feature.yaml  (功能开关)

关键决策:

  1. Namespace 用环境——上线管控的核心边界。线上账号只给 prod namespace 权限,dev 不该看到 prod
  2. Group 用服务名——同一个微服务的所有 Data ID 集中在一个 Group 里
  3. Data ID 按职责拆分——业务配置、中间件配置、功能开关分开放,单个文件不会膨胀

跨服务共享配置怎么办

很多配置不是单一服务的——比如所有服务都要连同一个 Redis:

1
2
group = shared-config
data id = application-redis.yaml, application-mq.yaml

每个服务在 bootstrap.yml 里多 import 一份共享配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
spring:
  cloud:
    nacos:
      config:
        server-addr: ${NACOS_ADDR}
        namespace: ${ENV}
        group: user-service                # 自己的 group
        extension-configs:
          - data-id: application-redis.yaml
            group: shared-config
            refresh: true
          - data-id: application-mq.yaml
            group: shared-config
            refresh: true

三、配置文件的优先级和加载顺序

Spring Cloud Nacos 默认按这个优先级加载(从低到高):

  1. ${spring.application.name}.${file-extension} 默认共享
  2. ${spring.application.name}-${profile}.${file-extension}(profile 独占)
  3. extension-configs(按数组顺序,越后越高)
  4. shared-configs(按数组顺序,越后越高)
  5. 命令行参数 / 环境变量

搞清楚这个优先级是排查"为什么我改了配置不生效"的前提——很可能你改的是低优先级,被高优先级覆盖了。

打开 Spring Boot Actuator 的 /actuator/configprops 或者 /actuator/env 能直观看到每个 key 来自哪个 PropertySource。


四、配置变更的监听:写错就完蛋

默认行为:自动刷新被 @Value 注入的字段

1
2
3
4
5
spring:
  cloud:
    nacos:
      config:
        refresh: true
1
2
3
4
5
6
@RestController
@RefreshScope                  // ★ 关键
public class FeatureController {
    @Value("${feature.enabled}")
    private boolean enabled;
}

@RefreshScope 让 Bean 在配置变更后重新创建,注入的 @Value 拿到新值。

容易踩的坑

坑 1:@RefreshScope 没加,配置不生效

新人最常踩——以为配 refresh: true 就够了。实际上还要在使用配置的 Bean 上加 @RefreshScope

坑 2:@RefreshScope 不能加在 @Configuration

会导致整个 Configuration 重建,下面声明的所有 Bean 全部重建——副作用极大,可能引起内存泄漏(旧 Bean 没释放)。

坑 3:@ConfigurationProperties 默认就刷新

不需要 @RefreshScope

1
2
3
4
5
6
7
@Component
@ConfigurationProperties(prefix = "feature")
public class FeatureConfig {
    private boolean enabled;
    private int threshold;
    // getters / setters
}

这是更推荐的姿势——比 @Value 类型安全、便于测试、自动刷新。

坑 4:手动监听用 @NacosConfigListener 已废弃

老博客经常推荐 @NacosConfigListener,但它属于早期 SDK 风格,Spring Cloud 集成下基本不该用。手动监听用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Autowired NacosConfigManager configManager;

@PostConstruct
public void listen() throws Exception {
    configManager.getConfigService().addListener(
        "application-feature.yaml", "user-service",
        new Listener() {
            public Executor getExecutor() { return null; }
            public void receiveConfigInfo(String content) {
                log.info("config changed: {}", content);
                // 自定义解析逻辑
            }
        });
}

但 99% 场景用 @ConfigurationProperties + @RefreshScope 就够了。


五、灰度发布:Nacos 自身能做到什么程度

很多人以为"灰度发布配置"是 Nacos 的标准能力——Nacos 开源版其实没有灰度配置

  • Nacos 开源版:配置变更会推到所有订阅了这个 Data ID 的实例,没有"只推一台"的能力
  • Nacos 商业版(阿里云 MSE Nacos):才有"配置灰度发布"功能,可以指定按 IP / 标签推送

如果用开源版又想做配置灰度,三种思路:

  1. 拆 namespace:灰度环境用单独的 namespace(最干净)
  2. 拆 group:同 namespace 不同 group,灰度实例订阅 xxx-gray group
  3. 配置里写规则:配置内容里写"哪些实例生效",业务代码读自己 IP 来判断(最灵活但侵入大)

生产推荐方案 1:直接搞一个 pre namespace 给灰度环境,和 prod 完全隔离。


六、服务发现:被低估的复杂度

临时实例 vs 永久实例

Nacos 注册的服务有两种"实例类型":

  • 临时实例(默认):客户端用心跳维持,断了 30 秒后自动剔除——适合业务微服务
  • 永久实例:服务端永久持有,下线要主动调 API——适合有固定地址的传统服务(数据库、第三方系统)
1
2
3
4
5
spring:
  cloud:
    nacos:
      discovery:
        ephemeral: true    # 临时实例

注意:1.x 时代两者可以混在同一服务,2.x 之后同一 service 必须全是临时或全是永久——升级时要核对。

心跳与剔除

版本差异(重要):下面"5 秒心跳 / 15 秒不健康 / 30 秒剔除"是 Nacos 1.x(HTTP 短连接) 的行为;Nacos 2.x 起默认改为 gRPC 长连接 + keepalive——客户端不再定时发 HTTP 心跳,连接断开即被识别下线,整体响应时延显著缩短(亚秒级)。如果你用的是 2.x(2024 年起的主流版本),心跳模型已经不适用。

Nacos 1.x 行为:临时实例每 5 秒发心跳,15 秒没心跳变"不健康",30 秒后剔除——这意味着实例突然 kill 后最长有 30 秒不可用窗口。

Nacos 2.x 行为:客户端与 server 维持 gRPC 长连接,server 通过连接状态判断实例存活;连接异常断开(进程崩溃、网络隔离)会立即触发下线事件,避免 30 秒窗口。

解决(不论版本都建议):

  • 客户端配置健康检查 + 重试(OpenFeign / Ribbon / Spring Cloud LoadBalancer)
  • 实例下线前先调用 Nacos NamingService.deregisterInstance 优雅注销——通常用 Spring Boot 的 ApplicationListener<ContextClosedEvent>

七、生产部署:踩过的坑

1. 数据存储:默认嵌入式不能上生产

Nacos 单机默认用嵌入式 Derby 数据库——重启数据可能丢,集群模式根本无法工作。生产必须切到 MySQL:

1
2
3
4
5
6
7
# Nacos 1.x / 2.x 老 key(仍兼容)
spring.datasource.platform=mysql
# Nacos 2.2+ 自身基于较新 Spring Boot,server 端推荐改用:spring.sql.init.platform=mysql
db.num=1
db.url.0=jdbc:mysql://nacos-mysql:3306/nacos?characterEncoding=utf8&...
db.user=nacos
db.password=...

2. 集群模式:至少 3 节点

Nacos 是双协议架构——配置中心和永久实例走 JRaft(CP),默认服务发现的临时实例走自研的 Distro(AP)协议 。两类数据落到同一个集群、用不同的一致性模型。至少 3 节点保证高可用——单节点是写测试代码用的,生产部署单节点没有任何高可用保证,强烈不建议。

这个差异在调试时很重要:Distro 是 AP,节点挂掉时部分实例信息可能短暂不一致——这是设计如此,不是 bug。

3. 安全:默认是裸奔的

Nacos 默认没有鉴权——任何能访问端口的人都能改你的配置。生产必须开启:

1
2
3
nacos.core.auth.enabled=true
nacos.core.auth.server.identity.key=...
nacos.core.auth.server.identity.value=...

并给不同环境账号、不同 namespace 设置不同权限(开源版控制台里能配)。

4. JVM 参数

生产 Nacos 的 JVM 参数默认是 -Xms2g -Xmx2g对大流量场景明显不够。配置数 ≥ 10000、连接客户端 ≥ 1000 时建议升到 4G 以上。

5. 不要在容器里跑临时数据

K8s 部署 Nacos 时,所有数据必须挂 PV,否则 Pod 重启数据全丢。MySQL 走外部,但 Nacos 自己的 logs / data 目录也要挂。


八、客户端容错:Nacos 挂了应用还能跑吗

Nacos 故障时(虽然概率小),客户端默认还能从本地 snapshot 文件读取上次拉到的配置——不会立刻挂

1
~/nacos/config/snapshot/{namespace}/{group}/{dataId}

但有几个细节:

  • 新增实例在 Nacos 故障时拉不到任何配置——直接启动失败
  • 服务发现故障时正在运行的实例还能用上次缓存的服务列表,但新启动的实例发现不了任何服务

所以生产环境永远要保证:

  • Nacos 集群必须多节点 + 跨可用区
  • 应用启动时用本地 fallback 配置(spring.cloud.nacos.config.import-check.enabled=false)
  • 重要服务的服务发现要有降级机制

九、监控与排查

1. 看配置历史

Nacos 控制台保留每次变更的历史,改错了能直接 rollback。这是 Nacos 区别于"配置写在 yaml 里 + git" 模式的核心价值——不需要发版本就能改配置,但同时有审计

2. 看订阅者

Nacos 控制台 → 配置详情 → “订阅者列表”,能看到当前哪些实例订阅了这个配置——调试"为什么我的应用没收到推送"的第一步

3. 应用侧日志

1
DEBUG  com.alibaba.nacos.client = DEBUG

打开后能看到拉配置、心跳、推送等所有交互的细节。


十、生产配置 Checklist

部署前对照这份清单:

  • Namespace 按环境划分
  • Group 按服务划分
  • Data ID 按职责拆分(业务/中间件/开关)
  • 共享配置走 shared-configs
  • 使用 @ConfigurationProperties 而非 @Value
  • @RefreshScope 不加在 @Configuration
  • 数据存储改 MySQL(不要嵌入式 Derby)
  • 集群模式 ≥ 3 节点
  • 鉴权开启 + 多账号权限隔离
  • JVM 内存 ≥ 4G
  • K8s 部署挂 PV
  • 应用启动有 fallback 配置
  • Actuator 监控接入
  • 业务配置变更走审批流程

小结

把全文压一句:

Nacos 的真正价值不在『配置存哪里』,而在『配置变更能可控、可审计、能秒级生效』——但要让它真正可靠,命名空间、监听机制、集群部署一个都不能省。

记住几条工程纪律:

  • 环境分 namespace,服务分 group,配置按职责拆 Data ID
  • 优先 @ConfigurationProperties,少用 @Value + @RefreshScope
  • 生产必上 MySQL + 3 节点 + 鉴权
  • 永远要有本地 fallback——配置中心是单点故障源

把 Nacos 用对,是国内 Spring Cloud 项目走得远的基础设施之一。

使用 Hugo 构建
主题 StackJimmy 设计