一、 引言:从 Eureka 的"停更"说起
微服务架构的核心痛点在于:服务实例是动态不稳定的。
在很长一段时间里,Eureka 是我们的首选。它简单、粗暴,奉行 AP(可用性)至上主义。但是,随着 Eureka 2.0 开源计划的搁置,以及微服务架构向 Kubernetes (K8s) 云原生环境的迁移,Eureka 暴露出了明显的短板:
-
更新延迟大:Eureka 的三级缓存机制导致服务下线感知慢,极端情况下可能需要分钟级才能感知故障。
-
功能单一:仅作注册中心,配置中心还得另搭 Spring Cloud Config + Bus + RabbitMQ,运维极其痛苦。
而 Nacos (Dynamic Naming and Configuration Service) 的出现,直接进行了一次降维打击。它最大的杀手锏在于:它不选边站,它全都要(支持 AP 和 CP 切换)。
二、 深度理论:CAP 的权衡艺术
CAP 定理(Consistency, Availability, Partition Tolerance)是分布式系统的铁律。P(分区容错)是必选项,我们只能在 C 和 A 之间权衡。
1. 为什么 Eureka 只能是 AP?
Eureka 的各个节点是对等的(Peer-to-Peer)。
-
场景:节点 A 收到注册请求,它会先更新自己,然后异步复制给节点 B。
-
问题 :如果网络断开,A 和 B 数据可能不一致。但在微服务调用中,偶尔拿到一个脏数据(例如调用了一个正在关闭的节点),客户端通常有重试机制(Retry)兜底,所以可用性(A)比一致性(C)更重要。
2. 为什么 Zookeeper 只能是 CP?
Zookeeper 采用 ZAB 协议(类似 Raft)。
-
场景:它必须选出一个 Leader。只有 Leader 能处理写请求。
-
问题 :如果 Leader 挂了,集群进入选举期(30s ~ 120s)。这期间整个系统无法进行服务注册!对于分秒必争的电商交易系统,这是不可接受的灾难。
3. Nacos 的"双模"智慧
Nacos 引入了 临时实例 (Ephemeral) 和 持久实例 (Persistent) 的概念,从而实现了 CAP 的混用。
|-------------|-------------------|---------|----------------------------|-----------------------|
| 模式 | 协议 | 数据存储 | 适用场景 | 关键特征 |
| AP (临时) | Distro (阿里自研) | 内存 | Spring Cloud 微服务, K8s Pods | 心跳检测,超时即剔除,访问速度快 |
| CP (持久) | Raft (强一致共识) | 磁盘 + 内存 | MySQL, Redis, 数据库主从节点 | 服务端探测,异常仅标记不健康,数据绝不丢失 |
三、 实战案例:电商大促系统
1. 业务架构分析
系统分为两类核心服务:
-
交易链路服务(Order-Service, Product-Service):部署在 K8s 上,大促时会瞬间从 10 个节点扩容到 1000 个节点。IP 随时变。
-
核心组件服务(Payment-Gateway, DB-proxy):通常部署在物理机或固定 IP 的虚拟机上,很少变动,但要求绝对稳定,不能把请求发给坏掉的节点。
2. 痛点重现
-
如果全用 Eureka (AP):Payment-Gateway 挂了一个节点,Eureka 还没感知到,订单服务还在疯狂往挂掉的网关发请求,导致支付失败率飙升。
-
如果全用 Zookeeper (CP):大促高峰期,某个机房网络抖动,ZK 重新选举,导致成千上万个 Product-Service 无法注册,整个平台瘫痪。
3. Nacos 的混合解决方案
我们可以在同一个 Nacos 集群中,针对不同的服务配置不同的模式。
服务 A:订单服务 (Order-Service) -> AP 模式
我们需要极高的吞吐量和极快的扩容感知。
application.yml 配置:
java
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 192.168.1.100:8848
# 设置为临时实例(默认值),开启 AP 模式
ephemeral: true
效果 :使用 Nacos 的 Distro 协议。
-
Order-Service 启动时,向 Nacos 任意节点发送 HTTP/gRPC 注册。
-
该 Nacos 节点无需等待其他节点确认,直接返回"注册成功"。
-
数据会在 Nacos 节点间异步同步。
服务 B:支付网关 (Payment-Gateway) -> CP 模式
我们需要强一致性,宁可报错也不要调用错误的节点。
application.yml 配置:
java
spring:
application:
name: payment-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.1.100:8848
# 【关键】设置为持久实例,开启 CP 模式
ephemeral: false
效果 :使用 Nacos 的 Raft 协议。
-
Payment-Gateway 注册时,请求会被转发给 Nacos 集群的 Leader。
-
Leader 将日志同步给 Follower,大多数节点写盘成功后,才返回"注册成功"。
-
如果 Payment-Gateway 宕机,Nacos 不会直接删除它,而是标记为"不健康",保留其元数据,直到人工介入或节点恢复。
四、 核心架构图解

