【SpringCloud合集-01】Nacos 微服务注册与配置中心实战学习
目录
基于 Spring Cloud Alibaba 2.2.x + Nacos 2.x 实操学习
一、Nacos 核心定位与整体架构
1. 核心定位
Nacos(Dynamic Naming and Configuration Service)是阿里开源的一站式微服务基础设施,同时承担服务注册发现和动态配置管理两大核心能力,替代了传统 Spring Cloud 技术栈中 Eureka + Spring Cloud Config + Spring Cloud Bus 的组合,是 Spring Cloud Alibaba 生态的核心组件。 它的核心优势在于:
- 双能力合一:服务治理与配置管理共用一套集群,降低运维成本
- 双一致性模式:支持 AP 高可用模式与 CP 强一致模式灵活切换
- 生产级验证:经过阿里双十一场景验证,支持十万级服务实例规模
- 开箱即用:提供可视化控制台,支持权限控制、配置版本、灰度发布等运维能力
2. 整体架构

架构分层说明:
- 接入层:客户端通过 HTTP/gRPC 协议与服务端交互,支持多语言 SDK
- 核心功能层:命名服务负责服务注册发现、健康检查;配置服务负责配置存储、推送、版本管理
- 一致性协议层:根据场景选择不同协议,保证分布式下的数据一致性
- 存储层:默认内置 Derby 数据库,生产环境推荐外接 MySQL 实现持久化
二、Nacos 服务注册发现实战与底层源码解析
1. Spring Cloud Alibaba 整合实战
第一步:引入依赖
<!-- Spring Cloud Alibaba 版本统一管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Nacos 服务注册发现启动器 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
第二步:应用配置
spring:
application:
name: order-service # 服务名,注册到Nacos的唯一标识
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos服务端地址
namespace: dev_123456 # 命名空间,用于环境隔离
group: business-order # 分组,用于业务模块隔离
ephemeral: true # 是否为临时实例,默认true
weight: 1 # 实例权重,用于流量分配
metadata: # 自定义元数据,可用于灰度、路由
version: v1.0
env: dev
第三步:启动类与服务调用
@SpringBootApplication
@EnableDiscoveryClient // 开启服务注册发现能力,Spring Cloud 高版本可省略
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
// 注入RestTemplate,结合Ribbon实现负载均衡调用
// 先不使用OpenFeign
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2. 核心概念:临时实例 vs 持久实例
Nacos 将服务实例分为两类,对应不同的一致性与健康检查策略:
| 类型 | 默认值 | 健康检查方式 | 宕机处理 | 一致性模式 | 适用场景 |
|---|---|---|---|---|---|
| 临时实例 | 是 | 客户端主动心跳上报 | 15s 无心跳标记不健康,30s 自动剔除 | AP 模式(Distro 协议) | 常规微服务实例,弹性扩缩容 |
| 持久实例 | 否 | 服务端主动探活检查 | 仅标记不健康,不会自动剔除 | CP 模式(Raft 协议) | 数据库、缓存、中间件等固定服务 |
3. 服务注册完整流程源码级拆解

