文章

Nacos

Nacos 笔记

目录

  1. 配置动态更新原理
  2. 临时与永久、AP 与 CP
  3. 网关改 Nacos 配置后全部报 500「网关内部错误」

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) 不建议热更,重启是常态

4. (待补充)

本文由作者按照 CC BY 4.0 进行授权