阅读时间:约 45 分钟 前置知识:熟悉微服务基本概念、了解服务注册发现的基本流程 你将收获:理解 Nacos 核心架构设计、掌握注册中心与配置中心的工作机制、深入 AP/CP 模式切换原理、理解推拉结合的实时性保障
一、为什么需要 Nacos?
在微服务架构中,服务实例的数量和位置是动态变化的。一个订单服务可能今天部署了 5 个实例,明天扩容到 20 个。服务之间如何相互发现?调用方怎么知道该把请求发给哪个 IP?配置变更了,难道要重启所有服务?
Nacos(Dynamic Naming and Configuration Service)就是为解决这些问题而生的。它的名字里藏着两个核心能力:Naming(命名服务/注册中心) 和 Configuration(配置中心)。阿里巴巴在 2018 年将其开源,目前已是 CNCF 孵化项目,在国内微服务生态中几乎成了标配。
先看一张整体架构图,建立全局认知:

graph TB
subgraph "Nacos Server 集群"
N1[Nacos Node 1<br/>AP 模式]
N2[Nacos Node 2<br/>AP 模式]
N3[Nacos Node 3<br/>AP 模式]
N1 --- N2 --- N3 --- N1
end
subgraph "服务提供者"
P1[订单服务<br/>实例 1<br/>192.168.1.10:8080]
P2[订单服务<br/>实例 2<br/>192.168.1.11:8080]
end
subgraph "服务消费者"
C1[网关服务<br/>订阅订单服务]
C2[用户服务<br/>订阅订单服务]
end
subgraph "配置管理"
CFG[(配置存储<br/>MySQL / Derby)]
end
P1 -->|1. 注册 / 心跳| N1
P2 -->|1. 注册 / 心跳| N2
C1 -->|2. 订阅 / 拉取实例列表| N1
C2 -->|2. 订阅 / 拉取实例列表| N2
N1 -->|3. 推送变更| C1
N2 -->|3. 推送变更| C2
N1 --- CFG
N2 --- CFG
图 1:Nacos 整体架构——注册中心工作流
这张图展示了 Nacos 的三个核心角色:Nacos Server 集群(服务端)、服务提供者(Provider)、服务消费者(Consumer)。服务提供者启动时向 Nacos 注册,消费者从 Nacos 订阅服务列表,当实例发生变化时 Nacos 主动推送通知。配置中心的流程类似,只是"注册"变成了"发布配置","订阅"变成了"监听配置变更"。
二、注册中心:服务如何被发现
2.1 数据模型
Nacos 的注册中心数据模型非常简洁,核心只有三个层级:
Namespace(命名空间)→ Group(分组)→ Service(服务)→ Instance(实例)
- Namespace:实现多租户隔离。不同的 Namespace 之间服务完全不可见。生产环境、测试环境用不同的 Namespace 隔离。
- Group:同一个 Namespace 内的进一步分组。比如"交易"分组和"营销"分组。
- Service:一个微服务,比如
order-service。 - Instance:服务的一个具体实例,包含 IP、端口、权重、健康状态等元数据。
最妙的是,Nacos 用同一个 Namespace/Group/Service 的三级结构来组织注册中心和配置中心的数据,这种设计统一性极大降低了使用者的心智负担。
2.2 注册流程
服务实例注册到 Nacos 的完整流程:

