Spring Cloud Alibaba Nacos 核心原理

本文首发于公众号:JavaArchJourney

Spring Cloud Alibaba Nacos (全称 Dynamic Naming and Configuration Service)是阿里巴巴开源的服务注册与发现配置管理框架,可以代替 Spring Cloud Eureka 和 Spring Cloud Config 的功能。

主要特点

  • 服务发现与注册:微服务可以向 Nacos 注册自己,并且能够查找其他已注册的服务。
  • 动态 DNS 服务:Nacos 可以将服务名称映射到实际的 IP 地址和端口,支持负载均衡和服务路由。
  • 健康检查:Nacos 支持定期检查服务实例的健康状态,并在检测到故障时自动将其从可用服务列表中移除。
  • 整合 Spring Cloud 负载均衡:Nacos 可以快速整合 Spring Cloud LoadBalancer / Ribbon / OpenFeign 实现客户端负载均衡。
  • 配置管理:Nacos 提供了一个集中化的配置管理解决方案,支持管理和分发不同环境下的配置信息,并且支持配置版本控制和动态刷新配置。
  • 可视化管理后台 :Nacos 提供了控制台,方便可视化进行服务管理、配置管理。

简单使用示例

部署 Nacos Server

与 Eureka Server 不同,Nacos Server 由阿里巴巴团队使用 Java 语言编写并将 Nacos Server 的下载地址给用户(Nacos 的 GitHub 发布页面),用户只需要直接下载并运行即可,启动方式见 Nacos 官网

Nacos Server 支持集群部署,以实现服务高可用。

创建 Nacos Client

pom.xml 添加依赖:

xml 复制代码
<dependencyManagement>
    <dependencies>
        <!--Spring Cloud 版本-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
      
        <!--Spring Cloud Alibaba 版本-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring.cloud.alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- Nacos Discovery Starter -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
  
    <!-- Nacos Config Starter -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
</dependencies>

bootstrap.yml 中配置(在 bootstrap.yml而非 application.yml 中配置主要是为了启用 Nacos 配置管理):

yaml 复制代码
spring:
  application:
    name: your-application-name # 服务名
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848 # Nacos Server 配置中心地址
        file-extension: yaml # 指定配置内容的数据格式,默认为 properties
      discovery:
        server-addr: 127.0.0.1:8848 # Nacos Server 注册中心地址
        enabled: true # 开启服务注册与发现功能,默认为 true ,若不需要可以手动置为 false

服务注册与发现

服务注册 就是在完成上述配置后,Nacos Client 启动时,就会把服务以服务名(spring.application.name)的方式注册到 Nacos Server。

服务发现可以结合 Spring Cloud LoadBalancer / Ribbon / OpenFeign 客户端来使用。

以 Spring Cloud LoadBalancer 为例,在 Spring Boot 应用中创建一个配置类,用于定义带 @LoadBalanced 注解的 RestTemplate,使 RestTemplate 支持服务发现和负载均衡:

java 复制代码
import org.springframework.cloud.client.loadbalanced.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

通过服务名来调用微服务:

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class MyRestService {

    private final RestTemplate restTemplate;

    @Autowired
    public MyRestService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public String getDataFromService(String serviceId) {
        // serviceId 是在服务注册中心注册的服务名称,而不是实际的主机名或IP地址
        // LoadBalancer 将自动解析服务ID到具体的服务实例,并执行负载均衡策略。
        return restTemplate.getForObject("http://" + serviceId + "/your-endpoint", String.class);
    }
}

可以看出,即使底层的服务注册发现组件从其他组件(比如 Spring Cloud Netflix Eureka)替换成 Spring Cloud Alibaba Nacos,上层的 Spring Cloud LoadBalancer 使用流程也可以完全不感知。

动态获取配置

可以使用 @Value 注解或 @RefreshScope 注解来动态获取和刷新配置。

java 复制代码
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RefreshScope
public class TestController {
    @Value("${your.config.key:default_value}")
    private String configValue;

    @GetMapping("/getConfig")
    public String getConfig() {
        return configValue;
    }
}

服务注册中心原理

服务注册存储结构:

