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。

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,为什么这样命名等下再解释。

config.yaml配置文件内容如下所示:
server:
port: 9000
kaven: "hello kaven"

启动config服务,很显然服务成功获取了config.yaml配置文件(不然端口默认是8080)。

config服务也在Nacos上注册成功了。

请求config服务的接口(http://localhost:9000/config),显示config.yaml配置文件中的内容。

修改Nacos中config.yaml配置文件kaven参数的值。

config服务会感知到config.yaml配置文件的更新。

再次请求config服务的接口,就会显示更新后的值了。

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}
这里博主来验证一下config服务是否会去获取config配置文件,将config.yaml配置文件中的kaven参数删除,再创建config配置文件,并且在config配置文件中添加kaven参数,如果config服务没有获取config配置文件,就会报错。


重新启动config服务,服务并没有报错。

请求config服务的接口,显示config配置文件中的内容。

所以服务会去获取如下所列的这些配置文件(如果存在,配置文件的优先级规则也是这个顺序,从高到低,大家可以自己去测试一下):
${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])。


Config类(dataId和refresh需要进行设置,group使用默认值即可):
public static class Config {
/**
* 扩展配置的数据ID
*/
private String dataId;
/**
* 扩展配置组,默认值为DEFAULT_GROUP
*/
private String group = "DEFAULT_GROUP";
/**
* 是否支持动态刷新,默认不支持
*/
private boolean refresh = false;
...
}在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):

很显然这些配置文件获取成功了。
这些配置文件的优先级(优先级从高到低):
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'}]Nacos配置中心的原理博主以后再进行介绍,Nacos配置中心就介绍到这里,之后会介绍Nacos的其他特性以及高可用。
如果博主有说错的地方或者大家有不同的见解,欢迎大家评论补充。