sequenceDiagram
participant P as 服务提供者
participant N as Nacos Server
participant C as 服务消费者
participant DB as 持久化存储
P->>N: POST /nacos/v1/ns/instance<br/>注册实例 (ip, port, serviceName...)
N->>N: 写入内存注册表
N->>DB: 异步持久化到 MySQL
N-->>P: 注册成功
loop 每 5 秒
P->>N: PUT /nacos/v1/ns/instance/beat<br/>发送心跳
N->>N: 更新 lastBeat 时间戳
N-->>P: 心跳确认
end
Note over N: 15 秒未收到心跳 → 标记不健康<br/>30 秒未收到心跳 → 摘除实例
C->>N: GET /nacos/v1/ns/instance/list<br/>获取服务实例列表
N-->>C: 返回实例列表
N->>C: UDP 推送 (或 gRPC 长连接推送)<br/>通知实例变更
图 2:Nacos 注册中心完整交互流程
关键点有三个:
(1)临时实例 vs 持久化实例
Nacos 把实例分为两类:
| 特性 | 临时实例(ephemeral=true,默认) | 持久化实例(ephemeral=false) |
|---|---|---|
| 健康检查 | 客户端主动上报心跳 | 服务端主动探测 |
| 心跳机制 | HTTP 心跳(5s 间隔) | 服务端 TCP/HTTP/Mysql 探测 |
| 摘除机制 | 心跳超时自动摘除 | 不会自动摘除,需人工操作 |
| 典型场景 | 微服务 Pod/容器 | DNS、数据库等基础设施 |
临时实例适用于 K8s Pod 这类生命周期短、频繁变化的场景;持久化实例适用于数据库、MQ 这类相对稳定的基础设施服务。这个设计让 Nacos 既能管理微服务,也能做 DNS 服务发现。
(2)心跳保活
客户端每 5 秒发送一次心跳,Nacos 服务端在 15 秒内没收到心跳就把实例标记为"不健康",30 秒内没收到就彻底摘除。这个时间窗口是可配置的,但默认值在大多数场景下都够用。
(3)服务端健康检查的并发控制
Nacos 2.x 引入了健康检查的任务分片机制。当集群中有多个 Nacos 节点时,健康检查任务会在节点间均匀分配,避免所有节点都去探测同一个实例,减少不必要的网络开销。
2.3 服务发现的推拉模型
这是 Nacos 最精妙的设计之一。它没有选择纯推(Push)或纯拉(Pull),而是推拉结合:

graph LR
subgraph "拉模型 (Pull)"
A1[客户端定期轮询] --> A2[服务端返回最新数据]
A2 --> A3[简单可靠但实时性差]
end
subgraph "推模型 (Push)"
B1[服务端主动推送] --> B2[客户端收到变更通知]
B2 --> B3[实时性高但实现复杂]
end
subgraph "Nacos 推拉结合"
C1[客户端首次订阅] --> C2[全量拉取实例列表]
C2 --> C3[建立长连接/UDP 通道]
C3 --> C4[服务端变更时推送 diff]
C4 --> C5[客户端本地合并更新]
C5 --> C6[定期全量对账保证最终一致]
end
图 3:Nacos 推拉结合模型对比纯推/纯拉
具体的工作机制:
第一阶段:全量拉取。 客户端首次订阅服务时,向 Nacos 发起一次全量拉取,获取当前所有健康实例的列表。这是"拉"的部分。
第二阶段:变更推送。 Nacos 2.x 通过 gRPC 长连接向客户端推送变更事件。当服务实例上线、下线、权重变化时,Nacos 立即通过这个长连接将变更推送给所有订阅该服务的客户端。这是"推"的部分。
第三阶段:定期对账。 为了防止推送丢失(网络抖动、客户端重启等),客户端会定期(默认 10 秒)发起一次轻量级查询,用 MD5 校验和比对本地缓存与服务端数据是否一致。如果不一致,就重新全量拉取。这是"拉"的兜底部分。
这种设计在实时性和可靠性之间取得了很好的平衡。用行话来说就是:"推"保证实时性,"拉"保证最终一致性。
2.4 Nacos 1.x vs 2.x 的通信模型演进
Nacos 1.x 的通信模型相对简单但存在不少问题:
| 方面 | Nacos 1.x | Nacos 2.x |
|---|---|---|
| 客户端-服务端通信 | HTTP + UDP | gRPC(双向流)+ HTTP |
| 服务发现推送 | UDP 推送(不可靠) | gRPC 双向流推送(可靠) |
| 连接管理 | 无连接状态,每次 HTTP 请求独立 | 长连接管理,连接复用 |
| 心跳机制 | HTTP 短连接心跳 | gRPC 长连接心跳 |
| 性能瓶颈 | 1.x 服务端压测 HTTP 连接数受限 | 2.x 单节点可支撑 10 万+ 客户端 |
- x 时代最大的痛点是用 UDP 推送变更通知。UDP 不保证送达,丢包了就丢了,客户端只能靠定期轮询来兜底——这导致通知延迟可能高达数秒。2.x 引入 gRPC 双向流后,推送变成了可靠的流式传输,实时性大幅提升。
三、配置中心:配置如何动态生效
3.1 配置中心的本质问题
你有没有经历过这样的场景:线上突然需要调整某个开关,或者限流阈值需要紧急下调,但改配置就得重启服务?重启意味着服务中断,哪怕只有几十秒,在核心链路上也是不可接受的。
配置中心解决的就是这个问题:让配置变更在不重启的情况下实时生效。
3.2 配置模型
Nacos 配置中心的数据模型:
Namespace → Group → Data ID(配置项)
- Data ID:配置的唯一标识,通常格式为
${applicationName}-${profile}.${extension},比如order-service-dev.yaml。 - Group:配置分组,默认
DEFAULT_GROUP。 - Namespace:租户隔离,和注册中心共用。
3.3 配置发布与监听流程

