Spring Cloud Alibaba 七天训练营(二)分布式配置

文档目录

1. 简介

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

Spring Cloud Alibaba Nacos Config 是 Config Server 和 Client 的替代方案,客户端和服务器上的概念与 Spring Environment 和 PropertySource 有着一致的抽象,在特殊的 bootstrap 阶段,配置被加载到 Spring 环境中。当应用程序通过部署管道从开发到测试再到生产时,您可以管理这些环境之间的配置,并确保应用程序具有迁移时需要运行的所有内容。Nacos 的获取和启动方式可以参考 Nacos 官网

如果读者是阿里云商业化组件 ANS 或 ACM 用户,请使用 Nacos Config 代替对应的组件。

2. 学习目标

  • 使用 Nacos Config 作为 Spring Cloud 分布式配置
  • 使用 Nacos Config 实现 Bean 动态刷新
  • 了解 Nacos Config 高级配置

3. 详细内容

  • 快速上手:使用 Nacos Config 作为外部化配置源
  • 多文件扩展名支持:以 YAML 文件扩展名为例,讨论 Nacos Config 多文件扩展名支持
  • 动态配置更新:演示 @RefreshScope 特性,实现 Bean 动态刷新
  • 自定义扩展:自定义 namespace、Group 以及 Data Id 的配置扩展
  • 运维特性:演示 Nacos Config 高级外部化配置以及 Endpoint 内部细节

4. 快速上手

4.1 如何引入 Nacos Config 支持分布式配置

Nacos Config 引入的方式同样也有两种,即 Aliyun Java Initializr 引入和 Maven pom.xml 依赖。官方推荐使用 Aliyun Java Initializr 方式引入 Nacos Discovery,以便简化组件之间的依赖关系。

4.1.1 [简单] 通过 Aliyun Java Initializr 创建工程并引入 Nacos Config(推荐)

由于 Spring Cloud 组件的版本和依赖较为复杂,推荐读者使用 Aliyun Java Initializr 构建应用工程。
读者选择偏好的 Web 浏览器访问 Aliyun Java Initializr,其资源网址为:https://start.aliyun.com/bootstrap.html

下文以 Google Chrome 浏览器为例,当网页加载后,首先,在 "项目基本信息" 部分输入 Group :“com.alibaba.cloud” 以及 Artifact:“nacos-config-sample”。然后,“组件依赖” 输入框搜索:“Nacos Config”,选择 "Nacos Configuration",如下所示:

Spring Cloud Alibaba 七天训练营(二)分布式配置

同上组件操作,增加 “Spring Web” 和 “Spring Boot Actuator” 组件:

Spring Cloud Alibaba 七天训练营(二)分布式配置

组件选择后,点击 “生成” 高亮按钮。随后,平台将生成一个名为 “nacos-config-sample.zip” 的压缩文件,将其保存到本地目录,并解压该文件,工程目录将随之生成。打开目录下的 pom.xml 文件,不难发现 Nacos starter 声明其中(以下 XML 内容均来自于项目根路径中的 pom.xml 文件):

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

不过该 starter 并未指定版本,具体的版本声明在 com.alibaba.cloud:spring-cloud-alibaba-dependencies 部分:

    <dependencyManagement>
        <dependencies>
            <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>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

其中,${spring-cloud-alibaba.version} 和 ${spring-boot.version} 分别为 Spring Cloud Alibaba 和 Spring Boot 组件依赖的版本,它们的版本定义在 <properties>元素中,即 2.2.1.RELEASE2.3.0.RELEASE

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
        <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
    </properties>

如果读者非常熟悉 Maven 依赖管理的配置方式,可以考虑 Maven pom.xml 依赖 Nacos Config。

4.1.2 [高级] 通过 Maven pom.xml 依赖 Nacos Config

如果要在您的项目中使用 Nacos 来实现服务注册/发现,使用 group ID 为 com.alibaba.cloud和 artifact ID 为 spring-cloud-starter-alibaba-nacos-config的 starter。

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

该声明方式同样需要声明 com.alibaba.cloud:spring-cloud-alibaba-dependencies,内容与上小节相同,在此不再赘述。下一节将讨论如何使用 Nacos Config 支持分布式配置。

4.1.3 [偷懒] 直接在沙箱里查看应用代码

