解锁配置管理新高度:架构师深度解读Nacos配置中心的设计哲学与实践之道

关注微信公众号 "程序员小胖" 每日技术干货,第一时间送达!

引言

在微服务的世界里,配置管理是一项复杂而又至关重要的任务。想象一下,如果有一个中心能够统一管理所有服务的配置,那将是多么高效和便捷的事情!今天,我们就来深入探索这样一个神奇的存在------Nacos配置中心,揭开它的神秘面纱,了解它是如何让配置管理变得简单而强大的。

配置中心简介

Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理Spring Cloud 应用的外部属性配置。

应用场景

配置中心实战

1. 搭建nacos配置中心

搭建过程参考(juejin.cn/post/735537...

结果

2. 服务端整合nacos配置中心

引入依赖

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

bootstrap.yml文件,配置nacos配置中心的地址

yaml 复制代码
spring:
  application:
    name: user-center

  cloud:
    nacos:
      config: 
        server-addr: nacos.mall.com:8848
        username: nacos
        password: nacos

3. 将application.yml中的配置移到配置中心, 在配置中心中创建微服务的配置

DataID

Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系 统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。

Group

Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配 置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置 分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组 件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置。

4. 注释掉application.yml中的配置,并在bootstrap.yml中指定需要加载的配置文件的路径

在 Nacos Spring Cloud 中,dataId 的完整格式如下: <math xmlns="http://www.w3.org/1998/Math/MathML"> p r e f i x − {prefix}- </math>prefix−{spring.profiles.active}.${fileextension}

  • prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。
  • spring.profiles.active 即为当前环境对应的 profile,详情可以参考 。 注意:当 spring.profiles.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 <math xmlns="http://www.w3.org/1998/Math/MathML"> p r e f i x . {prefix}. </math>prefix.{fileextension}
  • file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。

5.测试 (省略)

配置中心常用配置

  1. 支持profile粒度的配置

spring-cloud-starter-alibaba-nacos-config 在加载配置的时候,不仅仅加载了以 dataid 为 <math xmlns="http://www.w3.org/1998/Math/MathML"> s p r i n g . a p p l i c a t i o n . n a m e . {spring.application.name}. </math>spring.application.name.{file-extension:properties} 为前缀的基础配置,还加载了dataid为 <math xmlns="http://www.w3.org/1998/Math/MathML"> s p r i n g . a p p l i c a t i o n . n a m e − {spring.application.name}- </math>spring.application.name−{profile}. <math xmlns="http://www.w3.org/1998/Math/MathML"> f i l e − e x t e n s i o n : p r o p e r t i e s 的基础配置。在日常开发中如果遇到多套环境下的不同配置,可以通过 S p r i n g 提供的 {file-extension:properties} 的基础配置。在日常开发中如果遇到多套环境下的不同配置,可以通过Spring 提供的 </math>file−extension:properties的基础配置。在日常开发中如果遇到多套环境下的不同配置,可以通过Spring提供的{spring.profiles.active} 这个配置项来配置。

yaml 复制代码
  spring:
    profiles:
      active:  dev
  1. 支持自定义 namespace 的配置

用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。在没有明确指定 ${spring.cloud.nacos.config.namespace} 配置的情况下, 默认使用的是 Nacos 上Public 这个namespace。如果需要使用自定义的命名空间,可以通过以下配置来实现:

yaml 复制代码
  spring:
    cloud:
      nacos:
        config:
          namespace: 71bb9785-231f-4eca-b4dc-6be446e12ff8
  1. 定义 Group 的配置 Group是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从 而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配 置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配 置类型,如 database_url 配置和 MQ_topic 配置。 在没有明确指定 ${spring.cloud.nacos.config.group} 配置的情况下,默认是DEFAULT_GROUP 。如 果需要自定义自己的 Group,可以通过以下配置来实现:
yaml 复制代码
  spring:
    cloud:
      nacos:
        config:
          group: DEVELOP_GROUP
  1. 支持自定义扩展的 Data Id 配置

Data ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。 在实际的业务场景中应用和共享配置间的关系可能如下:

  • 单个应用的角度来看: 应用可能会有多套(develop/beta/product)发布环境,多套发布环境之间有不同的基础配置,例如数据库。
  • 多个应用的角度来看:多个应用间可能会有一些共享通用的配置,比如多个应用之间共用一套zookeeper集群 通过自定义扩展的 Data Id 配置,既可以解决多个应用间配置共享的问题,又可以支持一个应用有多个配置文件。
yaml 复制代码
server: 
  port: 8088

spring:
  application:
    name: user-center

   profiles: 
     active:  test

  cloud:
    nacos:
      config: 
        server-addr: 121.40.175.87:8848
        username: nacos
        password: nacos
        fix-extension: yml

        # 自定义 Data Id 的配置
        shared-configs: #不同工程的通用配置 支持共享的 DataId
          - data-id: nacos.yml
            group: GLOBALE_GROUP
          - data-id: openfeign.yml
            group: GLOBALE_GROUP
        extension-configs: # 支持一个应用多个 DataId 的配置
          - data-id: common.yml
            group: REFRESH_GROUP
            refresh: true #支持动态刷新
  • spring.cloud.nacos.config.shared-configs[n].data-id 来支持多个共享 Data Id 的配置,多个之间用逗号 隔开。 多个共享配置间的一个优先级的关系我们约定:按照配置出现的先后顺序,即后面的优先级要高于前面。如果没有明确配置,默认情况下所有共享配置的 Data Id 都不支持动态刷新。
  • spring.cloud.nacos.config.extension-configs[n].data-id 的配置方式来支持多个 Data Id 的配置。多个 Data Id 同时配置时,他的优先级关系是 n 的值越大,优先级越高。
  • spring.cloud.nacos.config.extension-configs[n].group 的配置方式自定义 Data Id 所在的组,不明确配 置的话,默认是 DEFAULT_GROUP。
  • spring.cloud.nacos.config.extension-configs[n].refresh 的配置方式来控制该 Data Id 在配置变更时,是 否支持应用中可动态刷新, 感知到最新的配置值。默认是不支持的.
  1. 配置的优先级 Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置
  • A: 通过 spring.cloud.nacos.config.shared-configs 支持多个共享 Data Id 的配置
  • B: 通过 spring.cloud.nacos.config.extension-configs[n].data-id 的方式支持多个扩展 Data Id 的配置
  • C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置 当三种方式共同使用时,他们的一个优先级关系是:A < B < C
  1. 完整的配置优先级从高到低
  1. 完全关闭配置 通过设置 spring.cloud.nacos.config.enabled = false 来完全关闭 Spring Cloud Nacos Config

配置的动态刷新

  1. spring-cloud-starter-alibaba-nacos-config 也支持配置的动态更新。

修改启动类,每隔3s从Enviroment中获取common.user和common.age中的值

java 复制代码
    public static void main(String[] args) throws InterruptedException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(UserConfigDemoApplication.class, args);

        while (true) {
            //当动态配置刷新时,会更新到 Enviroment中,因此这里每隔3秒中从Enviroment中获取配置
            String userName = applicationContext.getEnvironment().getProperty("common.name");

            String userAge = applicationContext.getEnvironment().getProperty("common.age");
            System.err.println("common name:" + userName + "; age: " + userAge);
            TimeUnit.SECONDS.sleep(3);

        }
    }