sequenceDiagram
participant Admin as 运维/开发
participant N as Nacos Server
participant DB as MySQL
participant App1 as 应用实例 A
participant App2 as 应用实例 B
Admin->>N: 发布配置<br/>(dataId, group, content)
N->>DB: 持久化配置内容
N->>DB: 记录配置历史版本
N-->>Admin: 发布成功
App1->>N: 建立 gRPC 长连接<br/>注册配置监听 (dataId, group, MD5)
App2->>N: 建立 gRPC 长连接<br/>注册配置监听 (dataId, group, MD5)
N->>N: 检测 MD5 变化
N->>App1: 推送配置变更通知<br/>(dataId, group, new MD5)
N->>App2: 推送配置变更通知<br/>(dataId, group, new MD5)
App1->>N: 拉取最新配置内容
App2->>N: 拉取最新配置内容
App1->>App1: 本地应用新配置<br/>(刷新 Bean / 重新加载)
App2->>App2: 本地应用新配置<br/>(刷新 Bean / 重新加载)
图 4:Nacos 配置中心发布与监听全流程
3.4 配置变更的回滚与审计
配置中心不只是"存配置"和"推送配置"。企业级配置中心的核心价值在于变更管控:
- 版本管理:每次配置变更都会生成一个新版本,可以在历史版本列表里查看任意历史版本的内容。
- 一键回滚:如果新配置上线后发现问题,可以一键回滚到任意历史版本。回滚本质上就是一次"发布历史版本内容"的操作。
- 变更审计:谁在什么时间改了什么配置,从哪个值改成了哪个值,都会被记录下来。
- 灰度发布:可以指定配置只对部分 IP 生效,先在少量实例上验证,再全量发布。
3.5 配置的动态刷新机制
Spring Cloud 应用中,@RefreshScope 注解标记的 Bean 会在配置变更后被重新初始化。底层原理是:
- Nacos 客户端通过 gRPC 长连接收到配置变更通知。
- 客户端拉取新的配置内容,更新本地缓存。
- 发布一个
RefreshEvent事件。 - Spring Cloud 的
RefreshScope监听该事件,销毁被@RefreshScope标记的 Bean 的缓存。 - 下次访问该 Bean 时,Spring 会用新的配置值重新创建它。
这个机制的关键在于:不是热替换对象,而是销毁旧对象,延迟创建新对象。这意味着 Bean 不需要是线程安全的,因为不存在并发修改的问题。
四、AP 模式与 CP 模式:Nacos 的一致性取舍
4.1 CAP 定理回顾
在分布式系统中,CAP 定理告诉我们在一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)之间最多只能同时满足两个。由于网络分区在分布式系统中不可避免,P 必须被满足,所以实际选择是:CP 还是 AP?
- CP(一致性 + 分区容错):发生网络分区时,牺牲可用性来保证数据一致性。宁可暂时不可用,也不能返回错误数据。
- AP(可用性 + 分区容错):发生网络分区时,牺牲一致性来保证可用性。宁可返回可能过时的数据,也不能让服务不可用。
Nacos 的独特之处在于:它同时支持 AP 和 CP 两种模式,并且可以在注册中心和配置中心之间独立切换。
4.2 注册中心:默认 AP,可切换 CP
注册中心的核心诉求是可用性优先。原因很简单:服务发现是微服务调用的入口,如果注册中心因为网络分区而拒绝服务,整个微服务体系就瘫痪了。即使返回的实例列表略有过时(比如包含了一个刚挂掉的实例),调用方可以通过重试和熔断来兜底,远比直接不可用要好。
Nacos 注册中心的 AP 实现基于 Distro 协议(阿里巴巴自研的分布式协议):
┌──────────────────────────────────────────────────┐
│ Nacos 注册中心 — Distro 协议(AP 模式) │
├──────────────────────────────────────────────────┤
│ 核心思想:每个节点负责一部分数据,独立处理写入 │
│ │
│ 写入流程: │
│ 1. 请求到达任意节点 │
│ 2. 该节点判断自己是否为数据"负责人" │
│ 3. 如果是负责人:直接写入,异步同步给其他节点 │
│ 4. 如果不是负责人:转发给负责节点处理 │
│ │
│ 一致性保证: │
│ - 写入:本地先落盘,异步同步(最终一致) │
│ - 读取:从本地内存直接返回(不保证强一致) │
│ - 对账:定期 checksum 比对,发现不一致就全量同步 │
└──────────────────────────────────────────────────┘
Distro 协议的设计哲学是:数据分片 + 独立决策 + 最终一致。每个 Nacos 节点负责一部分服务数据,对这部分数据有完全的写入权限,不需要和其他节点协商。这样即使集群中只剩一个节点,注册和发现功能依然可用。

