Chapter 04
一致性、选型与生产
前两章分别讲了临时实例的 AP 传播(Distro)和配置的近实时下发。这一章把 AP 和 CP 合到一张图上, 讲清那条脊柱到底怎么分流、横向和 Eureka / Consul / ZooKeeper / Apollo 怎么选、以及上生产前要拧紧哪些螺丝。
本章你将建立的 schema
- 双协议如何按
ephemeral分流——起点页那条脊柱在这里完整收口。 - 为什么注册中心默认选 AP,而 ZooKeeper 的 CP 反而不适合做注册中心。
- 网络分区时,AP 侧和 CP 侧分别会发生什么。
- 注册中心 vs Eureka/Consul/ZK、配置中心 vs Apollo 的选型依据。
- 生产前必须确认的几个默认值:protect-threshold、端口、版本兼容、JVM。
4.1双协议分流:脊柱收口
Nacos 把 Distro(AP)和 Raft(CP)藏在统一的一致性接口后面,按数据的 ephemeral 字段路由——临时实例走 AP,持久实例和配置走 CP。
到这里,起点页那张概念图可以补全了。一次写入进 Nacos,先看它是什么数据,再决定走哪套协议:
注册中心的使用者能容忍"服务列表稍微过时"——查到一个刚下线的实例,调用失败后重试别的就行。但不能容忍"网络分区时无法注册、无法查询"——那等于整个服务网格瘫痪。所以注册中心应当可用性优先于一致性。这正是 Eureka 团队当年反对用 ZooKeeper 做注册中心的核心论点。
ZooKeeper 是 CP(ZAB 协议):网络分区时,少数派那一侧直接不可用;而且 Leader 挂掉重新选主期间(可达秒级甚至更久),整个集群拒绝写入。对一个"随时有实例上下线要登记"的注册中心,这种停顿是灾难。Nacos 把临时实例放在 AP(Distro)正是这个权衡:宁可列表短暂不一致,也要分区时还能注册和查询。而配置、持久实例这些"错一点就出事"的数据,才放进 CP(Raft)。
4.2CAP 取舍的具体后果
同一次网络分区,AP 侧(Distro)两边都继续服务、容忍短暂不一致,CP 侧(Raft)只有多数派能写。
抽象的 CAP 落到 Nacos 上很具体。设想一个 5 节点集群被网络分区切成 3 + 2 两块:
| 数据 | 3 那一侧 | 2 那一侧 | 分区恢复后 |
|---|---|---|---|
| 临时实例(AP·Distro) | 继续注册 / 查询 | 也继续注册 / 查询 | 异步收敛,最终一致 |
| 持久实例 + 配置(CP·Raft) | 有多数派,可读可写 | 无多数派,不可写 | 本就一致,2 侧追平日志 |
很多人把"Nacos 支持 AP/CP 切换"理解成一个全局开关,调一下整个 Nacos 变 CP。错。它是按数据逐条选的:同一时刻、同一个集群里,你的临时实例在走 AP、你的配置在走 CP。理解这一点,上面这张分区表里"为什么同一次分区两类数据命运不同"就不再奇怪——因为它们从注册那一刻起就走了不同的协议。
4.3横向选型:和谁比、怎么选
注册中心看一致性模型与生态,配置中心看治理能力;Nacos 的独特卖点是"注册 + 配置二合一 + AP/CP 按数据选"。
| 方案 | 一致性 | 健康检查 / 特点 | 怎么选 |
|---|---|---|---|
| Nacos | AP 默认 / CP 可选(按 ephemeral) | 心跳 + 服务端探活;注册 + 配置二合一 | Java / Spring Cloud Alibaba 生态首选 |
| Eureka | AP(自我保护机制) | 客户端心跳;2.x 开源已停维护 | 遗留系统在用,新项目不建议 |
| Consul | CP(Raft) | 健康检查强;多数据中心 / 服务网格 | 多 DC、Mesh、非 Java 多语言 |
| ZooKeeper | CP(ZAB) | 会话 / 临时节点 | 协调服务可以;做注册中心不合适(见 §4.1) |
| 方案 | 特点 | 怎么选 |
|---|---|---|
| Nacos | 注册 + 配置二合一;长轮询 / gRPC 推送;灰度 | 已用 Nacos 做注册,配置顺手一体化 |
| Apollo | 治理强:权限 / 审计 / 三层模型 / 双 DB / 灰度完善 | 需要细粒度权限与审计治理时更胜一筹 |
| Spring Cloud Config | Git 存储,需 /refresh 或 Spring Cloud Bus 推送 | 实时性差、依赖多,渐被取代 |
| Consul KV / etcd | 通用 K/V,无配置中心 UI / 灰度 | 已重度使用它们、愿自建配置体验时 |
反过来说,什么时候不该选 Nacos:需要企业级细粒度权限和审计治理 → Apollo 更成熟;多数据中心或服务网格 mTLS → Consul;Kubernetes 原生的强一致 K/V → etcd;纯非 Java 多语言栈、又不吃 Spring Cloud Alibaba 红利 → Consul / etcd 更顺。
4.4生产前要拧紧的螺丝
Nacos 的几个默认值是为"开箱即用"调的,不是为生产调的——上线前逐个确认。
| 项 | 默认 | 风险 / 建议 |
|---|---|---|
| protect-threshold | 0 | = 永不保护。应设 0.x:健康占比过低时返回全部实例,避免流量全压到少数健康实例引发雪崩 |
| 心跳间隔 / 超时 | 5s / 15s / 30s | 上下线偏慢,可调 preserved.heart.beat.interval 等加快 |
| JVM 堆 | 单机 512m | 按服务 / 配置规模上调,配 G1 |
| 端口 | 8848 | 2.x 还需放通 9848 / 9849(主端口 +1000 / +1001) |
| 存储 | 内嵌 Derby | 集群必须外置 MySQL,并给 DB 做高可用 |
保护阈值是"健康实例占总实例的比例"。当健康占比低于阈值,Nacos 不再只返回健康实例,而是返回全部实例(含不健康的)。逻辑是:与其把全部流量挤到剩下的少数健康实例上、把它们也压垮(雪崩),不如把流量摊给所有实例、赌一部分"标记不健康"的其实还能处理。默认 0 意味着这层保护从不触发——名字像开了保护,实则裸奔。
2.x 服务端兼容 1.x 客户端;但 2.x 客户端连不上 1.x 服务端(它要走 gRPC)。所以升级顺序必须是先升服务端、再升客户端,反了直接连不上。
4.5失败模式:把前四章接起来
线上最高频的几类故障,根因都能回指到前面的某个机制——排障就是反向走这条链。
先看最经典的一条因果链——"实例已死,调用方还在打它",它正是 §1.4 的剔除时间线和 §2.4 的客户端缓存叠加出来的:
| 症状 | 根因(回指章节) | 对策 |
|---|---|---|
| 调用到已下线实例、抖动 | 剔除延迟 + 客户端缓存(§1.4 / §2.4) | 重试 + 缩短刷新 + protect-threshold |
| 扩缩容瞬间列表忽有忽无 | Distro owner 重新分配(§2.1) | 低峰分批扩缩容 |
| 发现不到服务 / 读不到配置 | namespace 填了名字非 UUID(§1.2 / §3.3) | 核对 namespace 的 UUID、group |
| 2.x 客户端连不上、8848 却通 | 9848 / 9849 未放通(§2.3) | 防火墙放通 +1000 / +1001 端口 |
| 配置改了不生效 | 缺 @RefreshScope(§3.3) | 加 @RefreshScope 或用 @ConfigurationProperties |
2.x 升级后,一个服务死活注册不上,启动日志反复刷连接失败,但你 telnet 8848 是通的。最可能是什么原因?
展开答案(先停 10 秒)
最可能是 9848 端口没放通。2.x 客户端注册走 gRPC,端口是主端口 +1000(即 9848);防火墙只开了 8848 时,HTTP 探测看着是通的,但 gRPC 连不上,于是注册失败。放通 9848 / 9849 即可。这是把 §2.3 的端口知识用在排障上。
§本章 self-check
先合上教程作答,再展开对照。
- 一句话:Nacos 的"AP/CP 可切换",切换的是什么粒度?是全局开关吗?
- 为什么注册中心通常选 AP 而非 CP?用 ZooKeeper 分区 / 选主时的行为来说明。
- 5 节点集群被分区成 3+2,临时实例(AP)和持久实例 / 配置(CP)分别会发生什么?
protect-threshold=0意味着什么?为什么生产上危险?它触发后 Nacos 返回什么?- (综合)2.x 升级后客户端连不上、但 8848 是通的,最可能的原因?升级顺序应该是服务端先还是客户端先?
答案(先做完再展开)
- 切换的是单条数据的粒度,由
ephemeral决定:临时实例 AP、持久实例 / 配置 CP。不是全局开关,同一集群两套协议同时在跑。 - 注册中心要可用性优先:能容忍列表稍旧(重试别的实例),但不能容忍分区时无法注册 / 查询。ZooKeeper 是 CP,分区时少数派不可用、选主期间整个集群拒绝写——对注册中心是灾难。
- 临时实例:3 和 2 两侧都继续注册 / 查询,可能短暂不一致,恢复后异步收敛。持久实例 / 配置:3 那侧有多数派可写,2 那侧无多数派不可写。
- = 永不触发保护。危险在于:当健康实例很少时仍只把流量发给健康的,可能把它们也压垮(雪崩)。触发后 Nacos 返回全部实例(含不健康),把流量摊开。生产设 0.x。
- 最可能 9848 端口未放通(2.x gRPC = 主端口 +1000)。升级顺序:先服务端、后客户端,因为 2.x 客户端连不了 1.x 服务端。
"既要注册中心高可用,又要配置强一致"
架构评审上有人质疑:注册中心要 AP 高可用、配置中心要 CP 强一致,这是矛盾的两个要求,得上两套中间件吧?用本教程的脊柱回应他——一套 Nacos 能不能同时满足?为什么?
提示(卡住再展开)
能,且这正是 Nacos 的设计初衷。AP / CP 不是集群级开关,而是按数据类型选(§4.1):临时实例走 Distro/AP(高可用),配置走 Raft/CP(强一致),两者在同一个集群里同时提供。所以"高可用的注册 + 强一致的配置"不矛盾——它们本来就走在两条不同的协议路径上。把图 4.1 画给他看,比说一百句都管用。