进入配置中心,修改common.yml,common.age从10改成30

查看控制台参数打印的变化

  1. OpenFeign开启对feign.Request.Options属性的刷新支持
yaml 复制代码
#启用刷新功能,可以刷新connectTimeout和readTimeout
  spring:
    cloud:
      openfeign:
        client:
          refresh-enable: true
  1. RefreshScope实现Bean的动态刷新

使用@Value注解可以获取到配置中心的值,如果类IndexController没有用@RefreshScope注解修饰就无法动态感知配置修改 后的值

java 复制代码
@RestController
@RefreshScope
public class IndexController {

    @Value("${common.age}")
    private String age;
    @Value("${common.name}")
    private String name;

    @GetMapping("/index")
    public String hello() {
        return name + "," + age;
    }

}

测试结果: 使用@RefreshScope修饰的IndexController,访问/index结果可以获取最新的值

@RefreshScope 导致@Scheduled定时任务失效问题

java 复制代码
@SpringBootApplication
@EnableScheduling // 开启定时任务功能
public class NacosConfigApplication {
}

@RestController
@RefreshScope //动态感知修改后的值
public class TestController {

    @Value("${common.age}")
    String age;
    @Value("${common.name}")
    String name;

    @GetMapping("/common")
    public String hello() {
        return name + "," + age;
    }

