🧩 一、问题背景
场景:
你有两个微服务:
-
order-service
(调用方 / Consumer) -
product-service
(被调用方 / Provider)
当 product-service
的一个实例下线(比如机器宕机或应用关闭)时,
order-service
要知道它已经失效,不能再发请求过去。
🧠 二、Nacos 的服务发现机制
Nacos 分为两部分:
-
Naming Server(注册中心)
-
Naming Client(客户端SDK)
Nacos 客户端(如 spring-cloud-starter-alibaba-nacos-discovery
)既可以注册自己,也能订阅别人。
📍 服务注册阶段
1️⃣ product-service
启动后向 Nacos Server 注册:
2️⃣ Nacos Server 将这个实例保存到内存和磁盘(临时实例或持久实例)。
3️⃣ 同时,order-service
订阅 product-service
的实例列表。
⚙️ 三、Consumer 如何“感知” Provider 下线?
Nacos 设计了两种机制结合使用:
1️⃣ 心跳检测(Heartbeat Check)
作用:判断服务端实例是否健康
-
对于 临时实例(ephemeral=true):
-
服务提供者每 5 秒 向 Nacos Server 发送心跳包。
-
如果超过 15 秒未收到心跳,Nacos Server 将认为该实例“下线”。
-
Nacos Server 会从注册表中移除该实例。
-
示例日志:
2️⃣ 推送机制(Push)+ 客户端本地缓存
当服务实例变化(新增、下线、心跳失效)时:
✅ Nacos Server 会主动推送变更事件给客户端
Nacos 使用的是 长轮询(Long Polling) 机制,而不是短轮询。
流程如下:
-
order-service
客户端启动后,向 Nacos Server 发起一个 长轮询请求: -
这个请求会阻塞 30 秒左右。
-
如果在这期间
product-service
的实例列表发生变化(新增/下线):-
Nacos Server 会立即返回变化数据。
-
-
客户端收到更新后:
-
更新本地缓存(本地注册表)
-
通知负载均衡组件(Ribbon / LoadBalancer)
-
-
客户端再发起新的长轮询请求,如此循环。
所以客户端能在几十毫秒到几秒内感知实例变化,而不是实时心跳。
🧭 四、调用方的负载均衡逻辑
Spring Cloud Alibaba 集成了 NacosDiscoveryClient
+ Spring LoadBalancer
:
-
每个服务都有本地的实例列表缓存。
-
当实例变化时(Server 推送事件),本地缓存更新。
-
下次发起 HTTP 调用时,会从最新缓存中选取健康实例。
所以:
-
当服务下线 → 心跳超时 → Nacos 推送 → 本地缓存刷新 → 下次调用自动避开无效节点。
🔄 五、完整交互流程图
📊 六、延迟感知与优化建议
优化点 | 描述 |
---|---|
心跳频率 | 默认 5 秒,可根据实际 SLA 调整(推荐 3~5s) |
心跳超时时间 | 默认 15 秒,可调低(如 10s)以更快下线检测 |
客户端缓存刷新间隔 | 客户端一般在 5s 内完成更新 |
推送机制优化 | 使用 nacos.naming.push.empty.protection=false 保证推送异常时重试 |
自定义健康检查 | 在 Provider 中暴露 /health ,由 Nacos 定时检测应用层健康状态 |
💡 七、总结记忆口诀
“Provider 发心跳,Server 记健康,Consumer 长轮询,缓存自动换。”
环节 | 主体 | 机制 | 作用 |
---|---|---|---|
健康检测 | Provider → Server | 心跳包 | 让 Nacos 知道谁活着 |
状态维护 | Server | 注册表维护 | 标记实例 UP/DOWN |
事件推送 | Server → Consumer | 长轮询推送 | 通知调用方更新实例列表 |
调用选择 | Consumer | 本地缓存 + 负载均衡 | 自动选择健康实例调用 |