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)。

参考

相关推荐
我是不会赢的21 分钟前
使用 decimal 包解决 go float 浮点数运算失真
开发语言·后端·golang·浮点数
yuqifang37 分钟前
写一个简单的Java示例
java·后端
Re27539 分钟前
分库分表后主键总“撞车”?5种全局唯一ID方案让你不再头疼
后端
陈随易1 小时前
VSCode v1.103发布,AI编程任务列表,可用GPT 5和Claude 4.1
前端·后端·程序员
中等生1 小时前
Python的隐形枷锁:GIL如何"绑架"了你的多线程梦想
后端·python
白露与泡影1 小时前
彻底解决SpringCloud TCP连接过多未释放问题~
tcp/ip·spring·spring cloud
Pitayafruit1 小时前
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
redis·分布式·后端
哈基米喜欢哈哈哈2 小时前
Netty入门(二)——网络传输
java·开发语言·网络·后端
尘心不灭2 小时前
Spring Boot 项目代码笔记
spring boot·笔记·后端