Nacos 的心跳机制(Heartbeat)
Nacos 的心跳机制(Heartbeat Mechanism)是其服务发现功能的核心组成部分,主要用于维持临时实例(Ephemeral Instances)的生命周期和健康状态。
简单来说,心跳机制就是客户端定期向服务端“报平安”,服务端根据是否收到心跳来判断该服务实例是否健康或是否需要剔除。
以下是 Nacos 心跳机制的详细深度解析,分为 Nacos 1.x (HTTP) 和 Nacos 2.x (gRPC) 两个阶段,以及核心的时间参数。
1. 核心概念:临时实例 vs 持久实例
在深入心跳之前,必须明确 Nacos 的两种实例类型:
- 临时实例 (Ephemeral):默认类型(Spring Cloud Alibaba 默认也是这种)。适用于 K8s、Docker 等动态 IP 场景。心跳机制主要针对此类实例。 如果心跳断了,实例会被从服务列表中删除。
- 持久实例 (Persistent):适用于静态 IP(如数据库、传统物理机)。Nacos 服务端会主动探测(TCP/HTTP)该实例。如果探测失败,标记为不健康,但不会删除。
2. Nacos 1.x 的心跳机制 (基于 HTTP 短轮询)
在 Nacos 1.x 版本中,心跳维持完全依赖于客户端主动发起的 HTTP 请求。
2.1 客户端行为 (Client Side)
- 注册服务:客户端启动时,向服务端注册。
- 开启定时任务:注册成功后,客户端会开启一个
ScheduledExecutorService线程池。 - 发送心跳:默认每隔 5秒 向服务端发送一个 HTTP POST 请求(
/nacos/v1/ns/instance/beat),携带服务名、IP、端口等元数据。
2.2 服务端行为 (Server Side)
服务端接收到心跳包后,会更新该实例的 lastBeatTime(最后心跳时间)。服务端内部有一个定时任务(ClientBeatCheckTask),定期扫描所有临时实例:
健康检查 (15秒):
- 如果当前时间 -
lastBeatTime> 15秒(默认),服务端会将该实例标记为不健康 (Unhealthy)。 - 此时,该实例依然存在于服务列表中,但
healthy字段为false。某些负载均衡策略(如 Ribbon/Spring Cloud LoadBalancer)可能会避开它。
- 如果当前时间 -
实例剔除 (30秒):
- 如果当前时间 -
lastBeatTime> 30秒(默认),服务端会认为该实例已宕机。 - 服务端将该实例从服务注册表中彻底删除,并推送变更事件给订阅了该服务的消费者。
- 如果当前时间 -
3. Nacos 2.x 的心跳机制 (基于 gRPC 长连接)
Nacos 2.0 进行了架构升级,引入了 gRPC 长连接。这极大地改变了心跳的实现方式,性能和实时性得到了显著提升。
3.1 连接即心跳 (Connection based)
- 长连接:Nacos 2.x 客户端与服务端之间建立 gRPC 长连接。
- 会话机制:不再需要像 1.x 那样每 5 秒发送一个 HTTP 请求。只要 TCP 连接保持建立,服务端就认为客户端是健康的。
- 断开检测:
- 如果客户端宕机(进程崩溃),TCP 连接断开,服务端通过
Connection事件瞬间感知。 - 服务端会立即将该连接关联的所有实例标记为不健康或剔除,无需等待 30 秒。这使得服务下线的感知速度从“秒级”提升到了“毫秒级”。
- 如果客户端宕机(进程崩溃),TCP 连接断开,服务端通过
3.2 兼容性 (依然保留心跳逻辑)
虽然主要依赖长连接状态,但 Nacos 2.x 依然保留了应用层的心跳逻辑(KeepAlive 请求),主要用于:
- 防止网络设备(如防火墙、负载均衡器)因连接空闲而切断 TCP 连接。
- 兼容 Nacos 1.x 的客户端 SDK。
4. 关键配置参数 (时间阈值)
这些参数可以在客户端配置文件中设置,也可以在代码中通过 Metadata 设置,甚至可以在 Nacos 控制台针对具体实例修改。
| 参数名 (Metadata Key) | 默认值 | 含义 |
|---|---|---|
preserved.heart.beat.interval |
5000 ms (5秒) | 客户端发送心跳的间隔时间。 |
preserved.heart.beat.timeout |
15000 ms (15秒) | 服务端标记实例为“不健康”的超时时间。 |
preserved.ip.delete.timeout |
30000 ms (30秒) | 服务端剔除(删除)实例的超时时间。 |
在 Spring Cloud Alibaba 中的配置示例:
yaml
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 下面这些通常不需要改,除非有特殊需求
metadata:
preserved.heart.beat.interval: 5000
preserved.heart.beat.timeout: 15000
preserved.ip.delete.timeout: 30000
5. 总结与流程图解
完整生命周期流程:
- Register: Client 启动 -> 发送注册请求 -> 开启心跳线程。
- Beat (Loop):
- Client 每 5s 发送一次心跳。
- Server 收到 -> 更新
lastBeatTime。
- Check (Server Task):
- Server 检查:
Now - lastBeatTime > 15s? -> 是 -> 设为 Unhealthy。 - Server 检查:
Now - lastBeatTime > 30s? -> 是 -> Delete Instance (剔除)。
- Server 检查:
- Discovery: 其他消费者订阅服务列表,收到剔除通知,更新本地缓存。
为什么 Nacos 2.x 更好?
- 1.x 问题:如果服务规模很大(例如 10k 实例),每 5 秒一次 HTTP 请求会产生巨大的 TPS 压力(10000 / 5 = 2000 TPS 仅用于心跳),消耗大量网络和 CPU 资源。
- 2.x 优势:长连接复用,减少了频繁建立连接和 HTTP 解析的开销;感知下线速度更快。
常见面试题/排查点
- 问题:为什么服务挂了,Nacos 控制台还需要等一会儿才消失?
- 回答:如果是 Nacos 1.x 客户端或网络分区导致,需要等待 30秒(delete timeout)。如果是 Nacos 2.x 且进程直接崩溃,通常是立即消失;如果是网络中断但 TCP 连接未超时,可能受 TCP KeepAlive 影响。
- 问题:如何加快服务下线感知?
- 回答:升级到 Nacos 2.x;或者在 1.x 中缩短
preserved.ip.delete.timeout(但不建议设置过短,防止网络抖动导致误删)。
- 回答:升级到 Nacos 2.x;或者在 1.x 中缩短