服务配置
一、服务配置中⼼介绍
⾸先我们来看⼀下,微服务架构下关于配置⽂件的⼀些问题:
-
配置⽂件相对分散。在⼀个微服务架构下,配置⽂件会随着微服务的增多变的越来越多,⽽且分散在各个微服 务中,不好统⼀配置和管理。
-
配置⽂件⽆法区分环境。微服务项⽬可能会有多个环境。例如:测试环境、预发布环境、⽣产环境。每⼀个环 境所使⽤的配置理论上都是不同的,⼀旦需要修改,就需要我们去各个微服务下⼿动维护,这⽐较困难。
-
配置⽂件⽆法实时更新。我们修改了配置⽂件之后,必须重新启动微服务才能使配置⽣效,这对⼀个正在运⾏ 的项⽬来说是⾮常不友好的。
基于上⾯这些问题,我们就需要配置中⼼的加⼊来解决这些问题。
配置中⼼的思路是:
-
⾸先把项⽬中各种配置全部都放到⼀个集中的地⽅进⾏统⼀管理,并提供⼀套标准的接⼝。
-
当各个服务需要获取配置的时候,就来配置中⼼的接⼝拉取⾃⼰的配置。
-
当配置中⼼中的各种参数有更新的时候,也能通知到各个服务实时的过来同步最新的信息,使之动态更新。 当加⼊了服务配置中⼼之后,我们的系统架构图会变成下⾯这样:
在业界常⻅的服务配置中⼼,有下⾯这些:
- Apollo
Apollo 是由携程开源的分布式配置中⼼。特点有很多,⽐如:
配置更新之后可以实时⽣效,⽀持灰度发布功能,并且能对所有的配置进⾏版本管理、操作审计等功能,提供开放 平台API。并且资料也写的很详细。
- Disconf
Disconf 是由百度开源的分布式配置中⼼。它是基于 Zookeeper 来实现配置变更后实时通知和⽣效的。
- SpringCloud Config
这是 Spring Cloud 中带的配置中⼼组件。它和 Spring 是⽆缝集成,使⽤起来⾮常⽅便,并且它的配置存储⽀ 持 Git 。
不过它没有可视化的操作界⾯,配置的⽣效也不是实时的,需要重启或去刷新。
- Nacos
这是 SpingCloud alibaba 技术栈中的⼀个组件,前⾯我们已经使⽤它做过服务注册中⼼。其实它也集成了服务 配置的功能,我们可以直接使⽤它作为服务配置中⼼。
二、Nacos Config 介绍
Nacos 是阿⾥巴巴开源的⼀个更易于构建云原⽣应⽤的动态服务发现、配置管理和服务管理平台。 NacosConfig 就是⼀个类似于 SpringCloud Config 的配置中⼼。
使⽤ nacos 作为配置中⼼,其实就是将 nacos 当 做⼀个服务端,将各个微服务看成是客户端,我们将各个微服务的配置⽂件统⼀存放在 nacos 上,然后各个微服务从 nacos 上拉取配置即可。
官⽅⽂档:https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config
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 Config ⼊⻔
使⽤ nacos 作为配置中⼼,其实就是将 nacos 当做⼀个服务端,将各个微服务看成是客户端,我们将各个微服 务的配置⽂件统⼀存放在 nacos 上,然后各个微服务从 nacos 上拉取配置即可。
创建发布
搭建项目
引⼊依赖
<dependencies>
<!-- 配置中心 -->
<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>
<!-- Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 发现服务 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
添加 config 的配置
Data ID: nacos-config.properties
Group : DEFAULT_GROUP
配置格式: Properties
配置内容: user.name=nacos-config-properties
user.age=90
注意: dataid 是以 properties (默认的⽂件扩展名⽅式)为扩展名。
书写配置⽂件
在运⾏此 Example 之前, 必须使⽤ bootstrap.properties 配置⽂件来配置 Nacos Server 地址,例如:
spring.application.name=nacos-config ##这⾥名字必须同 DataID⼀致
spring.cloud.nacos.config.server-addr=192.168.65.3:8848
spring.cloud.nacos.config.namespace=0e2f3694-d3ef-4a39-8aa6-5622872228ce ##这⾥如果是public的请不要书写
application.yml
server:
port: 8031
启动类
@SpringBootApplication
public class SpringCloudExampleConfigApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringCloudExampleConfigApplication.class, args);
String s1 = context.getEnvironment().getProperty("user.name");
String s2 = context.getEnvironment().getProperty("user.age");
System.out.println(s1 + "," + s2);
}
}
测试结果
出现错误
修改(注册一下)
server:
port: 8031
spring:
cloud:
nacos:
discovery:
namespace: 0e2f3694-d3ef-4a39-8aa6-5622872228ce
server-addr: 192.168.65.3:8848
再次测试
四、Nacos Config 深⼊
基于 yaml 的⽂件扩展名配置⽅式
以 bootstrap.properties ⽅式引导
spring-cloud-starter-alibaba-nacos-config 对于 yaml 格式也是完美⽀持的。这个时候只需要完成以下 两步:
1、在应⽤的 bootstrap.properties 配置⽂件中显示的声明 dataId ⽂件扩展名。如下所示
spring.cloud.nacos.config.file-extension=yaml
2、在 Nacos 的控制台新增⼀个 dataId 为 yaml 为扩展名的配置,如下所示:
这两步完成后,重启测试程序,可以看到如下输出结果。
bootstrap.yml ⽅式
注:将刚才的bootstrap.properties删除
点击配置列表,点击右边+号,新建配置。在新建配置过程中,要注意下⾯的细节:
- Data ID 不能随便写,要跟配置⽂件中的对应,对应关系如图所示
- 配置⽂件格式要跟配置⽂件的格式对应,且⽬前仅仅⽀持 YAML 和 Properties
- 配置内容按照上⾯选定的格式书写
重启测试程序,可以看到如下输出结果:
注意:如果使⽤默认命名空间请不要书写!
配置动态刷新
在⼊⻔案例中,我们实现了配置的远程存放,但是此时如果修改了配置,我们的程序是⽆法读取到的,因此,我们 需要开启配置的动态刷新功能。
硬编码⽅式
spring-cloud-starter-alibaba-nacos-config 也⽀持配置的动态更新,启动 Spring Boot 应⽤测试的代 码如下:
@SpringBootApplication
public class SpringCloudExampleConfigApplication {
@SneakyThrows
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringCloudExampleConfigApplication.class, args);
// String s1 = context.getEnvironment().getProperty("user.name");
// String s2 = context.getEnvironment().getProperty("user.age");
// System.out.println(s1 + "," + s2);
while (true) {
String s1 = context.getEnvironment().getProperty("user.name");
String s2 = context.getEnvironment().getProperty("user.age");
System.out.println(s1 + "," + s2);
Thread.sleep(1000);
}
}
}
注意:你可以通过配置 spring.cloud.nacos.config.refresh.enabled=false 来关闭动态刷新。
application.yml
server:
port: 8031
spring:
cloud:
nacos:
discovery:
namespace: 0e2f3694-d3ef-4a39-8aa6-5622872228ce
server-addr: 192.168.65.3:8848
refresh-enabled: true #是否支持自动刷新
动态刷新成功!
注解⽅式
注释其内容
@SpringBootApplication
public class SpringCloudExampleConfigApplication {
@SneakyThrows
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringCloudExampleConfigApplication.class, args);
// String s1 = context.getEnvironment().getProperty("user.name");
// String s2 = context.getEnvironment().getProperty("user.age");
// System.out.println(s1 + "," + s2);
// while (true) {
// String s1 = context.getEnvironment().getProperty("user.name");
// String s2 = context.getEnvironment().getProperty("user.age");
// System.out.println(s1 + "," + s2);
// Thread.sleep(1000);
// }
}
}
controller包
@Slf4j
@RequestMapping("/config")
@RestController
@RefreshScope //只需要在需要动态读取配置的类上添加此注解就可以
public class ConfigController {
@Value("${user.name}")
private String name;
@Value("${user.age}")
private Integer age;
@GetMapping("/a")
public String a() {
log.info("name:{},age:{}", name, age);
return "name:" + name + ",age:" + age;
}
}
当变更 user.name 时,应⽤程序中能够获取到最新的值。
测试结果
改变内容
注意格式:填写错误会报异常
再次测试
可⽀持profile粒度的配置
spring-cloud-starter-alibaba-nacos-config 在加载配置的时候,
不仅仅加载了以 dataid 为 s p r i n g . a p p l i c a t i o n . n a m e . {spring.application.name}. spring.application.name.{file-extension:properties} 为前缀的基础配置,
还加载了 dataid 为 s p r i n g . a p p l i c a t i o n . n a m e − {spring.application.name}- spring.application.name−{profile}.${file-extension:properties} 的基础配置。
如果想在同⼀个微服务的不同环境之间实现配置共享,其实很简单。只需要提取⼀个以 spring.application.name 命名的配置⽂件,通过 Spring 提供的 ${spring.profiles.active} 这个配置项来配置。,然后将其所有环境的公共配置放在⾥⾯即可。
新增配置 Nacos 上新增三个 ,如下所示:
bootstrap.yml 配置
1、
spring:
profiles:
active: dev
重启项目,页面刷新
2、
spring:
profiles:
active: test
重启项目,页面刷新
3、
spring:
profiles:
active: product
⽀持⾃定义 namespace 的配置
⾸先看⼀下 Nacos 的 Namespace 的概念:
⽤于进⾏租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配 置。 Namespace 的常⽤场景之⼀是不同环境的配置的区分隔离,例如开发测试环境和⽣产环境的资源(如配 置、服务)隔离等。
在没有明确指定 ${spring.cloud.nacos.config.namespace} 配置的情况下, 默认使⽤的是 Nacos 上 Public 这个 namespae 。如果需要使⽤⾃定义的命名空间,可以通过以下配置来实现:
spring.cloud.nacos.config.namespace=0e2f3694-d3ef-4a39-8aa6-5622872228ce
需要注意的是:该配置必须放在 bootstrap.properties|yaml ⽂件中。此外 spring.cloud.nacos.config.namespace 的值是 namespace 对应的 id , id 值可以在 Nacos 的控制 台获取。并且在添加配置时注意不要选择其他的 namespae ,否则将会导致读取不到正确的配置。
⽀持⾃定义 Group 的配置
在没有明确指定 ${spring.cloud.nacos.config.group} 配置的情况下, 默认使⽤的是 DEFAULT_GROUP 。如 果需要⾃定义⾃⼰的 Group ,可以通过以下配置来实现:
spring.cloud.nacos.config.group=DEVELOP_GROUP
需要注意的是:该配置必须放在 bootstrap.properties|yaml ⽂件中。并且在添加配置时 Group 的值⼀ 定要和 spring.cloud.nacos.config.group 的配置值⼀致。
spring:
application:
name: nacos-config
cloud:
nacos:
config:
namespace: 0e2f3694-d3ef-4a39-8aa6-5622872228ce
server-addr: 192.168.65.3:8848
file-extension: yaml
refresh-enabled: true
group: TEMA01
# profiles:
# active: dev
测试结果:
⽀持⾃定义扩展的 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 在 配置变更时,是否⽀持应⽤中可动态刷新, 感知到最新的配置值。默认是不⽀持的。
yaml 格式的应写成示例:
1、先创建
controller包
@Slf4j
@RequestMapping("/config")
@RestController
@RefreshScope //只需要在需要动态读取配置的类上添加此注解就可以
public class ConfigController {
@Value("${user.name}")
private String name;
@Value("${user.age}")
private Integer age;
@Value("${student.name}")
private String stuName;
@GetMapping("/a")
public String a() {
log.info("name:{},age:{}", name, age);
return "name:" + name + ",age:" + age;
}
@GetMapping("/b")
public String b() {
log.info("name:{}", stuName);
return "name:" + stuName;
}
}
bootstrap.yml
spring:
application:
name: nacos-config
cloud:
nacos:
config:
namespace: 0e2f3694-d3ef-4a39-8aa6-5622872228ce
server-addr: 192.168.65.3:8848
file-extension: yaml
refresh-enabled: true
# group: TEMA01
extension-configs:
- data-id: public-commons01.yaml
# profiles:
# active: dev
测试结果:
2、先创建
controller包
@Slf4j
@RequestMapping("/config")
@RestController
@RefreshScope //只需要在需要动态读取配置的类上添加此注解就可以
public class ConfigController {
@Value("${user.name}")
private String name;
@Value("${user.age}")
private Integer age;
@Value("${student.name}")
private String stuName;
@Value("${person.name}")
private String personName;
@GetMapping("/a")
public String a() {
log.info("name:{},age:{}", name, age);
return "name:" + name + ",age:" + age;
}
@GetMapping("/b")
public String b() {
log.info("name:{},personName:{},allName:{}", stuName, personName);
return "name:" + stuName + ",personName:" + personName;
}
}
bootstrap.yml
spring:
application:
name: nacos-config
cloud:
nacos:
config:
namespace: 0e2f3694-d3ef-4a39-8aa6-5622872228ce
server-addr: 192.168.65.3:8848
file-extension: yaml
refresh-enabled: true
# group: TEMA01
extension-configs:
- data-id: public-commons01.yaml
- data-id: abcde-commons02.yaml
# profiles:
# active: dev
测试结果
3、先创建(动态刷新)
controller包
spring:
application:
name: nacos-config
cloud:
nacos:
config:
namespace: 0e2f3694-d3ef-4a39-8aa6-5622872228ce
server-addr: 192.168.65.3:8848
file-extension: yaml
refresh-enabled: true
# group: TEMA01
extension-configs:
- data-id: public-commons01.yaml
group: DEFAULT_GROUP
refresh: true
- data-id: abcde-commons02.yaml
refresh: false
# profiles:
# active: dev
测试1:支持动态刷新
测试2:不支持动态刷新
如果是 yaml 格式的应写成:
多个 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 ⽂件扩展名没有影响。
共享配置
不同为服务之间实现配置共享的原理类似于⽂件引⼊,就是定义⼀个公共配置,然后在当前配置中引⼊。
先新建
all:
name: 所有⼈
age: 29
controller包
@Slf4j
@RequestMapping("/config")
@RestController
@RefreshScope //只需要在需要动态读取配置的类上添加此注解就可以
public class ConfigController {
@Value("${user.name}")
private String name;
@Value("${user.age}")
private Integer age;
@Value("${student.name}")
private String stuName;
@Value("${person.name}")
private String personName;
@Value("${all.name}")
private String allName;
@GetMapping("/a")
public String a() {
log.info("name:{},age:{}", name, age);
return "name:" + name + ",age:" + age;
}
@GetMapping("/b")
public String b() {
log.info("name:{},personName:{},allName:{}", stuName, personName,allName);
return "name:" + stuName + ",personName:" + personName+",allName:"+allName;
}
}
bootstrap.yml
spring:
application:
name: nacos-config
cloud:
nacos:
config:
namespace: 0e2f3694-d3ef-4a39-8aa6-5622872228ce
server-addr: 192.168.65.3:8848
file-extension: yaml
refresh-enabled: true
# group: TEMA01
extension-configs:
- data-id: public-commons01.yaml
group: DEFAULT_GROUP
refresh: true
- data-id: abcde-commons02.yaml
refresh: false
shared-configs:
- data-id: weilai-all.yaml
# profiles:
# active: dev
测试结果
两种区别
⽇常开发中,多个模块可能会有很多共⽤的配置,⽐如数据库连接信息, Redis 连接信息, RocketMQ 连接信 息,监控配置等等。那么此时,我们就希望可以加载多个配置,多个项⽬共享同⼀个配置之类等功能, Nacos Config 也确实⽀持。
- Nacos 在配置路径 spring.cloud.nacos.config.extension-config 下,允许我们指定⼀个或多个额外配置。
- Nacos 在配置路径 spring.cloud.nacos.config.shared-configs 下,允许我们指定⼀个或多个共享配置。
上述两类配置都⽀持三个属性: data-id 、 group (默认为字符串 DEFAULT_GROUP )、 refresh (默认为 true )。 实际上,Nacos中并未对 extension-configs 和 shared-configs 的差别进⾏详细阐述。我们从他们的结构, 看不出本质差别;除了优先级不同以外,也没有其他差别。⼀般来说,要在各应⽤之间共享⼀个配置,请使⽤上⾯的 shared-configs 。如果要在特定范围内(⽐如某个应⽤上)覆盖某个共享 dataId 上的特定属性,请使⽤ extension-config 。
配置的优先级
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。
完全关闭配置
通过设置 spring.cloud.nacos.config.enabled = false 来完全关闭 Spring Cloud Nacos Config 。
nacos的⼏个概念
命名空间(Namespace)
命名空间可⽤于进⾏不同环境的配置隔离。⼀般⼀个环境划分到⼀个命名空间。
配置分组(Group)
配置分组⽤于将不同的服务可以归类到同⼀分组。⼀般将⼀个项⽬的配置分到⼀组。
配置集(Data ID)
在系统中,⼀个配置⽂件通常就是⼀个配置集。⼀般微服务的配置就是⼀个配置集
说,要在各应⽤之间共享⼀个配置,请使⽤上⾯的 shared-configs 。如果要在特定范围内(⽐如某个应⽤上)覆盖某个共享 dataId 上的特定属性,请使⽤ extension-config 。
配置的优先级
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。
完全关闭配置
通过设置 spring.cloud.nacos.config.enabled = false 来完全关闭 Spring Cloud Nacos Config 。
nacos的⼏个概念
命名空间(Namespace)
命名空间可⽤于进⾏不同环境的配置隔离。⼀般⼀个环境划分到⼀个命名空间。
配置分组(Group)
配置分组⽤于将不同的服务可以归类到同⼀分组。⼀般将⼀个项⽬的配置分到⼀组。
配置集(Data ID)
在系统中,⼀个配置⽂件通常就是⼀个配置集。⼀般微服务的配置就是⼀个配置集