天天看点

Spring Cloud Alibaba:Nacos配置中心

Spring Cloud Alibaba:Nacos配置中心

动态配置服务可以以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。​

​Nacos​

​​提供了一个简洁易用的​

​UI​

​​ 帮助管理所有的服务和应用的配置。​

​Nacos​

​还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,帮助更安全地在生产环境中管理配置变更和降低配置变更带来的风险。

博主之前介绍过​

​Spring Cloud​

​​提供的配置中心​

​Config​

​组件:

  • ​​Spring Cloud 之Config配置中心-使用Bus组件实现配置动态更新​​

但​

​Config​

​​组件是不负责存储和管理配置文件的(先不管配置文件的缓存),配置文件存储在第三方平台上(如​

​Github​

​​),并且该平台需要有​

​Webhook​

​​的功能,当配置文件被修改后,该平台通过​

​Webhook​

​​的功能去回调​

​Config Server​

​​的接口,通知​

​Config Server​

​​配置文件更新了,之后​

​Config Server​

​​还需要使用​

​MQ​

​​将修改的配置文件传输给相应的​

​Config Client​

​​,因此​

​Config Client​

​​也需要与​

​MQ​

​​绑定。看起来​

​Config​

​​组件并不是很灵活,博主接下来会介绍使用​

​Nacos​

​​作为配置中心,​

​Nacos​

​​不同于​

​Config​

​​,​

​Nacos​

​​本身负责存储和管理配置文件,因此不需要第三方平台的介入,通过结合​

​push​

​​和​

​pull​

​​两种方式来实现动态配置服务,并且还提供简洁易用的​

​UI​

​ 帮助管理所有的服务和应用的配置。

上一篇博客中已经介绍了使用​

​Nacos​

​​作为服务注册与发现中心,关于​

​Nacos​

​服务的安装与运行请参考下面这篇博客:

  • ​​Spring Cloud Alibaba:Nacos服务注册与发现​​

创建服务

创建​

​AlibabaBlog maven​

​​工程作为父​

​module​

​​,再创建​

​config​

​​子​

​module​

​。

Spring Cloud Alibaba:Nacos配置中心

​​

​AlibabaBlog module​

​pom.xml​

​:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.kaven</groupId>
    <artifactId>AlibabaBlog</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <description>Spring Cloud Alibaba</description>
    <modules>
        <module>config</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
    </parent>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <spring-cloud-alibaba-version>2.2.6.RELEASE</spring-cloud-alibaba-version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </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>
        </dependencies>
    </dependencyManagement>
</project>      

​config module​

​pom.xml​

​:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>AlibabaBlog</artifactId>
        <groupId>com.kaven</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>config</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

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

​bootstrap.yml​

​​(这里不是​

​application.yml​

​):

spring:
  application:
    name: config
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml      

​ConfigController​

​接口类:

package com.kaven.alibaba.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;

/**
 * @Author: ITKaven
 * @Date: 2021/11/09 12:57
 * @Leetcode: https://leetcode-cn.com/u/kavenit
 * @Notes:
 */

@RestController
@RefreshScope
public class ConfigController {

    @Value("${kaven}")
    private String kaven;

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

​@RefreshScope​

​​注解需要加上,以便让​

​config​

​服务感知到这里有需要动态更新的配置文件参数。

​ConfigApplication​

​启动类:

package com.kaven.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @Author: ITKaven
 * @Date: 2021/11/09 11:43
 * @Leetcode: https://leetcode-cn.com/u/kavenit
 * @Notes:
 */

@SpringBootApplication
@EnableDiscoveryClient
public class ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class);
    }
}      

​@EnableDiscoveryClient​

​注解要加上。

为什么是​

​bootstrap.yml​

​​而不是​

​application.yml​

​:

​Spring Cloud​

​​构建于​

​Spring Boot​

​​之上,在​

​Spring Boot​

​​中有两种上下文,一种是​

​bootstrap​

​​, 另外一种是​

​application​

​​, ​

​bootstrap​