点击 链接,直接访问沙箱环境,这里会有为你准备好的案例代码^_^。

4.2 使用 Nacos Config 实现分布式配置

使用 Nacos Config 实现分布式配置与 Spring Cloud Consul 和 Spring Cloud Zookeeper 的方式非常类似,仅需添加相关外部化配置即可工作。换言之,Nacos Config 同样不会侵入应用代码,方便应用整合和迁移,如果读者熟悉 Spring Cloud Consul 或 Spring Cloud Zookeeper 使用方式 的话,通常需要将 Consul 或 Zookeeper 服务进程预先部署,Nacos Config 也如此。

4.2.1 启动 Nacos 服务器

我们为开发者提供了一套免费的 Nacos Server :进入 http://139.196.203.133:8848/nacos/ 查看控制台(账号名/密码为 nacos-configuration/nacos-configuration),选择 "配置管理/配置列表":

Spring Cloud Alibaba 七天训练营(二)分布式配置

由于服务是公共免费的,为了做好隔离,所以分布式配置的功能,请选择在 sandbox-configuration 的命名空间下操作。

具体启动方式参考 Nacos 官网
关于更多的 Nacos Server 版本,可以从 release 页面 下载最新的版本。

4.2.2 添加 Nacos 配置

点击“配置列表”页面右侧的 "+" 号(红色箭头所指):

Spring Cloud Alibaba 七天训练营(二)分布式配置

浏览器跳转新页面,并填充内容如下:

Spring Cloud Alibaba 七天训练营(二)分布式配置

其中,Data ID 由应用名(nacos-config-sample)+ 文件后缀名(.properties) 组成,点击“发布”按钮(红色箭头所指),配置内容为:

user.name=nacos-config-sample
user.age=90

发布成功后,控制台会出现提示弹出框。

特别提醒

图片中的 Data ID 中的应用名,使用的是 nacos-config-sample 。如果使用我们提供的 Nacos Server ,由于多个用户同时使用,可能存在应用名冲突的问题。建议修改为跟别人都不重复的应用名:
spring.application.name=xxxxxx
替换上面的xxxxx部分

4.2.3 配置应用 Nacos Config Server 地址

回到应用 nacos-config-sample 工程,在 resources 目录下新建名为 “application.properties" 文件,并配置以下内容:

spring.cloud.nacos.config.server-addr=139.196.203.133:8848
spring.cloud.nacos.config.username=nacos-configuration
spring.cloud.nacos.config.password=nacos-configuration
spring.cloud.nacos.config.namespace=sandbox-configuration

注意,Nacos Server 地址必须配置在 application.properties 文件。

注意当你使用域名的方式来访问 Nacos 时, spring.cloud.nacos.config.server-addr配置的方式为域名:port。 例如 Nacos 的域名为abc.com.nacos,监听的端口为 80,则 spring.cloud.nacos.config.server-addr=abc.com.nacos:80。 注意 80 端口不能省略。

4.2.4 添加读取 Nacos Config 实现

修改 nacos-config-sample 引导类,如下所示:

@SpringBootApplication
public class NacosConfigSampleApplication {

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

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

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }

    public static void main(String[] args) {
        SpringApplication.run(NacosConfigSampleApplication.class, args);
    }
}

4.2.5 启动 Nacos Config 应用

运行 nacos-config-sample 引导类 NacosConfigSampleApplication,观察控制台结果(截取关键日志信息):

[init] user name : nacos-config-sample , age : 90

如果在沙箱内启动应用,由于暂时还无法看到运行时的日志,可以通过WEB 控制器的方式查看参数,具体操作见 5.1 节。

5. 使用 Nacos Config 实现 Bean 动态刷新

Nacos Confg 支持标准 Spring Cloud @RefreshScope特性,即应用订阅某个 Nacos 配置后,当配置内容变化时,Refresh Scope Beans 中的绑定配置的属性将有条件的更新。所谓的条件是指 Bean 必须:

  • 必须条件:Bean 的声明类必须标注 @RefreshScope
  • 二选一条件:

    • 属性(非 static 字段)标注 @Value
    • @ConfigurationPropertiesBean