图 6:Nacos Distro 协议——AP 模式下的数据分片与独立决策机制
当注册中心切换到 CP 模式时,底层使用 JRaft(Raft 协议的 Java 实现)。这时所有写入操作需要通过 Raft 日志复制得到多数节点确认后才能返回。可用性会降低,但数据一致性得到保证。
4.3 配置中心:默认 CP,不可切换
配置中心的核心诉求是一致性优先。配置一旦发布,必须保证所有实例最终看到的是同一份配置。如果配置中心在分区时返回了不一致的配置,不同实例可能行为不一致,导致线上故障。
配置中心使用 JRaft 实现 CP 模式:

graph TB
subgraph "Nacos 配置中心 — JRaft 协议(CP 模式)"
direction TB
L[Leader] -->|日志复制| F1[Follower 1]
L -->|日志复制| F2[Follower 2]
F1 -->|确认| L
F2 -->|确认| L
subgraph "写入流程"
W1[客户端发起配置变更] --> W2[请求路由到 Leader]
W2 --> W3[Leader 写入本地日志]
W3 --> W4[Leader 将日志复制到 Follower]
W4 --> W5[等待多数节点确认]
W5 --> W6[Leader 提交日志并返回成功]
end
end
subgraph "持久化层"
DB[(MySQL<br/>配置持久化)]
end
L --- DB
F1 --- DB
F2 --- DB
图 5:Nacos 配置中心的 Raft 一致性协议
配置的写入必须经过 Raft 共识:Leader 将写操作封装为日志条目,复制到 Follower 节点,等待超过半数的节点确认后,才认为写入成功。如果 Leader 宕机,剩下的 Follower 会发起新的选举,保证集群继续对外提供服务。
4.4 为什么这样设计?
| 场景 | 一致性要求 | 可用性要求 | 选择 |
|---|---|---|---|
| 服务注册与发现 | 强一致性不是必须的,短暂不一致可接受 | 必须高可用,否则调用链断裂 | AP |
| 配置管理 | 必须一致,否则不同实例行为不一致 | 短暂不可用可接受 | CP |
这种"同一套系统,不同场景不同一致性策略"的设计非常务实,避免了"一刀切"带来的问题。这也是为什么 Nacos 能在注册中心和配置中心两个领域同时做到优秀的原因。
五、Nacos 集群架构与高可用
5.1 集群部署模式
Nacos 推荐的生产部署架构:
┌──────────────┐
│ Nginx/ALB │
│ (VIP: 80) │
└──────┬───────┘
┌─────────────┼─────────────┐
│ │ │
┌──────▼──────┐ ┌───▼──────┐ ┌────▼──────┐
│ Nacos Node 1│ │Nacos N.2 │ │Nacos N.3 │
│ 192.168.1.1 │ │192.168.1.│ │192.168.1.3│
└──────┬──────┘ └────┬─────┘ └─────┬─────┘
│ │ │
└─────────────┼─────────────┘
│
┌──────▼──────┐
│ MySQL │
│ (主从/集群) │
└─────────────┘
关键点:
- Nacos 节点间通过 gRPC 通信(2.x),负责数据同步和健康检查任务分配。
- MySQL 集群作为持久化存储,所有 Nacos 节点共享同一个数据库。这不是"分布式存储"方案,而是"共享存储"方案——Nacos 节点本身不存储数据,数据一致性由 MySQL 保证。
- 使用 Nginx 或云负载均衡器做流量分发,客户端只需知道 VIP 地址。