​​是应用程序的父上下文,也就是说​

​bootstrap​

​​加载优先于​

​applicaton​

​​。​

​bootstrap​

​​主要用于从外部资源中加载配置信息。这两个上下文共用一个环境,它是任何​

​Spring​

​​应用程序的外部属性的来源。​

​bootstrap​

​里面的属性会优先加载,它们默认也不能被本地相同配置覆盖。

配置文件

在​

​Nacos​

​​上增加一个配置文件​

​config.yaml​

​,为什么这样命名等下再解释。

Spring Cloud Alibaba:Nacos配置中心

​config.yaml​

​配置文件内容如下所示:

server:
  port: 9000

kaven: "hello kaven"      
Spring Cloud Alibaba:Nacos配置中心

启动​

​config​

​​服务,很显然服务成功获取了​

​config.yaml​

​​配置文件(不然端口默认是​

​8080​

​)。

Spring Cloud Alibaba:Nacos配置中心

​config​

​​服务也在​

​Nacos​

​上注册成功了。

Spring Cloud Alibaba:Nacos配置中心

请求​

​config​

​​服务的接口(​

​http://localhost:9000/config​

​​),显示​

​config.yaml​

​配置文件中的内容。

Spring Cloud Alibaba:Nacos配置中心

修改​

​Nacos​

​​中​

​config.yaml​

​​配置文件​

​kaven​

​参数的值。

Spring Cloud Alibaba:Nacos配置中心

​config​

​​服务会感知到​

​config.yaml​

​配置文件的更新。

Spring Cloud Alibaba:Nacos配置中心

再次请求​

​config​

​服务的接口,就会显示更新后的值了。

Spring Cloud Alibaba:Nacos配置中心

Data ID

在​

​Nacos​

​​中,​

​Data ID​

​的完整格式如下:

${prefix}-${spring.profiles.active}.${file-extension}      

​prefix​

​​默认为​

​spring.application.name​

​​的值,也可以通过配置项​

​spring.cloud.nacos.config.prefix​

​​来配置。​

​spring.profiles.active​

​​即为当前环境对应的​

​profile​

