Nacos 如何实现服务注册与发现的?


一则或许对你有用的小广告

欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书

  • 《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于 Spring AI + Spring Boot 3.x + JDK 21...查看介绍

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...查看介绍;演示链接:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/

  • 新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍

截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 源码级理解深度:面试官不仅仅是想知道 Nacos 能做注册中心,更是想考察你是否理解其内部的注册流程、心跳机制、数据同步协议等底层原理。

  2. 分布式设计能力:这道题涉及临时实例 vs 持久实例、AP vs CP、Distro 协议 vs Raft 协议等核心概念,考察你对分布式系统设计的理解。

  3. 生产排障能力:理解了注册与发现的原理,才能在生产环境中快速定位 "服务注册不上"、"实例下线感知慢" 等问题。

核心答案

Nacos 的服务注册与发现围绕两个核心角色展开:服务提供者(Provider)服务消费者(Consumer),通过 Nacos Server 作为中介完成注册、发现和健康检查。

整体流程概括:

注册:Provider 启动 → 向 Nacos 注册实例 → 持续发送心跳(临时实例)
发现:Consumer 启动 → 向 Nacos 订阅服务 → 获取实例列表 → 本地缓存
通知:实例变化 → Nacos 主动推送 → Consumer 更新本地缓存

一句话总结:Nacos 服务注册靠 心跳续约(临时实例)或服务端探测(持久实例) 维持,服务发现靠 长轮询推送 + 本地缓存 实现实时感知。

深度解析

一、服务注册流程

上图展示了 Nacos 服务注册的完整流程。关键要点:

  • 步骤 ①②③:Spring Boot 应用启动后,监听容器刷新事件,触发自动注册。NacosAutoServiceRegistration 是 Spring Cloud Alibaba 提供的自动注册类,它实现了 Spring 的 SmartLifecycle 接口,在容器启动完成后自动调用注册逻辑。
  • 步骤 ④⑤⑥:通过 NacosServiceRegistryNamingService 的调用链,最终通过 HTTP 或 gRPC(Nacos 2.x)向 Nacos Server 发送注册请求,携带 IP、端口、服务名、集群名、权重等信息。
  • 步骤 ⑦⑧⑨:Nacos Server 收到注册请求后,根据实例类型(临时/持久)决定存储方式——临时实例存内存(AP,Distro 协议),持久实例存磁盘(CP,Raft 协议)。注册成功后触发服务变更事件,通知所有订阅了该服务的消费者。

二、心跳机制(维持注册状态)

注册完之后,Provider 需要 持续证明自己还活着,否则 Nacos Server 会把实例剔除。

上图展示了 Nacos 两种实例类型的心跳和健康检查机制。关键要点:

  • 临时实例(默认):客户端主动发心跳。Nacos 1.x 通过 HTTP 接口发送心跳(每 5s 一次),Nacos 2.x 通过 gRPC 长连接自动保活,不再需要单独的心跳请求。如果 Nacos Server 在 15 秒 内没收到心跳,将实例标记为不健康;30 秒 内没收到心跳,将实例从注册表中剔除。

  • 持久实例:服务端主动探测。Nacos Server 定期通过 TCP 或 HTTP 方式向实例发起健康检查,不需要客户端参与。这种方式适合非 Java 应用(如 MySQL、Redis)的注册。

  • Nacos 2.x 的重大改进:用 gRPC 长连接替代了 HTTP 短连接心跳,连接建立后通过双向流自动保活,大幅减少了网络开销和心跳请求量。

三、服务发现流程