客户端核心源码
注册入口在 NacosNamingService,核心逻辑是组装实例参数后发送 HTTP 请求到服务端,注册成功后立即启动心跳任务。
// com.alibaba.nacos.client.naming.NacosNamingService
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
// 1. 校验参数,组装服务全名
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
// 2. 临时实例必须开启心跳
if (instance.isEphemeral()) {
BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
// 添加心跳任务,默认5秒执行一次
beatReactor.addBeatInfo(groupedServiceName, beatInfo);
}
// 3. 发送HTTP POST请求,调用服务端注册接口
serverProxy.registerService(groupedServiceName, groupName, instance);
}
服务端核心源码
服务端通过 InstanceController 接收注册请求,最终写入 ServiceManager 的内存注册表中。
// com.alibaba.nacos.naming.core.ServiceManager
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
// 1. 服务不存在则创建空服务
emptyService(namespaceId, serviceName, instance.isEphemeral());
Service service = getService(namespaceId, serviceName);
synchronized (service) {
// 2. 将实例添加到对应服务的实例列表中
addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}
// 3. 临时实例触发异步集群同步
if (instance.isEphemeral()) {
distroProtocol.sync(new DistroKey(serviceName, instance.getIp() + "#" + instance.getPort(),
KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,
DistroConfig.getInstance().getSyncDelayMillis());
}
}
注册表底层结构:采用双层 ConcurrentHashMap 实现,线程安全且查询性能极高。
Map<namespaceId, Map<groupedServiceName, Service>> serviceMap
- 第一层:按命名空间隔离
- 第二层:按服务名隔离 - Service 对象内部维护集群列表、实例列表、健康检查器等核心数据
4. 健康检查与剔除机制
- 客户端心跳:临时实例启动后台
BeatTask线程,默认每 5 秒向服务端发送一次心跳包,更新最后心跳时间。 - 服务端剔除:服务端后台定时任务每 5 秒扫描所有实例,15 秒未收到心跳的实例标记为不健康,30 秒未收到心跳的临时实例直接从注册表移除。
- 持久实例探活:服务端主动发起 TCP/HTTP 探活,失败仅标记不健康,不会自动删除,需要人工下线。
5. 服务发现与本地缓存
客户端获取服务列表采用「定时全量拉取 + 事件订阅 + 本地缓存」的三级机制:
- 客户端启动时主动拉取一次全量服务列表,存入本地内存
- 后台定时任务默认每 10 秒拉取一次,更新本地缓存
- 支持 UDP 推送,服务端实例变更时主动通知客户端
- 本地同时生成快照文件缓存到磁盘,即使 Nacos 全挂、客户端重启,也能读取缓存保证服务调用可用
生产意义:这是 Nacos 高可用设计的关键,注册中心故障不会影响已有服务的正常调用,仅影响新服务注册和实例变更。
三、Nacos 配置中心实战与底层源码解析
1. 配置中心三级模型
Nacos 配置管理采用 Namespace + Group + DataId 的三级结构,实现多层级隔离:
- Namespace 命名空间:最顶层隔离,用于区分开发、测试、生产等环境,不同命名空间下配置完全不可见
- Group 分组:中间层隔离,用于区分不同业务线、不同项目,默认值为
DEFAULT_GROUP - DataId 数据 ID:具体的配置文件,通常对应一个应用的配置,格式支持 yaml、properties 等
2. Spring Cloud 整合实战
第一步:引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
第二步:bootstrap 配置
配置中心的配置必须在 Spring 上下文刷新前加载,因此要写在 bootstrap.yml 中,优先级高于 application.yml。
spring:
application:
name: user-service
profiles:
active: dev
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: dev_123456
group: business-user
file-extension: yaml # 配置文件后缀,对应dataId拼接规则
# 共享配置:多个应用共用的公共配置
shared-configs:
- data-id: common-db.yaml
group: common-group
refresh: true # 是否支持动态刷新
# 扩展配置:额外引入的第三方配置
extension-configs:
- data-id: redis-config.yaml
group: middleware-group
refresh: true
DataId 完整拼接规则:
${spring.application.name}-${spring.profiles.active}.${file-extension}当
user-service应用启动时,Nacos 客户端默认会去 Nacos 服务端查找名为user-service-dev.yaml的配置文件,并将其作为该应用独有的主配置拉取下来。
这个规则解决了多环境隔离和多服务区分的问题:
-
区分服务(
${spring.application.name}):user-service和order-service即使都激活dev环境,也会分别拉取user-service-dev.yaml和order-service-dev.yaml,互不干扰。 -
区分环境(
${spring.profiles.active}):同一个user-service,在dev环境拉取user-service-dev.yaml,在prod环境拉取user-service-prod.yaml。这样就不需要改代码,只需启动时指定-Dspring.profiles.active=prod,就能自动切换数据库连接、日志级别等配置。 -
区分格式(
${file-extension}):支持yaml或properties,客户端根据后缀自动解析。
主配置,和之前的 shared-configs / extension-configs 关系:
| 配置类型 | 对应的 DataId 示例 | 作用范围 | 优先级(值覆盖) |
|---|---|---|---|
| 主配置(上面公式) | user-service-dev.yaml |
当前服务 + 当前环境 专属的业务配置 | 最高(最后加载) |
extension-configs |
redis-config.yaml |
当前服务引入的中间件专项配置 | 中等 |
shared-configs |
common-db.yaml |
所有服务通用的底层配置 | 最低(最先加载) |
第三步:动态刷新代码示例
@RestController
@RequestMapping("/config")
@RefreshScope // 开启该类配置的动态刷新,变更后自动重建Bean
public class ConfigTestController {
@Value("${user.config.nickname:default}")
private String nickname;
@Value("${user.config.max-count:100}")
private Integer maxCount;
@GetMapping("/test")
public String getConfig() {
return "昵称:" + nickname + ",最大次数:" + maxCount;
}
}
@RefreshScope 原理解析: 加了该注解的 Bean 会被放入专门的 RefreshScope 缓存中。配置变更时,Spring 会清空这个缓存,下次访问该 Bean 时会重新创建实例,新实例会注入最新的配置值,从而实现动态刷新。
3. 配置加载完整流程