除此之外,Nacos Confg 也引入了 Nacos Client 底层数据变化监听接口,即 com.alibaba.nacos.api.config.listener.Listener。下面的内容将分别讨论这三种不同的使用场景。

Nacos Client:Nacos 客户端 API,也是 Nacos Config 底层依赖

5.1 使用 Nacos Config 实现 Bean @Value属性动态刷新

基于应用 nacos-config-sample 修改,将引导类 NacosConfigSampleApplication标注@RefreshScope@RestController,使得该类变为 Spring MVC REST 控制器,同时具备动态刷新能力,具体代码如下:

@SpringBootApplication
@RestController
@RefreshScope
public class NacosConfigSampleApplication {

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

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

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }

    @RequestMapping("/user")
    public String user() {
        return String.format("[HTTP] user name : %s , age : %d", userName, userAge);
    }

    public static void main(String[] args) {
        SpringApplication.run(NacosConfigSampleApplication.class, args);
    }
}

重启引导类 NacosConfigSampleApplication,控制台输出如故:

[init] user name : nacos-config-sample , age : 90

再通过命令行访问 REST 资源 /user

% curl http://127.0.0.1:8080/user
[HTTP] user name : nacos-config-sample , age : 90

如果使用沙箱环境,请直接点击应用列表访问按钮,并在打开的浏览器窗口的地址栏中追加/user,如下图:

本文中,其他的基于 http 访问的步骤类似,后面不在赘述。

本次请求结果中的 user name 和 age 数据与应用启动时的一致,因为此时 Nacos Server 中的配置数据没变化。

随后,通过 Nacos 控制台调整 nacos-config-sample.properties 配置,将 user.age 从 90 变更为 99:

Spring Cloud Alibaba 七天训练营(二)分布式配置

点击“发布”按钮,观察应用日志变化(部分内容被省略):

c.a.n.client.config.impl.ClientWorker    : [fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=4a8cb29154adb9a0e897e071e1ec8d3c, content=user.name=nacos-config-sample
user.age=99, type=properties
o.s.boot.SpringApplication               : Started application in 0.208 seconds (JVM running for 290.765)
o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [user.age]
  • 第 1 和 2 行代码是由 Nacos Client 输出,通知开发者具体的内容变化,不难发现,这里没有输出完整的配置内容,仅为变更部分,即配置 user.age。
  • 第 3 行日志似乎让 SpringApplication 重启了,不过消耗时间较短,这里暂不解释,后文将会具体讨论,只要知道这与 Bootstrap 应用上下文相关即可。
  • 最后一行日志是由 Spring Cloud 框架输出,提示开发人员具体变更的 Spring 配置 Property,可能会有多个,不过本例仅修改一处,所以显示单个。

接下来,重新访问 REST 资源 /user

% curl http://127.0.0.1:8080/user
[HTTP] user name : nacos-config-sample , age : 99

终端日志显示了这次配置变更同步到了 @Value("${user.age}") 属性 userAge 的内容。除此之外,应用控制台也输出了以下内容:

[init] user name : nacos-config-sample , age : 99

而该日志是由 init()方法输出,那么是否说明该方法被框架调用了呢?答案是肯定的。既然 @PostConstruct方法执行了,那么 @PreDestroy方法会不会被调用呢?不妨增加 Spring Bean 销毁回调方法:

@SpringBootApplication
@RestController
@RefreshScope
public class NacosConfigSampleApplication {

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

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

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }
    
    @PreDestroy
    public void destroy() {
        System.out.printf("[destroy] user name : %s , age : %d%n", userName, userAge);
    }

    ...
}

再次重启引导类 NacosConfigSampleApplication,初始化日志仍旧输出:

[init] user name : nacos-config-sample , age : 99

将配置 user.age 内容从 99 调整为 18,观察控制台日志变化:

c.a.n.client.config.impl.ClientWorker    : [fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=e25e486af432c403a16d5fc8a5aa4ab2, content=user.name=nacos-config-sample
user.age=18, type=properties
o.s.boot.SpringApplication               : Started application in 0.208 seconds (JVM running for 144.467)
[destroy] user name : nacos-config-sample , age : 99
o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [user.age]

相较于前一个版本,日志插入了 destroy()方法输出内容,并且Bean 属性 userAge 仍旧是变更前的数据 99。随后,再次访问 REST 资源 /user,其中终端日志:

% curl http://127.0.0.1:8080/user
[HTTP] user name : nacos-config-sample , age : 18

应用控制台日志:

[init] user name : nacos-config-sample , age : 18

两者与前一版本并无差异,不过新版本给出了一个现象,即当 Nacos Config 接收到服务端配置变更时,对应的 @RefreshScopeBean 生命周期回调方法会被调用,并且是先销毁,然后由重新初始化。本例如此设计,无非想提醒读者,要意识到 Nacos Config 配置变更对 @RefreshScopeBean 生命周期回调方法的影响,避免出现重复初始化等操作。

注: Nacos Config 配置变更调用了 Spring Cloud API ContextRefresher,该 API 会执行以上行为。同理,执行 Spring Cloud Acutator Endpoint refresh也会使用 ContextRefresher

通过上述讨论,相信读者已对 Nacos 配置变更操作相当的熟悉,后文将不再赘述相关配置。接下来继续讨论 @ConfigurationPropertiesBean 的场景。

5.2 使用 Nacos Config 实现

@ConfigurationPropertiesBean 属性动态刷新
在应用 nacos-config-sample 新增 User类,并标注 @RefreshScope@ConfigurationProperties,代码如下:

@RefreshScope
@ConfigurationProperties(prefix = "user")
public class User {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

根据 @ConfigurationProperties的定义, User类的属性绑定到了配置属性前缀 user。下一步,调整引导类,代码如下:

@SpringBootApplication
@RestController
@RefreshScope
@EnableConfigurationProperties(User.class)
public class NacosConfigSampleApplication {

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

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

    @Autowired
    private User user;

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }

    @PreDestroy
    public void destroy() {
        System.out.printf("[destroy] user name : %s , age : %d%n", userName, userAge);
    }

    @RequestMapping("/user")
    public String user() {
        return "[HTTP] " + user;
    }

    public static void main(String[] args) {
        SpringApplication.run(NacosConfigSampleApplication.class, args);
    }
}

较前一个版本 NacosConfigSampleApplication实现,主要改动点:

  • 激活 @ConfigurationPropertiesBean @EnableConfigurationProperties(User.class)
  • 通过 @Autowired依赖注入 UserBean
  • 使用 user Bean( toString() 方法替换 user()中的实现

下一步,重启应用后,再将 user.age 配置从 18 调整为 99,控制台日志输出符合期望:

[init] user name : nacos-config-sample , age : 18
......
[fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=b0f42fac52934faf69757c2b6770d39c, content=user.name=nacos-config-sample
user.age=90, type=properties
......
[destroy] user name : nacos-config-sample , age : 18
o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [user.age]

接下来,访问 REST 资源 /user,观察终端日志输出:

% curl http://127.0.0.1:8080/user
[HTTP] User{name='nacos-config-sample', age=90}

User Bean 属性成功地变更为 90,达到实战效果。上小节提到 Nacos Config 配置变更会影响 @RefreshScopeBean 的生命周期方法回调。同理,如果为 User增加初始化和销毁方法的话,也会出现行文,不过本次将 User实现 Spring 标准的生命周期接口 InitializingBeanDisposableBean

@RefreshScope
@ConfigurationProperties(prefix = "user")
public class User implements InitializingBean, DisposableBean {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("[afterPropertiesSet()] " + toString());
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("[destroy()] " + toString());
    }
}

代码调整后,重启应用,并修改配置(90 -> 19),观察控制台日志输出:

[init] user name : nacos-config-sample , age : 90
......
c.a.n.client.config.impl.ClientWorker    : [fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=30d26411b8c1ffc1d16b3f9186db498a, content=user.name=nacos-config-sample
user.age=19, type=properties
......
[destroy()] User{name='nacos-config-sample', age=90}
[afterPropertiesSet()] User{name='nacos-config-sample', age=19}
[destroy] user name : nacos-config-sample , age : 90
......
o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [user.age]

不难发现, UserBean 的生命周期方法不仅被调用,并且仍旧是先销毁,再初始化。那么,这个现象和之前看到的 SpringApplication重启是否有关系呢?答案也是肯定的,不过还是后文再讨论。

下一小节将继续讨论怎么利用底层 Nacos 配置监听实现 Bean 属性动态刷新

5.3 使用 Nacos Config 监听实现 Bean 属性动态刷新

前文曾提及 com.alibaba.nacos.api.config.listener.Listener是 Nacos Client API 标准的配置监听器接口,由于仅监听配置内容,并不能直接与 Spring 体系打通,因此,需要借助于 Spring Cloud Alibaba Nacos Config API NacosConfigManager(感谢小伙伴 liaochuntao 和 zkzlx 的代码贡献),代码调整如下:

@SpringBootApplication
@RestController
@RefreshScope
@EnableConfigurationProperties(User.class)
public class NacosConfigSampleApplication {

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

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

    @Autowired
    private User user;

    @Autowired
    private NacosConfigManager nacosConfigManager;

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            String dataId = "nacos-config-sample.properties";
            String group = "DEFAULT_GROUP";
            nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    System.out.println("[Listener] " + configInfo);
                }
            });
        };
    }

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }

    @PreDestroy
    public void destroy() {
        System.out.printf("[destroy] user name : %s , age : %d%n", userName, userAge);
    }

    @RequestMapping("/user")
    public String user() {
        return "[HTTP] " + user;
    }

    public static void main(String[] args) {
        SpringApplication.run(NacosConfigSampleApplication.class, args);
    }
}

