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/

面试考察点

面试官提出这个问题,通常旨在考察以下几个层面的知识和理解:

  1. 对微服务核心组件的理解:考察你是否清楚服务注册中心在微服务架构中的核心作用和基本工作模型。
  2. 对 Nacos 核心流程的掌握:不仅仅是想知道 “客户端发送请求,服务端存储” 这样简单的步骤,更是想知道注册、续约、发现、下线等完整生命周期的具体交互细节和机制。
  3. 对底层实现原理的探究:深入考察你是否了解 Nacos 实现这些功能所依赖的关键技术,例如其数据存储模型、健康检查机制以及一致性协议
  4. 对架构特性的理解与应用:考察你是否理解 Nacos 同时支持 AP 与 CP 一致性模型的设计哲学,以及在不同场景下如何选择和权衡。
  5. 与生态整合的实践经验:通过你是否能描述其与 Spring Cloud、Dubbo 等主流框架的集成方式,来间接判断你的实际项目经验。

核心答案

Nacos 实现服务注册与发现的核心机制可以概括为:客户端通过内置的 SDK 向 Nacos Server 注册自身服务信息,并通过定时心跳维持注册状态(即健康上报);服务消费者则通过查询 Nacos Server 来获取健康的服务提供者实例列表,并基于此实现负载均衡调用。

整个流程围绕 “服务-实例” 的元数据模型展开,其生命周期管理依赖于客户端主动上报服务端主动健康检查相结合的方式。

深度解析

原理/机制

我们可以从客户端服务端两个视角来拆解其核心原理:

1. 服务注册与保活

  • 客户端视角:以 Spring Cloud Alibaba 应用为例,启动时,NacosServiceRegistry 会提取应用信息(服务名、IP、端口、健康检查路径等),通过 HTTP API 或 gRPC 将 Instance 对象发送给 Nacos Server 进行注册。之后,客户端会启动一个定时心跳线程(默认每5秒),通过发送 /nacos/v1/ns/instance/beat 请求来向 Server 证明自己存活。
  • 服务端视角:Nacos Server 接收注册请求后,会将实例信息存储在其内置的、基于内存的数据存储层ServiceManager 维护的 ConcurrentHashMap 结构)。对于心跳,服务端会更新对应实例的最后心跳时间。此外,Nacos Server 还主动发起健康检查(如 TCP 端口探测或 HTTP 路径探测),作为客户端心跳的补充,形成双保险来确保实例健康状态的准确性。

2. 服务发现与订阅

  • 客户端视角:服务消费者启动时,会向 Nacos Server 查询其订阅的服务名对应的所有实例列表,并缓存在本地。更重要的是,它会建立一个 gRPC 长连接到 Nacos Server,订阅该服务的变更。当服务提供者集群发生变化(如实例上线、下线、不健康)时,Server 会通过这个连接主动推送变更事件,客户端收到后立即更新本地缓存,从而实现近乎实时的服务发现
  • 服务端视角:服务端维护着服务与订阅者(客户端)之间的订阅关系。当某个服务的实例信息发生变更时,它会根据订阅关系,找到所有关注该服务的客户端连接,并推送变更数据。

3. 数据一致性与集群模式 这是 Nacos 架构的精髓。Nacos 的设计是 “1.0时代,一个 Nacos 解决两大问题(配置中心与服务发现)”,因此它巧妙地设计了两种一致性协议来适配不同场景:

  • Distro 协议:这是一个 AP 模式的、去中心化的分布式一致性协议。主要用于服务发现场景。其核心思想是每个 Nacos Server 节点负责一部分数据,并作为这部分数据的“责任节点”。写请求会被路由到责任节点,再由该节点异步地将数据同步给其他集群节点。这保证了在集群网络分区时,所有节点仍能提供注册与查询服务,实现了高可用,但短期内节点间数据可能不一致。
  • Raft 协议:这是一个 CP 模式的、强一致性的协议。主要用于配置管理场景。它通过选举 Leader 节点,所有写操作必须经过 Leader 并同步到多数派节点才算成功,保证了数据的强一致性,但在网络分区时,少数派节点会不可用。
  • 模式切换:在 Nacos 1.0.0 之后,可以通过 curl 命令动态修改指定服务的保护阈值等参数,或使用 NacosRule 等负载均衡策略,来灵活应对不同服务对一致性与可用性的需求。

代码示例

在 Spring Cloud Alibaba 项目中,使用方式非常简单:

// 1. 服务提供者/消费者启动类,通过注解启用服务注册与发现功能
@SpringBootApplication
@EnableDiscoveryClient // 关键注解,声明为 Nacos 客户端
public class ProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }
}

// 2. 在 application.yml 中配置 Nacos Server 地址和服务名
spring:
  application:
    name: product-service # 服务名,用于注册和发现
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.100:8848 # Nacos Server 地址
        group: DEFAULT_GROUP # 服务分组,默认为 DEFAULT_GROUP

// 3. 服务消费者通过 LoadBalancerClient 或 OpenFeign 进行服务调用
@RestController
public class OrderController {
    @Autowired
    private LoadBalancerClient loadBalancerClient;

    public String createOrder() {
        // 根据服务名获取一个服务实例
        ServiceInstance instance = loadBalancerClient.choose("product-service");
        String url = String.format("http://%s:%s/product/1", instance.getHost(), instance.getPort());
        // ... 发起HTTP调用
        return result;
    }
}

对比分析与最佳实践

  • 与 Eureka 对比:Nacos 的健康检查机制更丰富(客户端心跳+服务端主动探测),而 Eureka 主要依赖客户端心跳。在一致性上,Eureka 是纯 AP 架构,Nacos 则提供了 AP/CP 的灵活性。Nacos 的实例变更推送机制也比 Eureka 的定时拉取(30秒) 更及时。
  • 最佳实践
    1. 生产环境务必使用集群模式:至少部署3个节点,并通过 Nginx 或 SLB 做负载均衡,提供统一的访问入口。
    2. 合理配置健康检查:根据业务网络情况,调整心跳间隔(spring.cloud.nacos.discovery.heart-beat-interval)和超时时间,在灵敏度和网络压力间取得平衡。
    3. 利用命名空间(Namespace)和分组(Group)做好环境与逻辑隔离:例如,用 dev, test, prod 命名空间隔离不同环境;用 group 对同一环境下的服务进行逻辑划分。
    4. 服务消费者端配置本地缓存与容错:即使 Nacos Server 短暂不可用,应用也能依靠本地缓存列表继续运行一段时间。

总结

Nacos 通过 “客户端注册心跳 + 服务端健康检查” 来维护实例状态,通过 “客户端本地缓存 + 服务端变更推送” 来实现高效、实时的服务发现,并创新性地运用 Distro (AP) 与 Raft (CP) 混合一致性协议,使其在服务发现的高可用与配置管理的强一致之间取得了出色的平衡,成为现代微服务架构中强大的核心组件。