图 7:Nacos 集群高可用部署——节点间数据同步与故障转移
5.2 Nacos 节点挂了怎么办?
Nacos 的容错机制分两层:
第一层:客户端容错。 Nacos 客户端(Java SDK)在本地维护了一份服务实例的缓存快照。即使 Nacos 服务端全部宕机,客户端依然可以使用本地缓存的服务列表继续调用——虽然不能感知到新的实例变更,但已有的调用链路不会中断。
第二层:服务端容错。 如果 Nacos 集群中部分节点宕机:
- 注册中心(AP 模式):只要有一个节点存活,注册和发现功能就正常。Distro 协议保证每个节点独立决策。
- 配置中心(CP 模式):需要过半节点存活才能写入。如果 3 节点集群挂了 2 个,可以读但不能写。
六、源码视角:Nacos 的关键数据结构
6.1 注册表——ServiceManager
Nacos 注册中心的核心数据结构是 ServiceManager,它维护了一个双层 ConcurrentHashMap:
// 简化版核心结构
public class ServiceManager {
// 第一层 Key: namespace, 第二层 Key: group::serviceName
private final ConcurrentHashMap<String, ConcurrentHashMap<String, Service>> serviceMap;
// Service 内部维护实例列表
public class Service {
private String name;
// 集群 -> 实例列表
private ConcurrentHashMap<String, Cluster> clusterMap;
}
public class Cluster {
private String name;
// 持久化实例 (ephemeral=false)
private Set<Instance> persistentInstances;
// 临时实例 (ephemeral=true)
private Set<Instance> ephemeralInstances;
}
}
这个设计的精妙之处:
- 全内存:所有注册信息都存储在 JVM 内存中,查询 O(1) 复杂度,不需要访问数据库。
- ConcurrentHashMap:读写并发安全,读操作不加锁,写操作只锁对应分段。
- MySQL 异步持久化:实例注册写入内存后立即返回,异步写入 MySQL 防止数据丢失。即使 MySQL 暂时不可用,注册中心也不受影响。
6.2 一致性协议切换——EphemeralConsistencyService
协议切换的关键是 ConsistencyService 接口:
public interface ConsistencyService {
// 写入操作
void put(String key, Record value);
// 读取操作
Record get(String key);
// 监听变更
void listen(String key, RecordListener listener);
}
Nacos 在启动时根据配置和数据类型选择不同的实现:
- 临时实例数据 →
DistroConsistencyServiceImpl(AP 模式) - 持久化实例数据 →
RaftConsistencyServiceImpl(CP 模式,JRaft) - 配置数据 →
PersistentConsistencyServiceDelegateImpl(CP 模式,JRaft)
这个策略模式的设计让 Nacos 可以在不修改业务代码的情况下,在不同一致性协议之间切换。
七、实践要点与常见问题
7.1 Nacos vs Eureka vs Consul vs ZooKeeper
很多面试会问 Nacos 和其他注册中心的对比:
| 特性 | Nacos | Eureka | Consul | ZooKeeper |
|---|---|---|---|---|
| CAP 模型 | AP/CP 可切换 | AP | CP | CP |
| 配置中心 | ✅ 内置 | ❌ 需配合 Spring Cloud Config | ✅ 内置 | ❌ 需配合其他方案 |
| 健康检查 | 客户端心跳 + 服务端探测 | 客户端心跳 | 服务端探测 | 基于 Session 心跳 |
| 一致性协议 | Distro(AP)/ Raft(CP) | 无一致协议 | Raft | ZAB |
| 通信协议 | gRPC(2.x) | HTTP | HTTP/DNS/gRPC | TCP |
| 多数据中心 | ✅ | ❌ | ✅ | ❌ |
| 控制台 | 功能完善的 Web 控制台 | 基本控制台 | Web UI | 需第三方工具 |
| 社区活跃度 | 高(阿里巴巴主导) | 低(已停更) | 中 | 高(Apache 项目) |