Nacos 使用的是"服务-集群-实例"三层逻辑数据模型,并通过命名空间和分组实现服务隔离。

  • 服务名(Service Name):每个服务都有一个唯一的名称,用于标识该服务。
  • 命名空间(Namespace):命名空间用于隔离不同的环境或用户,例如开发、测试、生产环境可以分别位于不同的命名空间中。
  • 分组(Group):服务可以被划分到不同的组里,便于管理和区分具有相同服务名但不同功能或版本的服务。
  • 集群(Cluster):集群是服务实例的一个逻辑分组,通常用于区分不同的部署环境或区域。
  • 实例(Instance):服务的具体实例,包含了 IP 地址、端口、权重、上线状态等信息。实例分为临时实例和持久化实例。

临时实例和持久化实例对比:

特性 临时实例 持久化实例
健康检查机制 客户端心跳检测:客户端需要定期向 Nacos 服务器发送心跳包。 服务端主动探测:Nacos 服务器会主动发起 HTTP 或 TCP 请求进行健康检查。
生命周期管理 短期存在,依赖于客户端心跳维持状态;如果心跳停止且超过设定时间,则从注册表中移除。 长期存在,即使暂时不健康也不会从注册表中删除,而是标记为不健康,以便于服务监控与恢复。运维人员可以手动注销实例。
数据一致性协议 Distro 协议(AP模式) Raft 协议(CP模式)
配置参数 ephemeral=true(默认) ephemeral=false
适用场景 适用于动态扩展、频繁更新的服务实例,比如微服务架构下的业务服务(Spring Cloud 微服务等)。 大多数业务用的是临时实例。 更适合核心系统或需要长期稳定运行的服务,比如在物理服务器上部署的服务、一些无法主动上报心跳的基础的组件如数据库等。

Nacos 架构包含三个组件:

  • 服务提供者(Provider):提供业务功能的微服务,例如订单服务、用户服务等。
  • 服务消费者(Consumer):调用其他服务的微服务,例如前端网关或业务服务。
  • Nacos Server(注册中心):负责服务注册、发现、健康检查和元数据管理。

服务注册流程

  • 服务提供者启动 :服务提供者启动时,会通过配置的 server-addr(如 127.0.0.1:8848)连接 Nacos Server。
  • 注册服务实例:服务提供者将自己的元数据信息通过 HTTP 协议(默认)发送给 Nacos Server,包括:服务名、实例 IP 地址和端口、健康状态、所属分组等。服务可以被注册为临时实例或持久化实例。
  • 健康检查机制
    • 对于临时实例,采用客户端健康检测(心跳机制):服务实例每隔 5 秒向 Nacos 服务器发送一次心跳。默认情况下,如果 Nacos 服务器在超过 15 秒内没有收到某个服务实例的心跳,则会将该实例标记为不健康;如果超过 30 秒未收到心跳,则会从服务列表中删除该实例。
    • 对于持久化实例,采用服务端健康检测(主动探测机制):Nacos 服务器主动发起对服务实例的健康状态的检查,通过发送 HTTP 请求或 TCP 连接等方式来确认服务实例是否存活。服务端会按照设定的时间间隔进行探测,例如每隔 20 秒进行一次探测。如果健康检查失败,实例会被标记为不健康,但不会从服务列表中删除;运维人员可以根据监控情况手动进行故障实例的摘除或恢复操作。

服务发现流程:

  • 服务消费者启动 :消费者服务启动时,也同样通过配置的 server-addr连接 Nacos Server,并且也会将自己的服务注册到服务注册中心(即服务消费者本身也可以是一个服务提供者)。
  • 订阅服务信息:消费者可以指定服务名、分组名、集群名等条件来获取特定的服务实例。Nacos Server 会返回当前健康的服务实例列表给消费者。
  • 本地缓存与更新 :消费者会在本地缓存服务实例列表。在 Nacos 1.x 中,消费者会定期(比如 30s )从 Nacos 拉取数据,以保持服务实例列表的最新状态。Nacos 2.x,官网博客《Nacos 通信通道》说要统一改造为"长链接"推送机制。
  • 负载均衡调用:消费者使用 Spring Cloud LoadBalancer / Ribbon / OpenFeign 从实例列表中选择一个实例进行调用。

