Skip to content

Nacos 注册发现与配置中心

Nacos 是 Spring Cloud Alibaba 体系里常用的注册中心和配置中心。它同时解决两个核心问题:

  1. 服务怎么找到彼此:服务实例会扩容、缩容、重启、迁移,调用方不能写死 IP。
  2. 配置怎么集中管理:不同环境、不同服务、不同租户的配置需要统一发布、审计、回滚和动态生效。

零基础可以先记住一句话:

Nacos Discovery 负责“服务名到实例列表”,Nacos Config 负责“配置文件到应用 Environment”。

阅读入口:先建立两条主线

按“注册发现 → 配置加载 → 运行期变更 → 故障排查”阅读。先看原理图,再看基础配置和完整流程,最后进入内部原理与生产治理

核心原理:注册发现与配置分成两条数据流

Discovery 维护实例元数据并通过心跳、订阅和本地缓存传播;Config 按 dataId/group/namespace 管理配置版本,通过长轮询或推送通知客户端更新。两条数据流可以使用同一个 Nacos 集群,但存储、权限和故障影响不同。

mermaid
flowchart TD
    A["服务启动"] --> B["Discovery注册实例"]
    B --> C["心跳/健康检查"]
    D["应用启动"] --> E["Config加载dataId"]
    E --> F["PropertySource进入Environment"]
    F --> G["监听变更并刷新"]
    C --> H["消费者订阅实例快照"]

本页负责零基础概念、常用配置和基础排查。Nacos 1.x/2.x通信差异、gRPC连接、注册重做、服务订阅推送、ServiceInfoHolder、Distro、JRaft、Config MD5监听、快照降级、端口规划和生产故障恢复请继续阅读:

学习目标

学完本页你要能回答:

  1. Nacos 的注册中心和配置中心分别解决什么问题。
  2. Namespace、Group、Service、Cluster、Instance、DataId 分别是什么。
  3. 服务注册、心跳、健康检查、订阅推送、本地缓存的完整过程。
  4. 临时实例和持久实例有什么区别,为什么它们对应不同健康检查方式。
  5. Nacos 为什么既要本地缓存,又要服务端推送。
  6. Nacos Config 启动拉取和运行期刷新怎么工作。
  7. 配置动态刷新适合什么,不适合什么。
  8. Nacos 和 Eureka、Spring Cloud Config、Apollo 怎么比较。
  9. 服务找不到、配置不生效、改配置不刷新怎么排查。
  10. 商业项目里配置发布、权限、审计、回滚、灰度应该怎么做。

Nacos 的核心模型

mermaid
flowchart TD
    A["Nacos"] --> B["Naming<br/>注册发现"]
    B --> C["Service、Instance、Cluster"]
    C --> D["Config<br/>配置中心"]
    D --> E["Namespace、Group、DataId"]

图中为了在手机和窄屏上保持清晰,按“注册发现模型 -> 配置模型”纵向展示;Naming 与 Config 实际上是 Nacos 提供的两组并列能力,不是先后调用关系。各字段的准确职责以紧随其后的表格为准。

概念属于作用示例
Namespace注册和配置都可用环境或租户隔离devtestprod
Group注册和配置都可用分组隔离DEFAULT_GROUPMEDICAL_GROUP
Service注册发现服务名asset-service
Cluster注册发现实例分组、机房、区域shanghaibeijing
Instance注册发现一个运行中的服务实例10.1.2.11:8080
DataId配置中心一份配置的标识asset-service-prod.yml

常见误区:

误区正确理解
Namespace 只是页面分组Namespace 会影响服务发现和配置查找,不同 Namespace 默认互相看不见
Group 可以随便写服务提供者和消费者 Group 不一致,可能互相发现不到
DataId 就是文件名DataId 是 Nacos 配置定位的一部分,和应用名、环境、后缀强相关
Nacos 转发业务请求Nacos 只提供注册表和配置,不转发业务 HTTP 请求

为什么需要注册发现

没有注册中心时,调用方要写死服务地址:

text
http://10.1.2.11:8080/api/assets

这在微服务里会带来问题:

  1. 服务扩容后,调用方不知道新实例。
  2. 服务宕机后,调用方还会打到坏实例。
  3. 容器重启后 IP 变化,配置要改。
  4. 多环境、多机房、多版本路由难维护。
  5. 负载均衡、灰度、权重都需要额外实现。

有 Nacos 后,调用方只关心服务名:

text
lb://asset-service

底层流程是:

mermaid
flowchart TD
    A["asset-service 实例启动"] --> B["注册服务名、IP、端口、元数据"]
    B --> C["Nacos 保存实例列表"]
    C --> D["调用方订阅 asset-service"]
    D --> E["实例列表进入本地缓存"]
    E --> F["LoadBalancer 选择实例"]
    F --> G["HTTP 调用真实地址"]

前三步发生在提供者注册阶段,后四步发生在消费者订阅和业务调用阶段;纵向连接用于表达二者的数据依赖,不表示它们必须由同一线程连续执行。

服务注册全过程

服务实例启动后,会把自己注册到 Nacos。

mermaid
flowchart TD
    A["应用启动"] --> B["读取 spring.application.name"]
    B --> C["读取 Nacos server-addr、namespace、group"]
    C --> D["确定本机 IP、端口、metadata"]
    D --> E["向 Nacos 注册 Instance"]
    E --> F["Nacos 保存到服务实例列表"]
    F --> G["客户端定时心跳或服务端健康检查"]

注册信息通常包括:

字段示例作用
服务名asset-service调用方按服务名发现实例
IP10.1.2.11实际调用地址
端口8080实际调用端口
Namespaceprod环境隔离
GroupDEFAULT_GROUP服务分组
Clustershanghai区域、机房、集群
Weight1.0权重路由
Healthytrue健康状态
Metadataversion=v1灰度、标签、租户等

服务提供者配置示例:

yaml
spring:
  application:
    name: asset-service
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      discovery:
        namespace: prod
        group: DEFAULT_GROUP
        cluster-name: shanghai
        metadata:
          version: v1
          zone: shanghai

临时实例和持久实例

Nacos 实例有两类:临时实例和持久实例。旧资料也常把持久实例称为“永久实例”,本文统一使用更准确的“持久实例”。

类型健康判断适合场景实例异常后
临时实例客户端主动心跳普通 Spring Cloud 微服务心跳超时后自动删除
持久实例服务端主动探测固定 IP 服务、部分基础设施不会因连接断开直接删除,通常标记不健康

临时实例更像“我的生命周期跟客户端会话或连接相关;连接失效后注册信息应被清理”。持久实例更像“这台机器长期存在,服务端可以主动探测它是否健康,但不要因为连接断开就直接删除注册数据”。Nacos 1.x常见心跳模型和Nacos 2.x连接模型的准确区别见深度原理页。

mermaid
flowchart TD
    A["临时实例"] --> B["依赖客户端会话或连接"]
    B --> C["失联后清理注册"]
    C --> D["持久实例"]
    D --> E["服务端主动健康探测"]
    E --> F["失败时标记不健康并保留数据"]

Spring Cloud 微服务通常使用临时实例。因为容器实例频繁扩缩容,实例消失后应自动从注册表移除。

服务发现和订阅推送

消费者需要拿到实例列表。它不会每次调用都实时访问 Nacos,而是会维护本地缓存。

mermaid
flowchart TD
    A["消费者启动"] --> B["向 Nacos 订阅服务"]
    B --> C["拉取当前实例列表"]
    C --> D["写入本地缓存"]
    D --> E["服务实例发生变化"]
    E --> F["Nacos 通知订阅客户端"]
    F --> G["消费者更新本地缓存"]
    G --> H["业务请求到达"]
    H --> I["从本地缓存取实例列表"]
    I --> J["负载均衡选择实例"]

为什么需要本地缓存:

原因说明
性能每次调用都查 Nacos 会增加远程开销
可用性Nacos 短暂不可用时,调用方还能用旧缓存
降低压力注册中心不应该承受所有业务请求级查询
负载均衡LoadBalancer 基于本地实例列表选实例

为什么还需要推送或订阅:

原因说明
实例变化更快感知新实例上线、旧实例下线要尽快更新
降低轮询延迟不只靠固定周期刷新
支持健康状态变化不健康实例可从候选列表移除

注意:即使有推送,服务上下线也不是所有调用方瞬间一致。调用方本地缓存、连接池、网关路由、线程中的存量请求都存在传播窗口。

服务保护阈值和空列表保护

Nacos 服务发现不是简单地“健康就返回,不健康就一定不返回”。生产中还存在保护机制:当大量实例被判定不健康,注册中心可能为了避免全站瞬间无实例而触发保护;客户端收到异常空列表时,也可能保留最后一次可用快照。这些机制提高可用性,但会让调用方短时间继续看到旧实例或部分不健康实例。

