Nacos 深入原理
服务注册发现 + 配置管理平台。阿里的开源项目,Spring Cloud 中替代 Eureka(注册中心)+ Spring Cloud Config(配置中心)的更优选择。一套平台搞定两件事。
1. 两个核心功能
1.1 服务注册发现
服务提供者 Provider
↓ 注册(把自己的 IP:Port 告诉 Nacos)
↓
Nacos Server(注册中心)
↓
服务消费者 Consumer
↓ 订阅 + 发现(从 Nacos 获取Provider地址)
↓
直接调用 Provider
1.2 配置管理
应用启动 → 从 Nacos 读取配置文件
↓
配置文件变更 → Nacos 推送更新到应用 → 应用无感重新加载配置
2. 服务注册发现原理
2.1 概念解释
| 概念 | 说明 |
|---|---|
| 实例(Instance) | 一个运行中的 Provider 进程 |
| 服务(Service) | 同一种业务的一组实例集合 |
| 集群(Cluster) | 实例的逻辑分组(如机房A、机房B) |
Service: order-service
Cluster: Hangzhou
Instance: 192.168.1.10:8080 (实例1)
Instance: 192.168.1.11:8080 (实例2)
Cluster: Beijing
Instance: 192.168.2.10:8080 (实例3)
2.2 注册流程
// 应用启动时,Spring Cloud 向 Nacos 注册
// 伪代码逻辑
ServiceInstance instance = new ServiceInstance();
instance.setHost("192.168.1.10");
instance.setPort(8080);
instance.setServiceName("order-service");
nacosServer.registerInstance(instance); // 注册自己
服务提供者停止时,Nacos 有心跳检测机制:
Provider 启动 → 注册到 Nacos
↓
Nacos 定时发送心跳(默认 5秒)
↓
超过 15 秒没响应 → Nacos 认为实例不健康 → 从列表中摘除
↓
超过 30 秒没响应 → 彻底删除实例
2.3 发现流程
// 消费者从 Nacos 获取服务列表
List<ServiceInstance> instances = nacosServer.selectInstances("order-service", true);
// true = 只返回健康实例
Ribbon/Feign 底层调用这个方法,然后配合负载均衡算法(RoundRobin、Random、Weight)选择一个实例调用。
2.4 心跳机制细节
Nacos 1.x 使用 HTTP 长轮询 + 客户端上报心跳:
- Provider 每 5 秒主动上报心跳到 Nacos Server
- Nacos Server 超过 15 秒没收到心跳,标记为不健康
- 超过 30 秒,删除实例
Nacos 2.x 升级为 gRPC 协议,性能更好。
3. 配置中心原理
3.1 配置管理流程
// 应用启动时加载配置
String config = nacosServer.getConfig("order-service.yml", "dev");
// Nacos 控制台修改了配置,发布
// ↓
// Nacos 主动推送到所有订阅的客户端(HTTP 长轮询,30秒超时)
// ↓
// 客户端收到变更通知 → 重新拉取最新配置 → 刷新到 Spring 容器
3.2 配置变更推送机制
Nacos 使用 HTTP 长轮询 实现配置变更推送:
客户端 → 发起请求询问"配置变了没"
↓
服务端 hold 请求 30 秒
↓
30秒内配置没变 → 返回 304(Not Modified)
30秒超时 → 返回 304 → 客户端立刻再次请求(继续等)
↓
人为修改配置 → 服务端立即返回最新配置内容
这个机制保证了配置变更的实时性(最快 30ms 感知),又不浪费轮询资源。
3.3 配置的命名空间隔离
命名空间(Namespace)
└── 分组(Group)
└── 配置(Data ID)
# Nacos 配置示例
data-id: order-service.yml
group: DEFAULT_GROUP
namespace: production
不同环境用不同 Namespace(dev/test/prod),不同业务用不同 Group。
4. Nacos 与其他组件对比
4.1 注册中心对比:Nacos vs Eureka vs Zookeeper
| 特性 | Nacos | Eureka | Zookeeper |
|---|---|---|---|
| 一致性模型 | CP/AP 可切换 | AP(只保证最终一致) | CP(强一致) |
| 配置管理 | 支持 | 不支持 | 支持(但体验差) |
| 多环境隔离 | Namespace | 多 Profile | 多环境困难 |
| 活跃度 | 阿里背书,非常活跃 | Netflix 已停止维护 | Apache 维护 |
| CAP 原则 | 支持两种模式 | 只支持 AP | 只支持 CP |
Nacos CP/AP 切换:
# Nacos 默认是 AP 模式(临时实例)
# 切换为 CP 模式(持久化实例)
nacos.server.capability.mode=CP
CP 模式下,注册的是持久化实例, Leader 宕机不允许写入,适合 Etcd/Zookeeper 场景。
4.2 配置中心对比:Nacos vs Apollo vs Spring Cloud Config
| 特性 | Nacos | Apollo | Spring Cloud Config |
|---|---|---|---|
| 配置推送 | HTTP 长轮询 | HTTP 长轮询 | Git Hook(需结合 Bus) |
| 实时性 | ~30ms | ~30ms | 分钟级 |
| 多环境管理 | Namespace | 多环境+多集群 | Git 分支 |
| 权限管理 | 基础 | 完整(部门+人) | 无 |
| 审计日志 | 基础 | 完整 | 无 |
| 部署复杂度 | 简单(单节点) | 复杂(MySQL+多组件) | 简单 |
| 规模 | 万级服务 | 十万级服务 | 千级服务 |
Apollo 的优势:权限体系完整,适合大公司多部门管理;审计日志完善,适合金融等合规场景。
Nacos 的优势:部署简单,功能足够,对 Spring Cloud 集成好。
5. Nacos 集群架构
5.1 集群脑裂问题
Nacos 默认是 AP 模式,通过 Raft 协议选主:
节点A(Leader) 节点B(Follower) 节点C(Follower)
网络分区时:
分区1:节点A + 节点B
分区2:节点C(孤立的 Follower)
↓
节点C 如果也能选主 → 出现两个主(脑裂)
↓
Raft 的解决方案:节点C 选主失败(得票不足),不对外提供服务
5.2 持久化模式
Nacos 默认使用嵌入式数据库(Derby),集群部署需要配置外部 MySQL:
# cluster.conf 指定集群节点
192.168.1.10:8848
192.168.1.11:8848
192.168.1.12:8848
# application.properties 指定外部数据库
spring.datasource.platform=mysql
db.url=jdbc:mysql://192.168.1.100:3306/nacos_config
db.username=nacos
db.password=nacos
6. 实战配置示例
6.1 Spring Boot 接入 Nacos 配置中心
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
# bootstrap.yml
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: 192.168.1.10:8848
file-extension: yaml # 配置格式
namespace: dev # 命名空间
group: DEFAULT_GROUP # 分组
refresh-enabled: true # 开启自动刷新
profiles:
active: dev # 环境
6.2 动态刷新配置
@RestController
@RefreshScope // 关键注解:支持动态刷新配置
public class OrderController {
@Value("${order.page-size:10}")
private int pageSize;
@GetMapping("/orders")
public List<Order> getOrders() {
// pageSize 会随配置变更自动更新
return orderService.page(pageSize);
}
}
配置变更后,Nacos 推送通知 → @RefreshScope 重新创建 Bean → 新的配置值生效。
6.3 分布式配置共享
// 共享配置:多个服务共享同一份配置
// 优先级:服务自身配置 > 共享配置
@PropertySource(value = "classpath:shared.properties")
7. 常见问题与排查
7.1 服务已注册但 Discovery 找不到
常见原因:
- 实例的
healthCheck没配置,Nacos 无法判断健康状态 → 配置健康检查 - 网络不通(安全组/防火墙)→ 检查网络连通性
- 命名空间不一致(不同 Namespace 的服务互相不可见)→ 确认 Namespace
7.2 配置变更后应用没有刷新
排查步骤:
# 1. 确认 Nacos 控制台配置内容正确
# 2. 确认客户端日志开启了 debug
logging.level.com.alibaba.nacos.config=DEBUG
# 3. 看是否有收到变更通知的日志
# "Nacos config received push: ..."
常见原因:
- 没有加
@RefreshScope注解 - 注解标在错误的类上(不是标在需要刷新配置的类)
- 使用了静态变量(
static字段不受@RefreshScope管理)
7.3 客户端无法连接 Nacos Server
# 确认 Nacos Server 健康状态
curl http://192.168.1.10:8848/nacos/v1/console/health
# 返回 {"status":"UP"} 则正常
网络问题:Nacos 需要开放两个端口:
8848:客户端连接/请求 API9848:集群通信(gRPC,Nacos 2.x)
8. 最佳实践
- 生产环境必须用集群模式:单节点 Nacos 挂了,所有服务无法注册和发现
- 命名空间隔离环境:dev/test/prod 用不同 Namespace,避免污染
- 配置使用 YAML 格式:比 properties 更结构化,支持嵌套
- 配置加密:敏感配置(数据库密码、Token)使用 Nacos 的加密功能
- 配置灰度发布:先发布到部分实例,确认无误再全量