4. 长轮询动态推送原理
Nacos 配置中心没有用 WebSocket 等长连接技术,而是采用长轮询 + MD5 比对的方案,兼顾实时性与性能。
客户端核心逻辑
ClientWorker 类内部维护 LongPollingRunnable 线程,循环执行以下逻辑:
- 客户端携带本地所有配置的 MD5 值,向服务端发起 HTTP 请求,超时时间设置为 30 秒
- 服务端比对 MD5,如果有配置变更,立即返回变更的 DataId 列表
- 如果 30 秒内没有配置变更,服务端在超时前返回空响应
- 客户端收到响应后,立即发起下一次长轮询请求
- 客户端拿到变更的 DataId 后,单独拉取最新配置,更新本地环境
// com.alibaba.nacos.client.config.impl.ClientWorker.LongPollingRunnable
@Override
public void run() {
// 1. 检查本地配置MD5,组装校验参数
List<String> changedGroupKeys = checkUpdateDataIds(cacheDatum, inInitializingCacheList);
// 2. 有变更则拉取最新配置
for (String groupKey : changedGroupKeys) {
String[] key = GroupKey.parseKey(groupKey);
String dataId = key[0];
String group = key[1];
getServerConfig(dataId, group, 3000L);
}
// 3. 递归提交下一次长轮询任务
executorService.execute(this);
}
服务端核心逻辑
服务端 LongPollingService 接收长轮询请求,将请求挂起,不立即返回:
- 监听配置变更事件,如果对应配置发生修改,立即唤醒挂起的请求,返回变更信息
- 如果到超时时间都没有变更,返回空结果,由客户端重连
设计优势:相比普通轮询,大幅减少无效请求;相比 WebSocket,实现简单,兼容所有网络环境,且服务端压力更低。
四、Nacos 集群架构与一致性原理
1. 集群部署架构
生产环境推荐 3 节点及以上的奇数节点集群,通过 SLB/VIP 做统一入口,客户端连接 VIP 即可。
- 节点数量:至少 3 台,奇数台,保证 Raft 协议可以正常选举
- 存储选型:生产环境必须切换为 MySQL 存储,默认内置 Derby 仅适合单机测试
- 跨机房部署:建议节点分布在不同机房、不同机架,提高容灾能力
2. 双一致性协议设计
Nacos 最大的设计特点是针对不同场景采用不同的一致性协议,实现 AP 与 CP 的融合:
| 功能模块 | 对应实例类型 | 一致性协议 | 一致性级别 | 设计目标 |
|---|---|---|---|---|
| 服务注册发现 | 临时实例 | Distro 协议 | 最终一致(AP) | 优先保证高可用,容忍短暂不一致 |
| 服务注册发现 | 持久实例 | Raft 协议 | 强一致(CP) | 优先保证数据准确,故障时不可写入 |
| 配置中心 | 全部配置 | Raft 协议 | 强一致(CP) | 配置数据绝对不能出错,保证全节点一致 |
3. Distro 协议(AP 模式)
Distro 是 Nacos 自研的分布式一致性协议,专门针对临时服务注册场景设计:
- 去中心化:所有节点地位平等,客户端可以连接任意节点读写
- 数据分片:每个节点负责一部分服务的数据,通过哈希算法分配
- 异步同步:写请求路由到负责节点写入成功后立即返回,后台异步同步到其他节点
- 最终一致:数据存在短暂延迟,通常秒级内达到全节点一致
- 故障容错:任意节点宕机不影响其他节点,集群整体可用,仅该节点负责的分片数据暂时不可写
4. Raft 协议(CP 模式)
Raft 是工业界通用的强一致性共识算法,配置中心和持久实例都依赖它:
- 三个角色:Leader(主节点,处理所有写请求)、Follower(从节点,同步数据、处理读请求)、Candidate(选举候选状态)
- 选主机制:Leader 故障后,剩余节点发起选举,获得过半票数的节点成为新 Leader
- 日志复制:写请求由 Leader 接收,生成日志同步给 Follower,过半节点写入成功后提交返回
- 脑裂防护:必须过半节点同意才能写入,网络分区时少数派集群无法写入,保证数据不出现双写冲突
五、实战踩坑与最佳实践
1. 服务注册发现常见踩坑
- 环境隔离误区:不要用 Group 区分环境,Namespace 才是官方设计的环境隔离单位。不同环境(dev/test/prod)必须建独立的 Namespace,避免配置和服务串环境。
- 服务无故下线:优先检查网络是否有防火墙中断心跳、实例内存溢出导致心跳线程停止、客户端版本与服务端不兼容。
- 实例权重滥用:权重为 0 的实例不会被消费方调用,常用于灰度发布、优雅停机;不要随意设置过大权重,导致单实例流量过高。
- Nacos 宕机影响:客户端有本地内存缓存和磁盘快照,Nacos 集群全挂不会影响已有服务的正常调用,仅无法注册新服务和感知实例变更。
2. 配置中心常见踩坑
- 配置不刷新:检查类是否加了
@RefreshScope;检查长轮询请求是否被网关 / 防火墙中断;确认配置的refresh属性是否为 true。 - 配置优先级错乱:配置覆盖优先级由高到低排序:应用自身 profile 专属配置 > 应用主配置 > 扩展配置 > 全局共享配置;同名属性遵循 “高优覆盖低优” 原则,同类型配置内声明越靠后,优先级越高。
- 敏感信息明文:数据库密码、密钥等敏感配置不要明文存储,使用 Nacos 配置加密插件或集成配置中心加密方案。
- 配置误发布:生产环境开启配置发布审批、灰度发布、版本回滚能力,避免误修改导致全量故障。
3. 生产最佳实践
- 命名空间按环境划分,Group 按业务线划分,DataId 按应用 + 环境命名,形成规范的命名体系
- 核心服务使用持久实例,普通微服务使用临时实例,合理平衡可用性与一致性
- 配置中心按应用粒度拆分,公共配置抽离为共享配置,避免重复维护
- 集群至少 3 节点,外接 MySQL 持久化,开启监控告警,关注集群健康状态、连接数、配置推送延迟
- 客户端设置合理的超时和重试参数,避免服务端抖动影响业务
六、面试速记总结
- 核心定位:Nacos 是集服务注册发现、动态配置管理于一体的微服务基础设施,支持 AP/CP 双模式。
- 服务注册:客户端启动发送注册请求,服务端写入内存注册表,临时实例用心跳保活,30 秒无心跳自动剔除。
- 配置中心:三级模型 Namespace+Group+DataId;长轮询 + MD5 比对实现配置动态推送;@RefreshScope 实现 Bean 动态刷新。
- 一致性协议:临时实例用 Distro AP 协议,保证高可用;持久实例和配置用 Raft CP 协议,保证强一致。
- 高可用设计:客户端本地缓存服务列表和配置快照,注册中心故障不影响已有服务调用。
- 与 Eureka 对比:Eureka 仅支持 AP 服务注册,无配置中心能力;Nacos 功能更全,支持 CP 模式,性能和容量更高,阿里生态适配更好。
更多推荐

所有评论(0)