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
的其他特性以及高可用。
如果博主有说错的地方或者大家有不同的见解,欢迎大家评论补充。