    //触发@RefreshScope执行逻辑会导致@Scheduled定时任务失效
    @Scheduled(cron = "*/3 * * * * ?") //定时任务每隔3s执行一次
    public void execute() {
        System.out.println("定时任务正常执行。。。。。。");
    }
}

测试结果: 当在配置中心变更属性后,定时任务失效 当再次访问http://localhost:8060/common,定时任务生效

原因:@RefreshScope修饰的bean的属性发生变更后,会从缓存中清除。此时没有这个bean,定时任务当然也就不生效了。详细原因如下:

  • @RefreshScope 注解标注了@Scope 注解,并默认了ScopedProxyMode.TARGET_CLASS属性,此属性的功能就是创建一个代理,在每次调用的时候都用它来调用GenericScope#get 方法来获取bean对象。
  • 在GenericScope 里面包装了一个内部类 BeanLifecycleWrapperCache 来对加了 @RefreshScope 的bean进行 缓存,使其在不刷新时获取的都是同一个对象。
  • 如属性发生变更会调用 ContextRefresher#refresh()------>RefreshScope#refreshAll() 进行缓存清理方法调 用,并发送刷新事件通知 ------> 调用GenericScope#destroy() 实现清理缓存
  • 当下一次使用此bean对象时,代理对象会调用GenericScope#get(String name, ObjectFactory<?> objectFactory) 方法创建一个新的bean对象,并存入缓存中,此时新对象因为Spring 的装配机制就是新的属性了

解决方案

实现Spring事件监听器,监听 RefreshScopeRefreshedEvent事件,监听方法中进行一次定时方法的 调用

java 复制代码
@RestController
@RefreshScope //动态感知修改后的值
public class TestController implements ApplicationListener<RefreshScopeRefreshedEvent> {

    @Value("${common.age}")
    String age;
    @Value("${common.name}")
    String name;

    @GetMapping("/common")
    public String hello() {
        return name + "," + age;
    }

    //触发@RefreshScope执行逻辑会导致@Scheduled定时任务失效
    @Scheduled(cron = "*/3 * * * * ?") //定时任务每隔3s执行一次
    public void execute() {
        System.out.println("定时任务正常执行。。。。。。");
    }


    @Override
    public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
        this.execute();
    }
}

结语

尽管Nacos配置中心为我们带来了极大的便利,但在面对复杂的分布式系统时,如何更好地发挥其优势?配置的版本冲突如何优雅解决?在大规模服务环境下如何优化配置拉取性能?这些都是值得进一步探讨的问题。期待你在评论区分享你的见解与实践经验,让我们共同推动微服务配置管理技术的进步!

参考文档:docs.spring.io/spring-clou...

相关推荐
会游泳的石头2 小时前
OpenFeign 的实现原理详解
spring cloud
菜鸟起航ing3 小时前
【Java面试系列】Spring Cloud微服务架构中的分布式事务实现与性能优化详解 - 3-5年Java开发必备知识
java·spring cloud·微服务·面试·分布式事务
汤姆大聪明5 小时前
微服务与Spring Cloud Alibaba简介
java·spring boot·spring·spring cloud·微服务
汤姆大聪明7 小时前
Nacos服务发现和配置管理
java·spring boot·spring cloud·服务发现
小萌新上大分8 小时前
nginx入门,部署静态资源,反向代理,负载均衡使用
nginx·spring cloud·nginx配置·nginx部署前端项目·nginx负载均衡策略·nginx反向代理配置
在荒野的梦想15 小时前
若依微服务集成Flowable仿钉钉工作流
spring cloud·微服务·钉钉
小杨40420 小时前
springboot框架项目实践应用十八(nacos高级特性)
spring boot·后端·spring cloud
Familyism2 天前
微服务篇——SpringCloud
spring cloud·微服务·架构
A Everyman2 天前
SpringCloud-快速通关(一)
后端·spring·spring cloud·微服务
坤小满学Java2 天前
【SpringCloud】从入门到精通(下)
后端·spring·spring cloud