代码主要变化:

  • @Autowired依赖注入 NacosConfigManager
  • 新增 runner()方法,通过 NacosConfigManagerBean 获取 ConfigService,并增加了 AbstractListener( Listener抽象类)实现,监听 dataId = "nacos-config-sample.properties" 和 group = "DEFAULT_GROUP" 的配置内容

重启应用,并将配置 user.age 从 19 调整到 90,观察日志变化:

c.a.n.client.config.impl.ClientWorker    : [fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=b0f42fac52934faf69757c2b6770d39c, content=user.name=nacos-config-sample
user.age=90, type=properties
[Listener] user.name=nacos-config-sample
user.age=90
......

在第 1 行日志下方,新增了监听实现代码的输出内容,不过这段内容是完整的配置,而非变化的内容。读者请务必注意其中的差异。下一步要解决的是将配置映射到 Bean 属性,此处给出一个简单的解决方案,实现步骤有两个:

  • 将 String 内容转化为 Properties 对象
  • 将 Properties 属性值设置到对应的 Bean 属性

代码调整如下:

@SpringBootApplication
@RestController
@RefreshScope
@EnableConfigurationProperties(User.class)
public class NacosConfigSampleApplication {
    
    ......

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            String dataId = "nacos-config-sample.properties";
            String group = "DEFAULT_GROUP";
            nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    System.out.println("[Listener] " + configInfo);
                    System.out.println("[Before User] " + user);

                    Properties properties = new Properties();
                    try {
                        properties.load(new StringReader(configInfo));
                        String name = properties.getProperty("user.name");
                        int age = Integer.valueOf(properties.getProperty("user.age"));
                        user.setName(name);
                        user.setAge(age);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    System.out.println("[After User] " + user);
                }
            });
        };
    }

    ......
        
}

重启应用,并将配置 user.age 从 90 调整到 19,观察日志变化:

[Listener] user.name=nacos-config-sample
user.age= 19
[Before User] User{name='nacos-config-sample', age=90}
[After User] User{name='nacos-config-sample', age=19}

上述三个例子均围绕着 Nacos Config 实现 Bean 属性动态更新,不过它们是 Spring Cloud 使用场景。如果读者的应用仅使用 Spring 或者 Spring Boot,可以考虑 Nacos Spring 工程, Github 地址:https://github.com/nacos-group/nacos-spring-project,其中 @NacosValue支持属性粒度的更新。

6. Nacos Config 高级配置

6.1 支持自定义 namespace 的配置

首先看一下 Nacos 的 Namespace 的概念, Nacos 概念
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。

在没有明确指定 ${spring.cloud.nacos.config.namespace}配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。如果需要使用自定义的命名空间,可以通过以下配置来实现:

spring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7

注:该配置必须放在 bootstrap.properties 文件中。此外 spring.cloud.nacos.config.namespace的值是 namespace 对应的 id,id 值可以在 Nacos 的控制台获取。并且在添加配置时注意不要选择其他的 namespae,否则将会导致读取不到正确的配置