mermaid
flowchart TD
    A["实例健康状态变化"] --> B["Nacos更新服务列表"]
    B --> C{"是否触发保护"}
    C -- "否" --> D["推送当前实例列表"]
    C -- "是" --> E["保留更宽候选或旧快照"]
    D --> F["调用方更新本地缓存"]
    E --> F
    F --> G["LoadBalancer选择实例"]

小白要记住:保护机制不是保证“选中的实例一定健康”,而是防止“健康误判导致所有实例都被摘掉”。因此业务调用仍必须配置短超时、熔断、限流、降级、连接池回收和写请求幂等。

更深入的保护阈值、空推送保护、旧实例窗口和JDK 8 Demo见:保护阈值与空列表保护

Nacos 和 LoadBalancer 调用链路

mermaid
flowchart TD
    A["业务代码调用 Feign"] --> B["Feign 动态代理"]
    B --> C["解析服务名 asset-service"]
    C --> D["LoadBalancer 获取候选实例"]
    D --> E["Nacos 本地实例缓存"]
    E --> F["按 namespace/group/cluster 过滤"]
    F --> G["选择一个实例"]
    G --> H["HTTP Client 调用 IP:Port"]

所以“注册中心有实例”不等于“调用方一定能调用到”。可能的过滤条件包括:

  1. Namespace 不一致。
  2. Group 不一致。
  3. Cluster 不一致或同集群优先。
  4. 实例不健康。
  5. 权重为 0。
  6. 灰度 metadata 不匹配。
  7. 本地缓存还没刷新。
  8. 连接池还在复用旧连接。

配置中心定位模型

Nacos Config 通过三个核心字段定位配置:

text
Namespace + Group + DataId
mermaid
flowchart TD
    A["应用启动"] --> B["确定 Namespace"]
    B --> C["确定 Group"]
    C --> D["确定 DataId"]
    D --> E["从 Nacos 拉取配置内容"]
    E --> F["加载到 Spring Environment"]

示例:

字段示例
Namespaceprod
GroupDEFAULT_GROUP
DataIdasset-service-prod.yml

如果任何一个字段不一致,应用就可能拉不到配置。

DataId 常见命名

不同版本 Spring Cloud Alibaba 的加载规则和配置方式会有差异,但工程上通常建议 DataId 和应用名、环境、后缀保持清晰关系。

常见方式:

text
${spring.application.name}.${file-extension}
${spring.application.name}-${profile}.${file-extension}

例如:

text
asset-service.yml
asset-service-prod.yml

命名不要混乱,否则会出现“控制台明明有配置,但应用就是没读到”的问题。

配置启动拉取流程

mermaid
flowchart TD
    A["应用启动"] --> B["读取本地 bootstrap 或 application 配置"]
    B --> C["拿到 Nacos 地址、namespace、group、文件后缀"]
    C --> D["连接 Nacos Config"]
    D --> E["按 DataId 拉取远程配置"]
    E --> F["合并到 Spring Environment"]
    F --> G["自动配置和业务 Bean 读取配置"]

为什么配置中心要在早期加载?因为很多自动配置条件和 Bean 创建依赖配置。例如数据源地址、Redis 地址、线程池参数、开关配置,如果等业务 Bean 创建完才加载,已经太晚了。

接入示例:

yaml
spring:
  application:
    name: asset-service
  profiles:
    active: prod
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      config:
        namespace: prod
        group: DEFAULT_GROUP
        file-extension: yml
      discovery:
        namespace: prod
        group: DEFAULT_GROUP

Nacos 中配置:

yaml
asset:
  collect:
    enabled: true
    batch-size: 500
    timeout-ms: 3000

配置属性类:

java
@Data
@ConfigurationProperties(prefix = "asset.collect")
public class AssetCollectProperties {
    private boolean enabled = true;
    private int batchSize = 200;
    private int timeoutMs = 3000;
}

自动配置或业务配置:

java
@Configuration
@EnableConfigurationProperties(AssetCollectProperties.class)
public class CollectConfig {

    @Bean
    public CollectClient collectClient(AssetCollectProperties properties) {
        return new CollectClient(
            properties.getBatchSize(),
            properties.getTimeoutMs()
        );
    }
}

配置动态刷新原理

应用启动后,Nacos Client 会监听配置变化。配置变更后,客户端收到变更通知或通过监听机制发现变化,再拉取最新配置,更新到 Spring 环境中。

mermaid
flowchart TD
    A["运维修改 Nacos 配置"] --> B["Nacos 保存新配置"]
    B --> C["通知或触发客户端发现变更"]
    C --> D["客户端拉取新配置"]
    D --> E["更新 Environment"]
    E --> F["检查 Bean 的绑定与刷新方式"]
    F --> G["可刷新则应用新值<br/>不可刷新则仍持有旧值"]

