Nacos 的服务发现(Service Discovery)是指服务消费者(Consumer)如何获取服务提供者(Provider)地址列表的过程。
Nacos 采用了 "主动拉取 + 被动推送" 相结合的机制,确保了服务发现的实时性 和高可用性。
一、 服务发现的整体架构
服务发现主要涉及三个角色:
- 服务提供者 (Provider):注册自己的 IP/端口到 Nacos。
- Nacos Server:保存服务注册表,并感知实例状态变化。
- 服务消费者 (Consumer):从 Nacos 获取地址列表,并监听变化。
二、 详细流程详解
1. 消费者初始化与订阅 (Subscription)
当一个微服务启动并需要调用其他服务时:
- 查询缓存:消费者首先会检查本地内存中是否有目标服务的实例列表。
- 发起订阅:如果没有或需要更新,消费者会向 Nacos Server 发起订阅请求(Subscribe)。
- 版本差异 :
- Nacos 1.x:通过 HTTP 轮询定时拉取,并开启一个 UDP 端口接收服务端的推送。
- Nacos 2.x :通过 gRPC 双向流 建立长连接。客户端发送
ServiceQueryRequest,并在服务端注册该连接的订阅关系。
2. 服务端获取与过滤 (Server Side)
Nacos Server 收到查询请求后:
- 检索注册表 :从内存 Map(
Service->Cluster->Instances)中找到对应的实例。 - 健康检查过滤 :
- 只返回
healthy=true(健康)的实例。 - 只返回
enabled=true(启用)的实例。
- 只返回
- 选择集群:根据客户端配置的集群名称(Cluster Name),优先返回同集群的实例(就近访问策略)。
3. 客户端本地缓存 (Local Cache)
这是 Nacos 保证高可用的关键:
- 内存缓存 :客户端将获取到的
ServiceInfo(包含实例列表)保存到内存中的ServiceInfoHolder。 - 磁盘缓存 :同时将数据异步写入本地磁盘文件(路径通常在
${user.home}/nacos/naming/${namespace})。 - 容灾逻辑 :如果 Nacos Server 全部宕机,消费者会直接读取本地磁盘文件,保证业务不中断。
4. 实时更新机制 (Push & Pull)
为了保证消费者拿到的地址是最新的,Nacos 采用了以下策略:
- 被动接收推送 (Push) :
- 一旦服务提供者下线、故障或有新节点加入,Nacos Server 会触发
ServiceChangeEvent。 - Nacos 2.x 会通过已建立的 gRPC 长连接,主动将最新的实例列表推送到消费者(Push)。这种方式是毫秒级的。
- 一旦服务提供者下线、故障或有新节点加入,Nacos Server 会触发
- 定时主动拉取 (Pull) :
- 作为补偿机制,客户端内部有一个定时任务(默认每 10 秒或根据配置),会检查本地数据的版本,必要时重新拉取,防止因网络闪断导致的推送丢失。
5. 客户端负载均衡 (Load Balance)
服务发现拿到的是一个列表(比如 3 个 IP),具体调用哪一个由客户端决定:
- 在 Spring Cloud 环境中,通常配合 Spring Cloud LoadBalancer 或 Ribbon。
- 根据 Nacos 返回的 Weight(权重) 进行加权随机访问。
- 执行最终的 RPC 调用(如 RestTemplate, Feign, Dubbo)。
三、 核心流程图解
- Consumer ->
query(gRPC) -> Nacos Server - Nacos Server ->
result(Instance List) -> Consumer - Consumer ->
save-> Memory & Disk Cache - (当 Provider 发生变化时)
- Nacos Server ->
push(New List) -> Consumer - Consumer ->
update-> Local Cache
四、 关键特性总结
| 特性 | 说明 |
|---|---|
| 实时性 | 2.x 基于 gRPC 长连接推送,几乎瞬时感知服务变化。 |
| 高可用 (Self-Protection) | 就算 Nacos 集群挂了,客户端靠本地缓存(磁盘文件)也能正常调用服务。 |
| 就近访问 | 支持按 Cluster 归类,优先调用同机房/同地域的服务,降低延迟。 |
| 权重调整 | 支持在 Nacos 控制台动态修改实例权重,实现灰度发布或平滑下线。 |
五、 Nacos 是如何支撑大规模服务发现的?
- 分级存储:Namespace -> Group -> Service -> Cluster -> Instance,结构清晰。
- UDP/gRPC 推送:相比全量拉取,推送极大减少了网络带宽消耗。
- 写时复制 (CopyOnWrite):在更新实例列表时,不影响读取操作,保证高性能。
- 压缩传输:在返回大量实例时,Nacos 会对数据进行压缩,减少网络开销。