五、 源码探秘:Nacos 是如何实现自动切换的?
面试时如果能说出这一节,绝对是 P7/P8 级别的回答。
在 Nacos Server 端源码中,核心入口在 ServiceManager。当客户端发起注册请求(POST /instance)时,服务端会根据 ephemeral 字段分流。
1. 核心接口 ConsistencyService
Nacos 定义了一个一致性服务接口,有两个主要实现类:
-
DistroConsistencyServiceImpl: 对应 AP 模式。
-
RaftConsistencyServiceImpl: 对应 CP 模式。
java
// com.alibaba.nacos.naming.core.ServiceManager
public void registerInstance(String namespaceId, String serviceName, Instance instance) {
// 1. 根据 serviceName 获取服务定义,如果没有则创建
createServiceIfAbsent(namespaceId, serviceName, instance.isEphemeral());
Service service = getService(namespaceId, serviceName);
// 2. 检查当前服务实例是否与之前定义的模式冲突
if (service.isEphemeral() != instance.isEphemeral()) {
throw new NacosException("你的实例模式变了!不允许从 AP 突然变 CP");
}
// 3. 路由到不同的一致性协议处理器
if (instance.isEphemeral()) {
// AP 模式:放入内存 map,使用 Distro 协议异步同步
distroConsistencyService.put(key, instance);
} else {
// CP 模式:通过 Raft 协议写入,涉及磁盘 IO 和 Leader 确认
raftConsistencyService.put(key, instance);
}
}
3. Distro 协议的巧妙设计 (AP)
Nacos 的 Distro 协议不仅仅是简单的异步复制,它还引入了数据分片的概念:
-
Nacos 集群中,每个节点负责一部分服务的写操作(类似于 Hash 环)。
-
如果我不负责这个服务,但我收到了写请求,我会处理,然后同步给负责的节点。
-
新节点加入:新节点启动时,会自动找其他节点拉取全量数据,保证最终一致性。
监听服务变动
java
@Component
@Slf4j
public class ServiceListListener implements CommandLineRunner {
@Autowired
private NacosServiceManager nacosServiceManager;
@Autowired
private NacosDiscoveryProperties properties;
@Override
public void run(String... args) throws Exception {
// 获取 Nacos 原生 NamingService
NamingService namingService = nacosServiceManager.getNamingService(properties.getNacosProperties());
// 订阅 "netty-server" 服务的变化
// 这是 Nacos 的推拉结合机制:
// 1. 客户端定时 Pull
// 2. 服务端有变化发送 UDP Push 触发回调
namingService.subscribe("netty-server", event -> {
if (event instanceof NamingEvent) {
NamingEvent namingEvent = (NamingEvent) event;
List<Instance> instances = namingEvent.getInstances();
log.info("【服务变更】检测到 netty-server 实例列表变化,当前实例数: {}", instances.size());
// 遍历新列表
for (Instance instance : instances) {
if (instance.isHealthy()) {
log.info("可用实例: {}:{}", instance.getIp(), instance.getPort());
// TODO: 在这里执行:建立新连接、移除旧连接等逻辑
// updateConnectionPool(instances);
}
}
}
});
}
}
java
@Component
public class GrayRule {
@Autowired
private DiscoveryClient discoveryClient;
public ServiceInstance chooseInstance(String serviceName) {
// 1. 获取所有实例
List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
// 2. 筛选逻辑:假设当前请求头里带有 gray=true,只找 v2.0 的实例
// 真实场景通常配合 Spring Cloud Gateway Filter 实现
List<ServiceInstance> grayInstances = instances.stream()
.filter(instance -> {
String version = instance.getMetadata().get("version");
return "v2.0".equals(version);
})
.collect(Collectors.toList());
// 3. 如果有灰度实例,从中随机选一个
if (!grayInstances.isEmpty()) {
return grayInstances.get(new Random().nextInt(grayInstances.size()));
}
// 4. 降级:没有灰度实例,返回普通实例
return instances.get(0);
}
}
六、 进阶功能:Nacos 作为配置中心的"热更新"
在案例中,大促期间我们需要动态调整日志级别 或者降级开关。
1. 传统方式 vs Nacos 方式
-
传统:修改 Git -> Webhook -> Config Server -> Bus -> MQ -> 微服务。链路太长,容易断。
-
Nacos:控制台修改 -> 客户端感知。链路极短。
2. 案例
User-Service 中需要动态控制"注册功能开关":
java
@RestController
@RequestMapping("/user")
@RefreshScope // Spring Cloud Context 注解,核心!
public class UserController {
// 从 Nacos 配置中心读取值,默认值为 true
@Value("${user.register.enabled:true}")
private boolean registerEnabled;
@PostMapping("/register")
public String register(@RequestBody UserDto user) {
if (!registerEnabled) {
return "当前注册功能已关闭,请稍后再试(服务降级中)";
}
// 业务逻辑...
return "注册成功";
}
}
3. 配置中心原理:长轮询 (Long Polling)
Nacos 客户端通过客户端长轮询 (Long Polling)实时感知配置变化的
-
客户端发起 HTTP 请求给 Nacos Server,询问配置有没有变。
-
Server 端如果不立即返回,而是将请求挂起(Hold 住)30秒。
-
如果在 30秒内,配置变了,Server 立马返回结果(类似 Push 的效果)。
-
如果 30秒到了,配置没变,返回空(类似 Pull 的效果)。
-
-
客户端收到响应后,再次发起请求,周而复始。
这种方式结合了 Push 的实时性和 Pull 的轻量级,非常巧妙。
七、 总结与选型建议
|-------------|--------------|-----------|----------------------------------|
| 维度 | Eureka | Zookeeper | Nacos |
| 一致性协议 | 无 (P2P) | ZAB (CP) | Distro (AP) / Raft (CP) |
| 性能 | 中 (HTTP 短轮询) | 中 (TCP) | 高 (Nacos2.0 引入 gRPC) |
| K8s 亲和性 | 差 | 一般 | 极好 (支持 K8s Service 同步) |
| 功能丰富度 | 仅注册 | 仅注册(KV) | 注册 + 配置 + 流量管理 |
| 社区活跃度 | 停止维护 | 稳定 | 极高 (Spring Cloud Alibaba 核心) |