动态配置中心原理

  • 配置发布 :在 Nacos 控制台上,开发者可以针对不同的环境(如开发、测试、生产等)定义和管理配置。配置项以文件的形式存在,支持多种格式,如 properties、yaml 等。
  • 客户端监听 :当使用 Spring Cloud Nacos Config 的应用程序启动时,它会从 Nacos 配置中心拉取对应的配置信息,并将其保存在本地缓存中,作为 Nacos Server 不可用情况下的降级使用。然后,客户端会发起长轮询请求,开始监听特定 dataIdgroup 下的配置变化。
  • 配置变更通知 :Nacos 1.x 中,Nacos Client 通过"长轮询"机制来检测配置的变化。不同于传统的轮询方式,在长轮询中,如果服务器端没有新的数据,它不会立即返回一个空响应,而是将这个请求挂起,即保持连接打开状态,等待直到有配置变更或者超时。一旦检测到配置发生变化,Nacos Server 会找到所有注册了该配置项的客户端长轮询连接,并立即响应这些连接,将最新的配置数据返回给客户端。客户端处理完响应后,重新发起新的长轮询请求,形成循环。Nacos 2.x,官网博客《Nacos 通信通道》说要统一改造为"长链接"推送机制。
  • 配置动态更新 :通过在 Bean 中添加 @RefreshScope 注解,可以实现对这些 Bean 中的配置实现动态刷新。@RefreshScope 是 Spring Cloud 中用于实现配置动态刷新的机制,其原理是通过创建一个代理对象来替代被标记为 @RefreshScope 的 Bean,并采用延迟初始化策略(即仅在首次访问时根据最新的配置信息创建并初始化该 Bean)。当配置发生变更并触发刷新操作时,Spring Cloud 会发布刷新事件,RefreshScope 捕获此事件后将相关 Bean 的缓存视为过期并清除;下次访问这些 Bean 时,系统基于更新后的配置重新创建和初始化它们,从而实现在不重启应用的情况下响应配置变化的能力。

Nacos Server 集群数据一致性

Nacos Server 支持多节点集群部署,通常采用 3~5 个节点 来组成集群,以保证服务高可用。

Nacos Server 集群中的多个节点之间存储了相同的数据。对于服务注册信息,默认使用内存存储,速度快;对于配置信息,则默认使用内置的 Derby 数据库把配置持久化到磁盘上,避免配置丢失。两者都可选持久化到外部数据库(如 MySQL)。

集群部署时,需要保障多节点之间的数据一致性。根据 CAP 原理,在一个分布式系统中,一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)这三个基本属性不可兼得,系统设计时只能同时满足其中的两个:

  • AP 模式注重保障高可用,容忍节点故障,主要适用于临时服务注册、心跳机制。
  • CP 模式注重保障强一致性,容忍节点短时不可用,主要适用于服务元数据、配置中心。

Nacos 使用的是 CP + AP 的混合架构

  • 服务注册
    • 非持久化实例(临时实例):需要客户端上报心跳进行服务实例续约,Nacos 使用 Distro 协议保证注册信息的高可用(AP)。
    • 持久化实例:因为所有的数据都是直接使用调用 Nacos 服务端直接创建并主动探测实例的健康状态(一般用于一些基础组件如数据库、缓存等),所以 Nacos 会使用 Raft 协议保障注册信息在各个节点之间的强一致性(CP)。
  • 配置中心
    • 有 DB 模式(读写分离架构):一致性的核心是 Server 与 DB 保持数据一致性,从而保证 Server 数据一致;Server 之间都是对等的。数据写任何一个 Server,优先持久化,持久化成功后异步通知其他节点到数据库中拉取最新配置值,并且通知写入成功。
    • 无 DB 模式:Server 间采用 Raft 协议保证配置数据的强一致性(CP)。

参考

相关推荐
Victor35615 分钟前
Redis(32)Redis集群(Cluster)是什么?
后端
风象南15 分钟前
docker cp 引发的 node_exporter CPU 暴涨踩坑记
后端
Victor35621 分钟前
Redis(33)Redis集群的工作原理是什么?
后端
程序员 Andy1 小时前
项目中为什么使用SpringBoot?
java·spring boot·后端
bobz9658 小时前
5070 Ti CodeLlama 7B > Mistral 7B > Qwen3 8B
后端
麦兜*9 小时前
Spring Boot 集成 Docker 构建与发版完整指南
java·spring boot·后端·spring·docker·系统架构·springcloud
程序视点9 小时前
2025最佳图片无损放大工具推荐:realesrgan-gui评测与下载指南
前端·后端
fured10 小时前
[调试][实现][原理]用Golang实现建议断点调试器
开发语言·后端·golang
bobz96511 小时前
linux cpu CFS 调度器有使用 令牌桶么?
后端
bobz96511 小时前
linux CGROUP CPU 限制有使用令牌桶么?
后端