Nacos
Nacos 笔记
目录
1. 配置动态更新原理
一句话概括
Nacos 用长轮询实现配置热更新:客户端问服务端”配置变了吗?”,服务端说”没变,我等 30 秒再告诉你”,这 30 秒内一旦有变更,服务端立刻通知客户端。
什么是长轮询?
普通轮询:客户端每隔 30 秒问一次”配置变了吗?”,服务端每次都回答。浪费资源。
长轮询:客户端问一次”配置变了吗?”,服务端回答”没变,但我先不挂电话,等 30 秒,这期间有变更我马上告诉你”。
graph TD
A[客户端:配置变了吗?] --> B[服务端]
B --> C{30 秒内有变更?}
C -->|有| D[立刻返回变更内容]
C -->|没有| E[超时返回]
D --> F[客户端拿到新配置]
E --> G[客户端重新发起请求]
默认超时时间:30 秒(29.5 秒等待 + 0.5 秒处理)
怎么判断配置有没有变?
核心思路:对比 key + md5
每个配置项都有一个 md5 指纹(类似文件的”数字签名”)。客户端把自己的 key + md5 列表发给服务端,服务端做三件事:
| 情况 | 客户端 | 服务端 | 结果 |
|---|---|---|---|
| 修改 | 有 key,md5 旧 | 有 key,md5 新 | md5 不同 → 拉取新值 |
| 新增 | 没有这个 key | 有这个 key | 服务端发现客户端缺少 → 通知客户端去拉 |
| 删除 | 有这个 key | 没有这个 key | 服务端发现客户端多了 → 通知客户端移除 |
类比:两本书内容是否一样,不用逐字对比,只看 ISBN 号就行。md5 就是配置内容的”ISBN 号”。
配置很多怎么办?性能优化
假设有 10 万个配置项,全部比对一次会很慢。Nacos 做了两个优化:
优化 1:分批比对(分片)
把 10 万个配置分成多批,每批最多 3000 个,一批一批去比对。
类比:考试阅卷,一个老师改 10 万份卷子太慢,分成多个小组每组 3000 份,并行处理。
优化 2:两步走(先对答案,再发卷子)
第一步:只对答案
客户端把 3000 个配置的 key + md5 打包发给服务端,服务端只返回”哪些 key 的 md5 变了”。
这一步数据包很小,只传 key 和 md5,不传完整的配置内容。
第二步:再发卷子
客户端拿到”变了哪些 key”的列表,再逐个去服务端拉取这些 key 的最新值。
只拉取真正变更的配置,不浪费带宽。
核心目的:把一次大通信拆成多次小通信,减少网络压力。
总结
| 机制 | 作用 |
|---|---|
| 长轮询 | 减少轮询次数,有变更立刻通知 |
| 分片比对 | 配置多时分批处理,避免一次比对太慢 |
| 两步更新 | 先确认哪些变了,再拉取变更内容,减少数据传输量 |
三者配合,实现低延迟、低开销的配置动态更新。
2. 临时与永久、AP 与 CP
一句话概括
Nacos 是微服务的”大管家”,通过心跳报平安和主动查房两种方式管理服务的健康状态,并根据业务场景选择要可用(AP)还是要一致(CP)。
什么是 CAP?
CAP 理论说,分布式系统最多只能同时满足三个中的两个:
| 字母 | 含义 | 大白话 |
|---|---|---|
| C (Consistency) | 一致性 | 所有人看到的数据都一样 |
| A (Availability) | 可用性 | 系统随时都能响应请求 |
| P (Partition tolerance) | 分区容错 | 网络断了也能继续工作 |
类比:一个连锁超市,总店和分店之间网络断了(P 发生了):
- 选 CA:等网络恢复再营业 → 不可用
- 选 AP:分店继续卖货,但库存数据可能不一致 → 不一致
- 选 CP:分店暂停营业,等数据同步 → 不可用
Nacos 的选择:AP 或 CP 可以切换,P 是必须的(分布式系统网络总会出问题)。
临时实例 vs 永久实例:两种健康检查方式
临时实例(Ephemeral):客户端主动”报平安”
机制:服务实例每隔 5 秒向 Nacos 发一次心跳。
- 超过 15 秒没收到 → 标记为”不健康”
- 超过 30 秒没收到 → 直接从列表中踢掉
适用场景:频繁上下线、动态扩缩容的微服务(如 Spring Cloud 业务服务)。服务本身是活的,能自己发心跳。
graph LR
A[服务实例] -->|每 5 秒心跳| B[Nacos]
B -->|15 秒没收到| C[标记不健康]
B -->|30 秒没收到| D[直接剔除]
永久实例(Persistent):服务端主动”查房”
机制:服务实例不发心跳。Nacos 服务端主动去探测它(支持 HTTP、TCP 等协议)。
- 探测失败 → 标记为”不健康”,但不会删除
适用场景:数据库、DNS 等不能自己发心跳,且需要长期稳定存在的基础组件。
graph LR
A[Nacos] -->|主动探测| B[服务实例]
B -->|探测失败| C[标记不健康]
B -->|探测成功| D[保持健康]
对比
| 特性 | 临时实例 | 永久实例 |
|---|---|---|
| 健康检查 | 客户端发心跳 | 服务端主动探测 |
| 不健康时 | 标记 + 最终剔除 | 只标记,不剔除 |
| 适用场景 | 业务微服务 | 数据库、DNS 等基础组件 |
| 默认值 | ephemeral = true | ephemeral = false |
AP 模式 vs CP 模式:大管家的两种管理哲学
AP 模式:要可用,允许短暂不一致
特点:优先保证系统高可用。哪怕网络故障,服务依然可以注册和被发现。牺牲强一致性,允许不同节点上的服务列表有短暂延迟。
绑定规则:只支持临时实例。因为临时实例靠心跳保活,即使偶尔写失败,后续心跳还能补偿上来。
类比:微信群消息,有人断网了,其他人继续聊,等断网的人回来再同步消息。
CP 模式:要一致,宁可暂时不可用
特点:优先保证数据一致性。任何节点读取到的服务列表必须是同一版本。但如果发生网络分区,为了保证不出错,Nacos 可能会拒绝部分写入请求,导致部分服务暂时不可用。
绑定规则:支持永久实例。因为永久实例不发心跳,如果第一次注册失败,数据就丢了,所以必须保证强一致性。
类比:银行转账,必须等两边都确认才算成功,宁可让你等一下,也不能转错。
对比
| 特性 | AP 模式 | CP 模式 |
|---|---|---|
| 优先保证 | 可用性(A) | 一致性(C) |
| 网络故障时 | 继续服务,数据可能短暂不一致 | 可能拒绝请求,等数据同步 |
| 支持的实例类型 | 临时实例 | 永久实例 |
| 底层协议 | Distro(阿里自研) | Raft(经典算法) |
| 性能 | 高(异步广播,不阻塞) | 较低(需多数派确认) |
底层是怎么实现的?双协议架构
Nacos 能同时支持 AP 和 CP,是因为内部用了两套一致性协议:
AP 模式:Distro 协议(阿里自研)
- 去中心化:每个节点负责一部分数据,地位平等
- 异步广播:写入后异步通知其他节点,读写都不阻塞
- 性能极高:适合高并发场景
类比:多个收银员各自负责一部分商品,顾客随便找谁结账,事后对账就行。
CP 模式:Raft 协议(经典算法)
- 有 Leader:写请求必须由 Leader 处理
- 多数派确认:Leader 同步给多数节点确认后才算成功
- 数据绝不丢失:保证强一致性
类比:公司报销必须老板签字,老板不在就等着,不能别人代签。
怎么切换模式?
默认情况:Nacos 默认是 AP 模式,注册的实例默认是临时实例(ephemeral=true)。
切换为 CP 模式:在客户端注册时,将 ephemeral 设置为 false:
1
2
3
4
5
spring:
cloud:
nacos:
discovery:
ephemeral: false # false = 永久实例,底层自动走 CP(Raft) 协议
全局切换(不常用):通过 Nacos OpenAPI 强制切换整个集群的运行模式:
1
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
总结
| 概念 | 作用 |
|---|---|
| 临时实例 | 客户端心跳保活,适合业务微服务 |
| 永久实例 | 服务端主动探测,适合基础组件 |
| AP 模式 | 要可用,允许短暂不一致(Distro 协议) |
| CP 模式 | 要一致,宁可暂时不可用(Raft 协议) |
| ephemeral 参数 | 控制实例类型,自动选择对应模式 |
一句话:Nacos 通过心跳(临时)和主动探测(永久)来检查健康;通过底层的 Distro(AP)和 Raft(CP)协议来保证数据一致性;业务中只需配置 ephemeral 参数,Nacos 自动选择最合适的模式。
3. 网关改 Nacos 配置后全部报 500「网关内部错误」
一句话概括
网关是 WebFlux 响应式应用,Nacos 配置一改,Spring Cloud 就会触发”全量刷新“(把所有 Bean 销毁重建)。这个操作在普通业务服务上很安全,但会把正在高速运转的网关直接”刷坏”,导致所有请求 500「网关内部错误」,重启网关才恢复。
为什么业务服务没事,网关却会被刷坏?
类比:普通餐厅 vs 流水线工厂
- 业务服务(Spring MVC):像普通餐厅。客人(请求)来了,服务员一对一服务,端菜、结账都是阻塞式的,走了一个再来下一个。这时把厨房重新装修一下(全量刷新),问题不大。
- 网关(WebFlux):像高速流水线工厂。零件(请求)一刻不停地流过,机器(过滤器链、路由表)正在高速运转。你一边运转一边把机器拆了重装,正在流水线上的零件自然全部卡住、报废(500)。
本质区别:MVC 是”一个请求占一个线程”的阻塞模型,重建 Bean 时没有请求在”半路”;WebFlux 是事件驱动的非阻塞模型,请求随时都在管线里流转,全量刷新时组件处于”拆一半”的中间状态,在途请求全部出错。
发生了什么?刷新流程
graph LR
A[改 Nacos 配置] --> B[Nacos 长轮询通知网关]
B --> C[Spring Cloud 发布刷新事件]
C --> D[全量刷新:销毁并重建所有 Bean]
D --> E[网关核心组件处于拆一半状态]
E --> F[在途请求全部 500 网关内部错误]
F --> G[重启网关:组件重新装配,恢复]
现在是怎么解决的?
原则:网关不做”全量刷新”,只做”定向刷新”。
| 措施 | 说明 |
|---|---|
| 关闭全量刷新 | 网关配置里加了 refresh-enabled: false,改主配置不再崩,但需要重启才生效(两害相权取其轻) |
| 高频配置拆出去 | 把”经常要改的配置”放到独立的 dataId,用 Nacos 监听器定向更新,免重启 |
两个独立 dataId(改完保存即生效,不用重启网关):
| dataId | 内容 | 作用 |
|---|---|---|
catfun-gateway-routes |
路由 JSON 数组 | 改路由(加服务、改路径)立即生效 |
catfun-gateway-auth-whitelist |
免登录路径 JSON 数组 | 加/删免登录接口立即生效 |
安全性:监听器解析配置失败时,会保留旧配置并打印错误日志,不会让网关挂掉。重启网关只需要在配置好这两个 dataId 后做最后一次。
类比:不在行驶中拆整辆汽车(全量刷新),而是单独给车换个轮子(定向刷新)——换错了大不了用旧轮子继续跑。
小结
| 配置类型 | 生效方式 |
|---|---|
网关主配置(Nacos catfun-gateway-dev.yml) |
需重启 |
路由(catfun-gateway-routes) |
热生效 |
认证白名单(catfun-gateway-auth-whitelist) |
热生效 |
| 业务服务(MVC)改配置 | 热生效(默认支持 @RefreshScope) |
| 连接类配置(数据源、Redis、MQ) | 不建议热更,重启是常态 |