​​(为空时,对应的连接符​

​-​

​​也将不存在,因此​

​Data ID​

​的拼接格式变成:

${prefix}.${file-extension}      

​file-exetension​

​​为配置文件的后缀,可以通过配置项​

​spring.cloud.nacos.config.file-extension​

​来配置。

​config​

​​服务没有设置​

​profile​

​​,并且​

​prefix​

​​为​

​config​

​​(默认为​

​spring.application.name​

​​的值),​

​file-extension​

​​为​

​yaml​

​​,因此​

​config.yaml​

​​配置文件会被​

​config​

​服务获取到。

但通过​

​config​

​​服务的后台可以看见,​

​config​

​​服务会去获取​

​config​

​​和​

​config.yaml​

​​这两个配置文件。很显然​

​config​

​​是​

​prefix​

​的值,并且不加如下文件后缀:

.${file-extension}      
Spring Cloud Alibaba:Nacos配置中心

这里博主来验证一下​

​config​

​​服务是否会去获取​

​config​

​​配置文件,将​

​config.yaml​

​​配置文件中的​

​kaven​

​​参数删除,再创建​

​config​

​​配置文件,并且在​

​config​

​​配置文件中添加​

​kaven​

​​参数,如果​

​config​

​​服务没有获取​

​config​

​配置文件,就会报错。

Spring Cloud Alibaba:Nacos配置中心
Spring Cloud Alibaba:Nacos配置中心

重新启动​

​config​

​服务,服务并没有报错。

Spring Cloud Alibaba:Nacos配置中心

请求​

​config​

​​服务的接口,显示​

​config​

​配置文件中的内容。

Spring Cloud Alibaba:Nacos配置中心

所以服务会去获取如下所列的这些配置文件(如果存在,配置文件的优先级规则也是这个顺序,从高到低,大家可以自己去测试一下):

${prefix}-${spring.profiles.active}.${file-extension}
${prefix}.${file-extension}
${prefix}      

共享配置文件

如果一些服务中的某些配置是相同的,比如​

​Redis​

​​或者​

​MQ​

​​等中间件集群的配置,如果没有共享配置文件,这些服务的配置文件中都要重复这些配置,而当服务数量特别多时,这就不方便配置文件的管理了,比如当​

​Redis​

​​集群配置发生改变,就需要修改每个依赖该​

​Redis​

​集群的服务的配置文件,这需要很大的人力成本和时间成本,所以共享配置文件是有必要的。

而在​

​Nacos​

​​中实现共享配置文件也特别方便,修改​

​config​

​​服务的​

​bootstrap.yml​

​配置文件:

spring:
  application:
    name: config
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        prefix: config
        shared-configs[0]:
          data-id: redis.yaml
          refresh: true
        extension-configs[0]:
          data-id: mq.yaml
          refresh: true
  profiles:
    active: test      

其中​

​shared-configs​

​​和​

​extension-configs​

​这两个配置都可以实现共享配置文件的功能。

shared-configs[0]:
          data-id: redis.yaml
          refresh: true
        extension-configs[0]:
          data-id: mq.yaml
          refresh: true      

并且​

​shared-configs​

​​和​

​extension-configs​

​​都是​

​List​

​​类型的数据,因此在它们的后面都加上了​

​[0]​

​​下标(从​

​0​

​​开始,下一个就是​

​[1]​

​)。

Spring Cloud Alibaba:Nacos配置中心
Spring Cloud Alibaba:Nacos配置中心

​Config​

​​类(​

​dataId​

​​和​

​refresh​

​​需要进行设置,​

​group​

​使用默认值即可):

public static class Config {

    /**
     * 扩展配置的数据ID
     */
    private String dataId;

    /**
     * 扩展配置组,默认值为DEFAULT_GROUP
     */
    private String group = "DEFAULT_GROUP";

    /**
     * 是否支持动态刷新,默认不支持
     */
    private boolean refresh = false;

        ...
  }      

在​

​Nacos​

​中创建这些配置文件:

Spring Cloud Alibaba:Nacos配置中心

配置文件内容依次如下:

config.yaml: "config.yaml"
config: "config"
config-test.yaml: "config-test.yaml"
redis.yaml: "redis.yaml"
mq.yaml: "mq.yaml"      

修改​

​config​

​服务的接口:

package com.kaven.alibaba.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;

/**
 * @Author: ITKaven
 * @Date: 2021/11/09 12:57
 * @Leetcode: https://leetcode-cn.com/u/kavenit
 * @Notes:
 */

@RestController
@RefreshScope
public class ConfigController {

    @Value("${config.yaml}")
    private String configYaml;

    @Value("${config}")
    private String config;

    @Value("${config-test.yaml}")
    private String configTestYaml;

    @Value("${redis.yaml}")
    private String redisYaml;

    @Value("${mq.yaml}")
    private String mqYaml;


    @GetMapping("/config")
    public String getConfig() {
        return configYaml.concat(" ")
                .concat(config).concat(" ")
                .concat(configTestYaml).concat(" ")
                .concat(redisYaml).concat(" ")
                .concat(mqYaml);
    }
}      

请求​

​config​

​​服务的接口(配置文件中没有配置服务端口,因此是默认端口​

​8080​

​):

Spring Cloud Alibaba:Nacos配置中心

很显然这些配置文件获取成功了。

这些配置文件的优先级(优先级从高到低):

config-test.yaml
config.yaml
config
mq.yaml
redis.yaml      

因此配置文件的优先级规则如下(如果存在,优先级从高到低):

${prefix}-${spring.profiles.active}.${file-extension}
${prefix}.${file-extension}
${prefix}
${extension-configs}
${shared-configs}      

在​

​config​

​服务的日志输出中也有体现:

Located property source: [BootstrapPropertySource {name='bootstrapProperties-config-test.yaml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-config.yaml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-config,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-mq.yaml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-redis.yaml,DEFAULT_GROUP'}]