Nacos 如何实现服务注册与发现的?
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
面试考察点
-
源码级理解深度:面试官不仅仅是想知道 Nacos 能做注册中心,更是想考察你是否理解其内部的注册流程、心跳机制、数据同步协议等底层原理。
-
分布式设计能力:这道题涉及临时实例 vs 持久实例、AP vs CP、Distro 协议 vs Raft 协议等核心概念,考察你对分布式系统设计的理解。
-
生产排障能力:理解了注册与发现的原理,才能在生产环境中快速定位 "服务注册不上"、"实例下线感知慢" 等问题。
核心答案
Nacos 的服务注册与发现围绕两个核心角色展开:服务提供者(Provider) 和 服务消费者(Consumer),通过 Nacos Server 作为中介完成注册、发现和健康检查。
整体流程概括:
注册:Provider 启动 → 向 Nacos 注册实例 → 持续发送心跳(临时实例)
发现:Consumer 启动 → 向 Nacos 订阅服务 → 获取实例列表 → 本地缓存
通知:实例变化 → Nacos 主动推送 → Consumer 更新本地缓存
一句话总结:Nacos 服务注册靠 心跳续约(临时实例)或服务端探测(持久实例) 维持,服务发现靠 长轮询推送 + 本地缓存 实现实时感知。
深度解析
一、服务注册流程
上图展示了 Nacos 服务注册的完整流程。关键要点:
- 步骤 ①②③:Spring Boot 应用启动后,监听容器刷新事件,触发自动注册。
NacosAutoServiceRegistration是 Spring Cloud Alibaba 提供的自动注册类,它实现了 Spring 的SmartLifecycle接口,在容器启动完成后自动调用注册逻辑。 - 步骤 ④⑤⑥:通过
NacosServiceRegistry→NamingService的调用链,最终通过 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 推送,大幅提升了性能和可靠性。
面试高频追问
-
追问一:Nacos 2.x 的 gRPC 连接断了怎么办?
- Nacos 2.x 内置了连接重试机制。gRPC 连接断开后,客户端会按照指数退避策略自动重连(1s → 2s → 4s → 最多 30s)。重连成功后,客户端会重新发送订阅请求,恢复服务发现能力。在断连期间,Consumer 使用本地缓存继续工作,不受影响。
-
追问二:Nacos 注册中心挂了,微服务还能调通吗?
- 可以。Consumer 本地缓存了服务实例列表,Nacos 挂了不影响已有的调用。只是无法感知新的服务上下线变化。Provider 重新注册需要等 Nacos 恢复。
-
追问三:Nacos 如何防止服务实例列表被恶意篡改?
- Nacos 支持通过
authentication开启鉴权,客户端需要携带accessToken才能进行注册和订阅操作。生产环境建议开启鉴权,配合命名空间(Namespace)进行环境隔离。
- Nacos 支持通过
常见面试变体
- 变体一:"Nacos 的注册中心原理是什么?"
- 变体二:"Nacos 1.x 和 2.x 的服务注册有什么区别?"
- 变体三:"Nacos 的临时实例和持久实例有什么区别?"
- 变体四:"Nacos 是如何保证服务发现实时性的?"
记忆口诀
注册靠心跳,发现靠推送,缓存兜底保可用:Provider 通过心跳(临时实例)或被探测(持久实例)维持注册状态;Consumer 通过订阅 + 本地缓存实现服务发现,Nacos Server 主动推送变更保证实时性,定时拉取作为兜底保证最终一致。
总结
Nacos 服务注册的核心是 心跳续约(临时实例)+ 服务端探测(持久实例),服务发现的核心是 订阅推送 + 本地缓存 + 定时拉取 的双重保障机制。Nacos 2.x 通过 gRPC 长连接替代 HTTP 短连接,大幅提升了性能和可靠性。理解这套机制,是排查微服务注册发现相关问题的基础。