图 8:主流注册中心全景对比——Nacos、Eureka、Consul、ZooKeeper 架构差异
7.2 如何选择注册中心?
- 纯 Spring Cloud 技术栈 → Nacos:与 Spring Cloud Alibaba 深度集成,注册+配置一站式解决。
- 已有 ZooKeeper 运维经验,且不需要配置中心 → ZooKeeper:成熟稳定,但复杂度高。
- 多数据中心、需要 Service Mesh 集成 → Consul:多数据中心支持最好。
- Eureka:已停止维护,不建议新项目使用。
7.3 常见坑
(1)Nacos 1.x 的 UDP 推送丢失
在 1.x 版本中,如果客户端和 Nacos 服务端之间网络不稳定,UDP 推送可能丢失,导致客户端感知不到服务变更。解决方案:升级到 2.x 使用 gRPC 长连接,或者缩短客户端的轮询间隔。
(2)配置中心并发写入冲突
多个管理员同时对同一个 Data ID 进行修改时,后提交的会覆盖先提交的。Nacos 的配置管理界面没有"编辑锁"机制,需要从流程上规范——同一时间只能有一个人编辑生产配置。
(3)权重配置的误解
Nacos 实例的权重范围是 0~1(浮点数),0 表示不接收流量,1 表示正常权重。这不是"几台机器的权重比",而是"单台机器接收流量的比例"。如果需要实现 1:2:3 的流量分配,理解为三台机器分别接收 100%、200%、300% 的流量是不对的;实际上 Nacos 的权重是客户端负载均衡的参考值,最终效果取决于负载均衡算法的实现。
(4)保护阈值(Protect Threshold)
Nacos 有一个"保护阈值"的概念,默认 0(关闭)。当健康实例占总实例的比例低于这个阈值时,Nacos 会停止摘除不健康实例,防止"所有实例都被摘除导致服务完全不可用"的雪崩效应。但很多人不知道这个功能,在线上因网络抖动导致大量实例被摘除时,不会利用这个保护机制。
八、总结
Nacos 的设计哲学可以用三句话概括:
- 实用主义:注册中心用 AP 保证高可用,配置中心用 CP 保证数据一致,不追求理论上的一致性洁癖,而是根据实际场景做取舍。
- 推拉结合:首次全量拉取 + 变更增量推送 + 定期对账兜底,三层机制保证数据既实时又可靠。
- 统一抽象:用同一套 Namespace/Group/Data 模型管理注册和配置,降低使用复杂度。
理解和掌握 Nacos 的原理,不只是为了应对面试,更是为了在实际工作中能做出正确的架构决策——知道什么时候该用 AP 模式,什么时候该用 CP 模式,什么时候该用临时实例,什么时候该用持久化实例。这些决策直接影响着系统的稳定性和可用性。
面试官视角:请回答
以下问题从面试官视角出发,考察你对 Nacos 原理的深入理解和实际工程经验。尝试在不查阅资料的情况下回答。
Q1:Nacos 注册中心默认是 AP 模式,如果某个服务实例挂了,消费者多久能感知到?请从心跳机制、推送机制、对账机制三个层面分析延迟,并说明最坏情况下的延迟是多少。
点击查看参考答案
从三个层面分析:
- 心跳超时:临时实例客户端每 5 秒发送心跳,Nacos 15 秒未收到心跳标记不健康,30 秒未收到摘除。所以从实例挂掉到被 Nacos 摘除,最坏情况是 30 秒。
- 推送延迟:Nacos 2.x 使用 gRPC 长连接推送变更,摘除动作发生后,推送几乎是实时的(毫秒级)。Nacos 1.x 使用 UDP 推送,可能丢包,延迟取决于轮询间隔。
- 对账延迟:客户端每 10 秒(默认)做一次 MD5 对账,如果推送丢失,最坏情况 10 秒后对账发现不一致并重新拉取。
最坏情况:假设实例在心跳周期刚开始时挂掉,心跳 30 秒超时摘除 + 推送丢失 + 10 秒后对账发现,总共约 40 秒。这是没有调优默认参数的情况。实际生产中可以调小 nacos.naming.clean.empty-service.interval 等参数来缩短。
要注意的是,消费者调用这个已挂实例时,业务层面的熔断和重试会比 Nacos 摘除更快生效——Nacos 的摘除更多是"防止新请求被路由到已挂实例",而不是"熔断"的替代。
Q2:Nacos 配置中心使用 Raft 协议保证一致性。如果 3 节点 Nacos 集群中 Leader 挂了,配置变更会中断多久?新的 Leader 选举期间,客户端能读到配置吗?
点击查看参考答案
- 中断时间:Raft 的 Leader 选举超时时间默认在 150ms~300ms 之间(随机),选举过程本身通常在几十毫秒内完成。所以配置写入的中断时间通常在 200ms~500ms 之间。加上客户端重试(Nacos SDK 内置重试),对业务几乎无感知。
- 选举期间能否读取:可以。Raft 的读操作可以由 Follower 处理(如果开启了 ReadIndex 或 Lease Read 优化),即使没有 Leader,Follower 也能返回上次已提交的配置。配置读是"无状态"的,不需要经过 Leader。所以配置读取不会中断。
- 极端情况:如果 3 节点挂了 2 个,只剩 1 个节点,既无法选举出 Leader(达不到多数),也无法提交新的写入。此时配置可读不可写。这个场景下,Nacos 不脑裂,宁可牺牲可用性也要保证一致性——这正是 CP 模式的体现。
Q3:Nacos 2.x 为什么从 HTTP+UDP 改为 gRPC?从协议层面分析 gRPC 相比 HTTP+UDP 在服务发现场景下的优势。
点击查看参考答案
gRPC 相比 HTTP+UDP 的核心优势:
- 双向流(Bidirectional Streaming):HTTP 是请求-响应模型,服务端无法主动推送;UDP 虽然可以推送但不可靠。gRPC 基于 HTTP/2,天然支持双向流,服务端可以主动推送变更,客户端可以复用同一条连接发送心跳和接收推送。
- 连接复用:HTTP/1.1 是短连接模型,每次心跳都要重新建立 TCP 连接(或受限于连接池大小)。gRPC 使用长连接,一条连接承载所有通信,大幅减少连接开销,单节点可支撑的客户端数量从万级提升到十万级。
- 可靠性:UDP 不保证送达,丢包后需要客户端轮询兜底。gRPC 基于 TCP,推送是可靠的,不需要额外的对账机制来弥补推送丢失。
- 流控和多路复用:HTTP/2 的多路复用允许多个 Stream 在同一条 TCP 连接上并发传输,互不阻塞,解决了 HTTP/1.1 的队头阻塞问题。
- 序列化效率:gRPC 默认使用 Protobuf 序列化,比 JSON 体积更小、解析更快,在高频心跳场景下带宽优势明显。
但也要注意 gRPC 的代价:长连接需要服务端维护连接状态,连接数多时内存开销大;某些负载均衡器对 HTTP/2 长连接的支持不如 HTTP/1.1 成熟。
Q4:设计一个支持 10 万+ 实例的注册中心,需要考虑哪些核心问题?请从数据分片、一致性、推送效率、容灾几个维度给出设计方案。
点击查看参考答案
这是一个系统设计题,考察架构思维:
1. 数据分片
- 按服务名做一致性哈希分片,每个节点负责一部分服务数据。
- 类似 Nacos 的 Distro 协议思路:每个节点对负责的数据有写入权,对其他分片的数据只有只读副本。
- 分片策略要支持动态 rebalance(节点上下线时数据迁移)。
2. 一致性
- 注册中心用 AP 模型(最终一致),不要求强一致。
- 写操作:本地写入 + 异步同步副本(类似 Gossip 或 Distro)。
- 对账机制:定期 checksum 比对,发现不一致做全量或增量同步。
- 配置中心单独部署,用 Raft 保证 CP。
3. 推送效率
- 使用长连接(gRPC/WebSocket),避免轮询。
- 推送时只发送变更 diff,不全量推送。
- 按服务名做推送隔离:某个服务的变更只推送给订阅该服务的客户端,不广播给所有客户端。
- 推送合并:短时间内同一服务的多次变更合并为一次推送。
4. 容灾
- 客户端本地缓存兜底,服务端全挂也能用。
- 多机房部署,每个机房有独立集群,机房间异步同步。
- 灰度发布配置变更,先在少量实例上验证。
- 配置变更支持一键回滚。
5. 性能
- 全内存存储,注册表不落盘(MySQL 只做异步持久化)。
- 读写分离:读从内存直接返回,写异步落盘。
- 批量注册 API,减少网络往返。
- 连接数和内存的平衡:单节点 10 万连接需要约 10-20GB 内存。
本文是「Java 技术专家与架构师面试:250 个高频考点总览」系列的第 178 篇。下一篇预告:179|ZooKeeper 原理:ZAB、临时节点、Watcher、一致性与典型应用。