不是所有配置都适合动态刷新。

配置类型是否适合动态刷新说明
功能开关适合例如是否开启采集
限流阈值适合调整后要配合监控
批处理大小谨慎可能影响吞吐和下游压力
线程池核心参数谨慎要确认线程池实现支持动态修改
数据源地址不建议直接刷新连接池、事务、旧连接处理复杂
MQ Topic/Group不建议随意刷新消费组变化可能引发重复消费或漏消费

@RefreshScope 示例:

java
@RefreshScope
@RestController
public class CollectSwitchController {

    @Value("${asset.collect.enabled:true}")
    private boolean enabled;

    @GetMapping("/collect/enabled")
    public boolean enabled() {
        return enabled;
    }
}

更推荐把配置绑定到配置类,并明确哪些配置支持运行期变更,哪些必须重启。

配置发布为什么要有流程

配置中心让改配置变容易,也让线上事故变容易。

错误示例:

yaml
asset:
  collect:
    batch-size: 50000

如果采集批大小从 500 改成 50000,可能导致:

  1. 单批 SQL 太大。
  2. 下游医院接口超时。
  3. JVM 内存上涨。
  4. MQ 消息变大。
  5. 数据库锁时间变长。
  6. 采集任务失败重试形成雪崩。

生产配置变更应该有:

能力说明
权限控制不是所有人都能改生产配置
审批高风险配置要审批
变更记录谁在什么时候改了什么
灰度先对少量实例或小流量生效
回滚出问题能快速恢复旧配置
监控配置变更后观察错误率、耗时、QPS

Nacos 高可用

生产环境不要单节点 Nacos。

mermaid
flowchart TD
    A["应用实例"] --> B["Nacos 三节点集群"]
    B --> C["节点 1、节点 2、节点 3"]
    C --> D["共享数据库与备份"]

生产建议:

  1. 至少三节点部署。
  2. 使用外部数据库保存配置和元数据。
  3. 数据库要备份。
  4. 管理端账号不能使用默认弱口令。
  5. 控制台不要暴露公网。
  6. 不同环境最好独立 Namespace,重要环境也可独立集群。
  7. 监控 Nacos 节点 CPU、内存、连接数、请求耗时、数据库连接。

Nacos 是基础设施,不能让它成为单点。

Nacos 和 Eureka 对比

对比项EurekaNacos
生态Spring Cloud NetflixSpring Cloud Alibaba
注册发现支持支持
配置中心不提供提供
命名空间能力较弱Namespace/Group/Cluster 更常用
健康检查客户端心跳、自我保护支持临时/永久实例和健康检查
新项目常见度老项目维护常见Alibaba 生态新项目常见
设计重点可用性、客户端缓存注册发现 + 配置管理一体化

不要只说“Nacos 替代 Eureka”。更准确是:Nacos 不只覆盖注册发现,还覆盖配置中心能力,平台职责更重,因此权限、审计、备份、发布流程也更重要。

Nacos 和 Spring Cloud Config / Apollo 对比

对比项Nacos ConfigSpring Cloud ConfigApollo
注册发现内置 Nacos Discovery不提供不提供注册中心
配置管理提供提供,常配 Git提供,配置治理成熟
动态刷新支持支持,常配 Bus支持
权限和发布治理需要按平台能力建设依赖配套平台Apollo 在配置治理上较强
适合Spring Cloud Alibaba 一体化Spring 官方生态、Git 管理配置治理要求较高的企业

选型要看团队生态,不要只看单点功能。

商业场景:医疗数据采集平台

医疗数据采集平台里,Nacos 常用于:

场景Nacos 能力
采集服务发现资产服务Discovery
Gateway 路由到服务Discovery + LoadBalancer
医院接口地址Config
采集开关Config 动态刷新
采集批大小Config,谨慎动态调整
限流阈值Config + Sentinel
灰度版本Instance metadata
多环境隔离Namespace

调用链路:

mermaid
flowchart TD
    A["采集任务进入 collect-service"] --> B["读取医院接口、批大小和开关"]
    B --> C["Feign 按服务名发起调用"]
    C --> D["LoadBalancer 读取本地实例列表"]
    D --> E["选择 asset-service 实例"]
    E --> F["HTTP 调用真实地址"]

配置变更链路:

