Nacos 如何感知服务的上线和下线?
Nacos 感知服务上线和下线的机制主要依赖于 主动注册、心跳机制(Heartbeat) 以及 服务端的主动探测。
要透彻理解这个问题,需要区分 服务提供者(Provider) 如何向 Nacos 汇报状态,以及 服务消费者(Consumer) 如何从 Nacos 获取最新的服务列表。此外,Nacos 中 临时实例 和 持久化实例 的处理逻辑也有所不同。
以下是详细的流程解析:
一、 服务上线(Service Online)
服务上线的感知相对简单,主要是“主动上报”。
- 启动注册:
- 当微服务(Provider)启动时,Nacos Client(SDK)会读取配置文件,通过 HTTP(Nacos 1.x)或 gRPC(Nacos 2.x)接口向 Nacos Server 发送注册请求。
- 请求中包含服务名、IP、端口、权重、集群名以及元数据等信息。
- 服务端存储:
- Nacos Server 收到请求后,会将该实例信息存储在内存注册表中(如果是持久化实例,还会写入磁盘/数据库)。
- 此时,Nacos Server 就“感知”到了该服务已上线。
二、 服务下线(Service Offline)
服务下线的感知分为两种情况:正常下线 和 异常下线(宕机/网络故障)。
1. 正常下线(Graceful Shutdown)
- 主动注销:当服务进行正常的关闭流程(如
kill PID或 Spring Boot 的ContextClosedEvent)时,Nacos Client 会调用 注销接口。 - 即时移除:Nacos Server 收到注销请求后,立即将该实例从服务列表中移除,并通知订阅者。
2. 异常下线(Crash / Network Failure)
这是 Nacos 感知的核心难点。如果服务直接崩溃(如 kill -9 或断电),它无法发出注销请求。Nacos 依靠 心跳机制 和 健康检查 来感知。
这里必须区分两种实例类型:
A. 临时实例(Ephemeral Instance)—— 默认模式(AP 模型)
适用于 Kubernetes、Docker 等 IP 随时变化的场景。
- 心跳维持(Client -> Server):
- 服务启动后,Client 会启动一个定时任务,每隔 5秒 向 Nacos Server 发送一次心跳包(Heartbeat)。
- Nacos 2.x 使用 gRPC 长连接,通过连接的活跃状态来维持心跳。
- 健康检查逻辑:
- 15秒:如果 Nacos Server 超过 15秒 没有收到心跳,会将该实例标记为 不健康(Unhealthy),但暂不移除。
- 30秒:如果超过 30秒 仍未收到心跳,Nacos Server 会认为该实例已宕机,将其从服务列表中 彻底剔除(Delete)。
B. 持久化实例(Persistent Instance)—— CP 模型
适用于 MySQL、Redis 或传统静态 IP 的服务。
- 主动探测(Server -> Client):
- 持久化实例不会因为心跳中断而被剔除。
- Nacos Server 会 主动 探测该实例的 IP 和端口(通常通过 TCP/HTTP 探测)。
- 状态变更:
- 如果探测失败,Nacos 会将其标记为 不健康,但 不会删除 该实例的元数据。它依然存在于列表中,只是健康状态为
false。
- 如果探测失败,Nacos 会将其标记为 不健康,但 不会删除 该实例的元数据。它依然存在于列表中,只是健康状态为
三、 消费者(Consumer)如何感知变化?
Nacos Server 知道服务上下线了,消费者(调用方)是如何知道的?Nacos 采用了 推拉结合(Push + Pull) 的模式。
- 服务端推送(Push):
- Nacos 1.x:利用 UDP 协议。当服务列表发生变化时,Server 发送 UDP 包给所有订阅了该服务的 Client。
- Nacos 2.x:利用 gRPC 长连接。Server 通过长连接直接将变更事件推送给 Client。这是实时的,延迟极低。
- 客户端轮询(Pull):
- 作为兜底机制,Nacos Client 会启动一个定时任务(默认 10秒 左右),主动向 Server 查询最新的服务列表。
- 这保证了即使推送失败(如 UDP 丢包),客户端也能在一段时间后感知到服务状态的变化,保证最终一致性。
- 本地缓存:
- Client 获取到最新的服务列表后,会更新本地缓存(Local Cache)。服务调用时,直接读取本地缓存,不依赖 Nacos Server 的实时响应。
总结图解
| 动作 | 触发机制 | Nacos Server 的反应 | 消费者感知方式 |
|---|---|---|---|
| 上线 | Client 主动调用注册 API | 写入内存注册表 | Server 推送 + Client 定时拉取 |
| 正常下线 | Client 主动调用注销 API | 立即移除实例 | Server 推送 + Client 定时拉取 |
| 异常下线 (临时实例) | 心跳超时 (Client 停止发送) | >15s 标为不健康 >30s 剔除实例 |
Server 推送 + Client 定时拉取 |
| 异常下线 (持久实例) | 探测失败 (Server 主动检查) | 标为不健康 (不剔除) | Server 推送 + Client 定时拉取 |
核心关键词
- 心跳(Heartbeat):续约机制。
- 阈值(Threshold):15s 变不健康,30s 剔除。
- 临时 vs 持久:决定是被动等待心跳还是主动探测,以及是否删除数据。
- 推拉结合:UDP/gRPC 推送 + 定时 Pull 兜底。