上图展示了 Nacos 服务发现的完整流程。关键要点:

  • 步骤 ①②③:Consumer 启动时,通过 NamingService.subscribe() 向 Nacos Server 发起订阅,同时拉取一份完整的服务实例列表缓存到本地。后续每次发起远程调用时,直接从本地缓存获取实例列表,不会每次都请求 Nacos Server。

  • 步骤 ④⑤⑥(双重保障机制):

    • 定时拉取(兜底):Consumer 每 6 秒主动向 Nacos Server 发起查询,对比本地缓存,如果实例有变化则更新。这是兜底机制,确保即使推送失败也能最终一致。
    • 推送通知(实时):Nacos Server 在实例发生变更时(注册、下线、变不健康),主动向所有订阅者推送变更通知。Nacos 1.x 通过 UDP 推送,Nacos 2.x 通过 gRPC 推送,更加可靠。
  • 步骤 ⑦:Consumer 收到更新后的实例列表,交给 Spring Cloud LoadBalancer 进行负载均衡,选择一个具体实例发起调用。

四、Nacos 集群间的数据同步

上图展示了 Nacos 集群间两种不同的数据同步协议。关键要点:

  • Distro 协议(AP):Nacos 自研的 AP 协议,用于临时实例的数据同步。核心思想是 哈希分片 + 异步复制——每个 Nacos 节点根据哈希值负责一部分服务的写入,写入后异步同步给其他节点。任何节点挂了都不影响整体服务,牺牲了一点一致性,换来高可用。

  • Raft 协议(CP):用于持久实例的数据同步。只有 Leader 节点处理写请求,写入后需要过半 Follower 确认才算成功。保证了强一致性,但 Leader 选举期间集群不可写。

五、Nacos 2.x 的架构升级

Nacos 2.x 相比 1.x 做了重大架构升级,核心变化:

维度 Nacos 1.x Nacos 2.x
通信协议 HTTP 短连接 gRPC 长连接
心跳方式 HTTP 心跳(每 5s) gRPC 双向流保活
配置监听 长轮询(Long Polling) gRPC Server Push
服务变更推送 UDP(不可靠) gRPC(可靠)
连接管理 无状态,每次新建连接 有状态,连接管理器
性能提升 基准 连接数减少 50%+,吞吐提升 2 倍

Nacos 2.x 用 gRPC 长连接替代了 1.x 的 HTTP 短连接 + UDP 推送,大幅提升了性能和可靠性。

面试高频追问

  1. 追问一:Nacos 2.x 的 gRPC 连接断了怎么办?

    • Nacos 2.x 内置了连接重试机制。gRPC 连接断开后,客户端会按照指数退避策略自动重连(1s → 2s → 4s → 最多 30s)。重连成功后,客户端会重新发送订阅请求,恢复服务发现能力。在断连期间,Consumer 使用本地缓存继续工作,不受影响。
  2. 追问二:Nacos 注册中心挂了,微服务还能调通吗?

    • 可以。Consumer 本地缓存了服务实例列表,Nacos 挂了不影响已有的调用。只是无法感知新的服务上下线变化。Provider 重新注册需要等 Nacos 恢复。
  3. 追问三:Nacos 如何防止服务实例列表被恶意篡改?

    • Nacos 支持通过 authentication 开启鉴权,客户端需要携带 accessToken 才能进行注册和订阅操作。生产环境建议开启鉴权,配合命名空间(Namespace)进行环境隔离。

常见面试变体

  • 变体一:"Nacos 的注册中心原理是什么?"
  • 变体二:"Nacos 1.x 和 2.x 的服务注册有什么区别?"
  • 变体三:"Nacos 的临时实例和持久实例有什么区别?"
  • 变体四:"Nacos 是如何保证服务发现实时性的?"

记忆口诀

注册靠心跳,发现靠推送,缓存兜底保可用:Provider 通过心跳(临时实例)或被探测(持久实例)维持注册状态;Consumer 通过订阅 + 本地缓存实现服务发现,Nacos Server 主动推送变更保证实时性,定时拉取作为兜底保证最终一致。

总结

Nacos 服务注册的核心是 心跳续约(临时实例)+ 服务端探测(持久实例),服务发现的核心是 订阅推送 + 本地缓存 + 定时拉取 的双重保障机制。Nacos 2.x 通过 gRPC 长连接替代 HTTP 短连接,大幅提升了性能和可靠性。理解这套机制,是排查微服务注册发现相关问题的基础。