mermaid
flowchart TD
    A["运维调整采集开关"] --> B["提交 Nacos 配置"]
    B --> C["记录审批和变更"]
    C --> D["应用监听到配置变化"]
    D --> E["刷新支持动态变更的 Bean"]
    E --> F["采集任务按新开关执行"]
    F --> G["监控成功率和耗时"]

生产排查:服务发现不到

现象:Feign 报 No instances available for asset-service

mermaid
flowchart TD
    A["No instances available"] --> B["核对服务名、Namespace、Group"]
    B --> C["核对控制台健康实例与注册 IP"]
    C --> D["核对客户端缓存是否收到更新"]
    D --> E["核对 Cluster、Metadata、权重过滤"]
    E --> F["恢复后用真实调用验证"]

这是一条排查顺序,不表示所有步骤都正常后才进入下一步。发现某项不一致就先修复并重新验证;每一步要检查的证据见下表,深层日志与恢复命令见服务发现 Runbook

排查清单:

检查项说明
服务名@FeignClient(name)spring.application.name
Namespace提供者和消费者是否同环境
Group服务分组是否一致
Cluster是否同集群优先导致过滤
Healthy实例是否健康
Weight权重是否为 0
IP 可达注册 IP 是否能从调用方访问
本地缓存调用方是否已刷新实例列表

生产排查:配置不生效

现象:Nacos 控制台有配置,但应用读不到。

mermaid
flowchart TD
    A["配置不生效"] --> B["核对 DataId、Group、Namespace"]
    B --> C["核对启动期导入方式和客户端日志"]
    C --> D["核对 Environment 中的值与来源"]
    D --> E["核对属性绑定、刷新范围与类型转换"]
    E --> F["排除环境变量和本地配置覆盖"]

配置排查必须区分“客户端没有拿到配置”“Environment 中已有新值”“业务 Bean 仍持有旧值”三个层次。逐层取证方法见配置 Runbook,不要只通过重启来掩盖根因。

常见原因:

原因表现
DataId 拼错控制台有配置但应用找不到
file-extension 不一致yml/yaml/properties 混乱
Namespace 错dev 应用读 prod 或反过来
Group 错同 DataId 不同 Group
Profile 未激活读了默认配置而不是环境配置
本地配置覆盖最终 Environment 中不是 Nacos 值
配置类未注册@ConfigurationProperties 没生效
类型转换失败字符串转数字、时间、集合失败

生产排查:修改配置后不刷新

先判断这个配置是否应该动态刷新。

检查项说明
客户端是否监听配置日志里是否有配置变更记录
Bean 是否支持刷新是否使用 @RefreshScope 或动态配置机制
配置读取方式构造方法里读一次的值不会自动变
配置类型数据源、线程池、MQ 这类复杂 Bean 不建议随便刷新
是否有多实例是否所有实例都收到变更

很多配置“不刷新”不是 Nacos 问题,而是代码把配置在启动时读取成普通字段,后续没有刷新机制。

常见坑

后果正确做法
Namespace 混乱服务发现不到、配置读错环境dev/test/prod 清晰隔离
Group 随便改提供者和消费者互相不可见统一分组规范
DataId 命名混乱配置找不到应用名 + profile + 后缀
配置中心无审批生产误改配置权限、审批、审计、回滚
动态刷新所有配置连接池、线程池状态不可控只刷新适合动态调整的配置
Nacos 单节点基础设施单点故障三节点 + 数据库备份
注册不可达 IP控制台有实例但调用失败注册调用方可访问地址
把配置当数据库频繁存业务数据配置中心只放配置,不放高频业务状态

独立面试入口

面试标准回答、追问和源码原理跳转已拆分到 Nacos独立面试题。知识点页只保留完整原理、Demo、商业场景和排查步骤,避免标准回答与课程正文混在一起。

关联知识点

知识点跳转
Spring Cloud 主线Spring Cloud 从零到生产级掌握
EurekaEureka 注册发现
服务调用链路服务调用链路
负载均衡Ribbon 与 LoadBalancer
配置中心配置中心
GatewayGateway
Nacos面试题Nacos独立面试题
Spring Cloud面试主线Spring Cloud 面试知识点

本章小结

Nacos 不只是“控制台里能看到服务”。注册发现要理解服务名、实例、心跳、健康状态、订阅推送、本地缓存和负载均衡;配置中心要理解 Namespace、Group、DataId、启动拉取、动态监听、刷新边界和配置发布治理。真正到商业项目里,Nacos 的难点不只是接入,而是环境隔离、命名规范、权限审计、高可用、配置变更流程和线上排查。