6.2 支持自定义 Group 的配置

在没有明确指定 ${spring.cloud.nacos.config.group}配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group,可以通过以下配置来实现:

spring.cloud.nacos.config.group=DEVELOP_GROUP

注:该配置必须放在 bootstrap.properties 文件中。并且在添加配置时 Group 的值一定要和 spring.cloud.nacos.config.group的配置值一致。

6.3 支持自定义扩展的 Data Id 配置

Spring Cloud Alibaba Nacos Config 从 0.2.1 版本后,可支持自定义 Data Id 的配置。关于这部分详细的设计可参考 这里。 一个完整的配置案例如下所示:

spring.application.name=opensource-service-provider
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# config external configuration
# 1、Data Id 在默认的组 DEFAULT_GROUP,不支持配置的动态刷新
spring.cloud.nacos.config.extension-configs[0].data-id=ext-config-common01.properties
# 2、Data Id 不在默认的组,不支持动态刷新
spring.cloud.nacos.config.extension-configs[1].data-id=ext-config-common02.properties
spring.cloud.nacos.config.extension-configs[1].group=GLOBALE_GROUP
# 3、Data Id 既不在默认的组,也支持动态刷新
spring.cloud.nacos.config.extension-configs[2].data-id=ext-config-common03.properties
spring.cloud.nacos.config.extension-configs[2].group=REFRESH_GROUP
spring.cloud.nacos.config.extension-configs[2].refresh=true

可以看到:

  • 通过 spring.cloud.nacos.config.extension-configs[n].data-id的配置方式来支持多个 Data Id 的配置。
  • 通过 spring.cloud.nacos.config.extension-configs[n].group的配置方式自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。
  • 通过 spring.cloud.nacos.config.extension-configs[n].refresh的配置方式来控制该 Data Id 在配置变更时,是否支持应用中可动态刷新, 感知到最新的配置值。默认是不支持的。

注:多个 Data Id 同时配置时,他的优先级关系是 spring.cloud.nacos.config.extension-configs[n].data-id其中 n 的值越大,优先级越高。
spring.cloud.nacos.config.extension-configs[n].data-id的值必须带文件扩展名,文件扩展名既可支持 properties,又可以支持 yaml/yml。 此时 spring.cloud.nacos.config.file-extension的配置对自定义扩展配置的 Data Id 文件扩展名没有影响。

通过自定义扩展的 Data Id 配置,既可以解决多个应用间配置共享的问题,又可以支持一个应用有多个配置文件。

为了更加清晰的在多个应用间配置共享的 Data Id ,你可以通过以下的方式来配置:

# 配置支持共享的 Data Id
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml
# 配置 Data Id 所在分组,缺省默认 DEFAULT_GROUP
spring.cloud.nacos.config.shared-configs[0].group=GROUP_APP1
# 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false
spring.cloud.nacos.config.shared-configs[0].refresh=true

可以看到:

  • 通过 spring.cloud.nacos.config.shared-configs[n].data-id来支持多个共享 Data Id 的配置。
  • 通过 spring.cloud.nacos.config.shared-configs[n].group来配置自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。
  • 通过 spring.cloud.nacos.config.shared-configs[n].refresh来控制该Data Id在配置变更时,是否支持应用中动态刷新,默认false。

6.4 配置的优先级

Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。

  • A: 通过 spring.cloud.nacos.config.shared-configs[n].data-id支持多个共享 Data Id 的配置
  • B: 通过 spring.cloud.nacos.config.extension-configs[n].data-id的方式支持多个扩展 Data Id 的配置
  • C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置

当三种方式共同使用时,他们的一个优先级关系是:A < B < C

6.5 完全关闭配置

通过设置 spring.cloud.nacos.config.enabled = false 来完全关闭 Spring Cloud Nacos Config

6.6 更多高级配置

更多关于 Nacos Config Starter 的配置项如下所示:

配置项 Key 默认值 说明
服务端地址 spring.cloud.nacos.config.server-addr Nacos Server 启动监听的ip地址和端口
配置对应的 DataId spring.cloud.nacos.config.name 先取 prefix,再取 name,最后取 spring.application.name
配置对应的 DataId spring.cloud.nacos.config.prefix 先取 prefix,再取 name,最后取 spring.application.name
配置内容编码 spring.cloud.nacos.config.encode 读取的配置内容对应的编码
GROUP spring.cloud.nacos.config.group DEFAULT_GROUP 配置对应的组
文件扩展名 spring.cloud.nacos.config.fileExtension properties 配置项对应的文件扩展名,目前支持 properties 和 yaml(yml)
获取配置超时时间 spring.cloud.nacos.config.timeout 3000 客户端获取配置的超时时间(毫秒)
接入点 spring.cloud.nacos.config.endpoint 地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
命名空间 spring.cloud.nacos.config.namespace 常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等
AccessKey spring.cloud.nacos.config.accessKey 当要上阿里云时,阿里云上面的一个云账号名
SecretKey spring.cloud.nacos.config.secretKey 当要上阿里云时,阿里云上面的一个云账号密码
Nacos Server 对应的 context path spring.cloud.nacos.config.contextPath Nacos Server 对外暴露的 context path
集群 spring.cloud.nacos.config.clusterName 配置成Nacos集群名称
共享配置 spring.cloud.nacos.config.sharedDataids 共享配置的 DataId, "," 分割
共享配置动态刷新 spring.cloud.nacos.config.refreshableDataids 共享配置中需要动态刷新的 DataId, "," 分割
自定义 Data Id 配置 spring.cloud.nacos.config.extConfig 属性是个集合,内部由 ConfigPOJO 组成。Config有 3 个属性,分别是 dataId, group以及 refresh

7. Nacos Config Actuator Endpoint

Nacos Config 内部提供了一个 Endpoint, 对应的 Endpoint ID 为 nacos-config,其 Actuator Web Endpoint URI 为 /actuator/nacos-config

注:使用 Nacos Config Spring Cloud 1.x 版本的话,其 URI 地址则为 /nacos-config

其中,Endpoint 暴露的 json 中包含了三种属性:

  • NacosConfigProperties: 当前应用 Nacos 的基础配置信息
  • RefreshHistory: 配置刷新的历史记录
  • Sources: 当前应用配置的数据信息

由于 Aliyun Java Initializr 所生成的应用工程默认激活 Spring Boot Actuator Endpoints(JMX 和 Web),具体配置存放在 application.properties文件中,同时,Actuator Web 端口设置为 8081,内容如下:

management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# Actuator Web 访问端口
management.server.port=8081

因此,应用 nacos-config-sample 无需调整,直接访问:http://127.0.0.1:8081/actuator/nacos-config,服务响应的内容如下:

{
  "NacosConfigProperties": {
    "serverAddr": "127.0.0.1:8848",
    "username": "",
    "password": "",
    "encode": null,
    "group": "DEFAULT_GROUP",
    "prefix": null,
    "fileExtension": "properties",
    "timeout": 3000,
    "maxRetry": null,
    "configLongPollTimeout": null,
    "configRetryTime": null,
    "enableRemoteSyncConfig": false,
    "endpoint": null,
    "namespace": null,
    "accessKey": null,
    "secretKey": null,
    "contextPath": null,
    "clusterName": null,
    "name": null,
    "sharedConfigs": null,
    "extensionConfigs": null,
    "refreshEnabled": true,
    "sharedDataids": null,
    "refreshableDataids": null,
    "extConfig": null,
    "configServiceProperties": {
      "secretKey": "",
      "namespace": "",
      "username": "",
      "enableRemoteSyncConfig": "false",
      "configLongPollTimeout": "",
      "configRetryTime": "",
      "encode": "",
      "serverAddr": "127.0.0.1:8848",
      "maxRetry": "",
      "clusterName": "",
      "password": "",
      "accessKey": "",
      "endpoint": ""
    }
  },
  "RefreshHistory": [
    
  ],
  "Sources": [
    {
      "lastSynced": "2020-09-14 11:11:37",
      "dataId": "nacos-config-sample.properties"
    },
    {
      "lastSynced": "2020-09-14 11:11:37",
      "dataId": "nacos-config-sample"
    }
  ]
}

对文档有任何问题,请在评论区留言!

上一篇:Spring Cloud Alibaba 七天训练营(三)服务注册与发现


下一篇:storm 实战及实例讲解(一)