[spring cloud] nacos注册中心和配置中心

1. Nacos 作为服务注册中心 (Service Registry)

1.1 核心原理

Nacos 作为注册中心,主要维护一张"服务列表"。

  1. 服务注册 (Registration): 服务提供者(Provider)启动时,会通过 REST API 发送请求向 Nacos Server 注册自己的信息(IP、端口、服务名)。
  2. 心跳机制 (Heartbeat): 提供者会定时(默认 5 秒)发送心跳包。如果 Nacos 超过 15 秒没收到心跳,会将实例设为不健康;超过 30 秒则剔除。
  3. 服务发现 (Discovery): 服务消费者(Consumer)从 Nacos 拉取服务列表,并缓存在本地。
  4. CAP 理论: Nacos 支持 AP (可用性优先,默认) 和 CP (一致性优先) 模式的切换。

1.注册 & 发送心跳
2.订阅 & 拉取列表
3.推送变更
4.远程调用(RPC/REST)
服务提供者 Provider
Nacos Server
服务消费者 Consumer

1.2 Java 代码实战 (Spring Boot 3.0+)

第一步:引入依赖 (pom.xml)

在使用 Spring Boot 3.x 时,需配合 Spring Cloud Alibaba 2022.0.0.0 或更高版本。

xml 复制代码
<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies>

第二步:配置文件 (application.yml)

yaml 复制代码
server:
  port: 8081

spring:
  application:
    name: nacos-provider-demo # 服务名称,非常重要
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # Nacos Server 地址

第三步:启动类与测试

Spring Boot 3.x 中,@EnableDiscoveryClient 注解由自动化配置处理,通常不再需要显式添加,但写上也无妨。

java 复制代码
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}

启动后,在 Nacos 控制台的"服务管理 -> 服务列表"中即可看到 nacos-provider-demo


2. Nacos 作为配置中心 (Config Center)

2.1 核心原理

在微服务架构中,几十个服务的配置文件如果都散落在 Jar 包里,修改配置需要重启,非常麻烦。Nacos 提供了集中式管理和动态刷新。

  1. Data ID: Nacos 组织配置的核心,通常格式为 ${spring.application.name}-${spring.profiles.active}.${file-extension} (例如 services-order-dev.yaml)。
  2. 长轮询 (Long Polling): 客户端不单纯是拉取配置。客户端会发起一个长连接请求,Nacos 收到请求后若配置无变化会 hold 住请求(默认 30秒),一旦配置发生变化,立即返回最新配置。这保证了配置更新的实时性。
  3. 动态刷新: 结合 Spring 的 @RefreshScope,可以在不重启应用的情况下更新内存中的变量。

2.2 Java 代码实战 (Spring Boot 3.0+ 重点)

第一步:引入依赖

在之前的 pom.xml 基础上添加:

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

第二步:配置文件 (application.yml)

直接在 application.yml 中使用 import 语法。

yml 复制代码
spring:
  profiles:
    active: dev
  application:
    name: service-order
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      config:
        import-check:
          enabled: false
        namespace: ${spring.profiles.active:dev} #指定命名空间

server:
  port: 8080

---

spring:
  config:
    import:
      - nacos:common.properties?group=order # nacos:{DataId}?group={Group}
    activate:
      on-profile: dev

---
spring:
  config:
    import:
      - nacos:common.properties?group=order
    activate:
      on-profile: prod

第三步:在 Nacos 控制台添加配置

  1. 登录 Nacos 控制台。
  2. 进入"配置管理 -> 配置列表"。
  3. 点击"+"号新增配置:
  • 新建命名空间
  • Group: 填 order
  • Data ID: common.properties (必须与上面 import 的逻辑匹配)
  • 配置格式: Properties
  • 配置内容:
Properties 复制代码
# 订单超时时间
order.timeout=5000
  1. 发布。

第四步:Java 代码读取与动态刷新

为了演示不重启更新配置,我们需要使用 @RefreshScope

java 复制代码
package com.example.demo.controller;

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 // 关键注解:当 Nacos 配置变更时,动态刷新此类中的 @Value 属性
public class ConfigController {

    @Value("${user.name}")
    private String userName;

    @Value("${user.age}")
    private Integer userAge;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return "User: " + userName + ", Age: " + userAge;
    }
}

3. 配置优先级

在 Spring Boot 3 + Nacos 中,配置的加载是有严格顺序的。原则是:"越具体,优先级越高"

假设服务名是 service-order,环境是 dev。以下是配置生效的优先级(从高到低,上面的覆盖下面的):

优先级 配置文件 (Data ID) 说明
NO.1 (最高) service-order-dev.yaml 精确匹配:既指定了服务名,又指定了环境 profile。这是主配置。
NO.2 service-order.yaml 服务默认:只指定了服务名,没指定环境。通常放该服务所有环境通用的配置。
NO.3 common.properties (import) 扩展/共享配置 :通过 spring.config.import 引入的外部配置。
NO.4 (最低) 本地 application.yml 项目 Jar 包里的本地配置。

这两个是 Nacos 配置中心进阶使用的两个重要场景。

  1. @ConfigurationProperties :属于 Spring Boot 原生方式 。用于将一组配置批量绑定到 Java Bean 中。在 Spring Cloud 环境下,它自带"自动刷新"光环,比 @Value 更强大、更优雅。
  2. NacosConfigManager 监听 :属于 Nacos SDK 底层方式 。当你不仅仅想更新变量,还想在配置变化时触发一段业务逻辑(比如:清理本地缓存、调整线程池大小、打印日志),就需要用到它。

下面我基于 Spring Boot 3.0+ 为你详细介绍原理和代码。


4. @ConfigurationProperties 批量绑定与自动刷新

4.1 原理
  • 批量绑定 :Spring Boot 通过扫描 prefix 前缀,将配置文件(Nacos 里的 YAML/Properties)中的值自动映射到 Bean 的属性上。
  • 自动刷新
  • 这与 @Value 不同。使用 @Value 必须加 @RefreshScope 才能动态刷新。
  • 使用 @ConfigurationProperties 的 Bean,Spring Cloud 有一个专门的 ConfigurationPropertiesRebinder 监听器。当 Nacos 配置发生变化发出 RefreshEvent 时,这个监听器会自动销毁旧 Bean,重新绑定生成新 Bean。
4.2 Java 代码实战

场景 :我们在 Nacos 的 service-order-dev.yaml 中配置了一组订单的超时规则。

Nacos 配置内容:

yaml 复制代码
order:
  rules:
    connect-timeout: 2000
    read-timeout: 5000
    auto-confirm-days: 7

Java 代码 (OrderProperties.java):

java 复制代码
package com.example.order.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data // 必须要有 Setter 方法,Spring 才能注入值
@Component // 注册为 Spring Bean
@ConfigurationProperties(prefix = "order.rules") // 指定前缀
public class OrderProperties {

    /**
     * 连接超时时间
     * 对应 YAML 中的 order.rules.connect-timeout
     */
    private Integer connectTimeout;

    /**
     * 读取超时时间
     */
    private Integer readTimeout;

    /**
     * 自动确认收货天数
     */
    private Integer autoConfirmDays;
}

测试控制器:

java 复制代码
@RestController
public class TestController {

    @Autowired
    private OrderProperties orderProperties;

    @GetMapping("/props")
    public OrderProperties getProps() {
        // 直接返回对象,当 Nacos 修改配置后,再次访问这里,值会自动变
        return orderProperties;
    }
}

实操验证

  1. 启动服务,访问 /props,看到 connectTimeout 是 2000。
  2. 在 Nacos 控制台修改为 9999,发布。
  3. 不用重启 ,再次访问 /props,你会发现值已经变成了 9999。

5. NacosConfigManager 监听配置变化

5.1 原理

有时候,配置变了,我们不只是想改变变量的值,我们想执行代码

例如:useLocalCache 开关从 true 变成了 false,我不仅要更新变量,我还需要立即清空内存里的 Map。这时候仅仅通过注入属性是不够的。

NacosConfigManager 是 Spring Cloud Alibaba 对 Nacos 原生 SDK (ConfigService) 的封装。通过它,我们可以注册一个监听器 (Listener) ,当 Nacos 服务端推送配置变更时,客户端的回调方法 receiveConfigInfo 会被触发。

5.2 Java 代码实战

场景 :监听 common.properties 文件的变化,一旦变化,打印日志并执行自定义逻辑。

java 复制代码
package com.example.order.listener;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;

@Slf4j
@Component
public class CustomConfigListener {

    @Autowired
    private NacosConfigManager nacosConfigManager;

    // 假设我们要监听的 DataId 和 Group
    private final String dataId = "common.properties";
    private final String group = "order";

    @PostConstruct
    public void init() throws NacosException {
        // 获取原生的 ConfigService
        nacosConfigManager.getConfigService().addListener(dataId, group, new Listener() {
            
            @Override
            public Executor getExecutor() {
                // 返回 null 表示使用 Nacos 内部的主线程来执行回调
                // 如果逻辑很复杂,建议自定义线程池
                return null;
            }

            @Override
            public void receiveConfigInfo(String configInfo) {
                // configInfo 就是变化后最新的配置内容(字符串形式)
                log.info("【监听到配置变化】DataID: {}, Group: {}", dataId, group);
                log.info("新配置内容: \n{}", configInfo);
                
                // TODO: 在这里执行你的业务逻辑
                // 比如:refreshLocalCache();
                // 比如:updateThreadPoolSize();
            }
        });
    }
}
5.3 进阶:监听特定的值 (结合 JSON/YAML 解析)

receiveConfigInfo 返回的是整个文件的字符串。如果你只想监听 timeout 这一项的变化,你需要自己解析字符串。

java 复制代码
// 伪代码示例
public void receiveConfigInfo(String configInfo) {
    Properties props = new Properties();
    props.load(new StringReader(configInfo)); // 解析配置
    
    String newTimeout = props.getProperty("timeout");
    if (!newTimeout.equals(currentTimeout)) {
        log.info("检测到 timeout 发生改变,执行更新逻辑...");
        currentTimeout = newTimeout;
    }
}
相关推荐
刘一说2 小时前
Spring Cloud微服务中的断路器:从Hystrix到Sentinel的进化之路
spring cloud·hystrix·微服务
oak隔壁找我2 小时前
java-jwt 使用
后端
Hello.Reader2 小时前
连接四元组它为什么重要,以及它和端口复用(SO_REUSEPORT)的关系(Go 实战)
开发语言·后端·golang
想摆烂的不会研究的研究生2 小时前
并发场景——接口幂等性设计
数据库·redis·后端·缓存
sunnyday04262 小时前
Spring AOP 实现日志切面记录功能详解
java·后端·spring
手握风云-2 小时前
JavaEE 进阶第九期:Spring MVC - Web开发的“交通枢纽”(三)
前端·spring·java-ee
小Ti客栈2 小时前
Spring Boot Profile 与外部化配置详解
spring·springboot
on the way 1232 小时前
day07-Spring循环依赖
后端·spring
Java天梯之路2 小时前
Spring Boot 钩子全集实战(六):SpringApplicationRunListener.contextPrepared()详解
java·spring boot·后端