Spring Cloud(十):分布式配置管理 Config 之 Git 实现
在微服务架构集群部署的环境中,可能会有十几甚至几十个服务,若要修改项目属性参数,手动的方式是很糟糕的,所以就有了分布式配置管理这个概念。
将服务器的配置外部化,可以存在文件或数据库中,一个专门用于管理应用服务配置的应用,可以随时修改和自动更新到目标服务器上。目前已有开源项目,如,smconf、disconf、QConf。
Spring Cloud Config 为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Config Server,可以在所有环境中管理应用程序的外部属性。Spring Cloud Config 官方文档,spring-cloud-config Github。
概述
Spring Cloud Config Server 默认使用 Git 来存储配置,使用 Git 管理配置文件自动支持文件的版本管理。
Config Server 特性
- 提供基于 HTTP API 的外部配置(key-value 或 yaml 内容)。
- 加密和解密属性值(对称或非对称)。
- 使用 @EnableConfigServer 轻松地将 Config Server 嵌入到 Spring Boot 应用中。
Spring Cloud Config Server 默认的Git存储库方式,是将 spring.cloud.config.server.git.uri 指定的Git配置仓库克隆到本地,客户端的配置文件存储在此Git存储库中或子目里,并将之用于客户端初始化。
1 | spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/config-repo |
Config Server 获取以下形式的配置文件供客户端应用使用:
1 | /{application}/{profile}[/{label}] |
- application:指 spring.config.name 属性设置的 Config Client 名称,不存在则使用 spring.application.name 的值。
- profile:应用需要使用的 profile,若没有指定,则使用默认 default。
- label:Git 分支、标签,若没有指定,则使用默认 master。
注意:spring.application.name 的值不要使用 application- 作为前缀,会导致无法正确地解析配置文件。
Config Client 特性
- 绑定到配置服务器,并使用远程属性源初始化 Spring 环境。
- 加密和解密属性值(对称或非对称)。
- Spring Bean 上添加 @RefreshScope 注解即可实现在配置更改时重新初始化。
- 使用和管理端点:
- /env:用于更新 Environment 和重新绑定 @ConfigurationProperties 及日志级别。
- /refresh:用于刷新 @RefreshScope beans。
- /restart:重启 Spring Context(默认情况下禁用)。
- /pause 和 /resume:用于调用 Lifecycle 方法(在 ApplicationContext 上的 stop() 和 start() )。
Config Git 存储库
Config Server
创建 Config Server 项目并引入 spring-cloud-config-server 依赖。
pom.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springcloud.configserver</groupId>
<artifactId>config-server-jdbc</artifactId>
<version>v1.0.0</version>
<name>config-server-git</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>在启动类上添加 @EnableConfigServer 注解来启用Config Server。
1
2
3
4
5
6
7
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}Spring Cloud Config Server 为外部配置(key-val 或等效 yaml 内容)提供基于 HTTP 资源的 API。 通过使用 @EnableConfigServer 注解,服务器可嵌入 Spring Boot 应用程序中。
创建远程 Git 仓库,用于存储客户端应用的配置文件。
这里在 GitHub 创建配置存储库用于,库名最好与服务应用名相同。实际生产会创建私有仓库,需要授权认证才可访问。
Config Server Git 配置存储库:https://github.com/gxing19/config-repoConfig Server 配置文件
application.properties1
2
3
4
5
6
7
8
9
10
11# 端口
9010 =
# 应用名
config-repo =
# Git URI 使用占位符
https://github.com/gxing19/config-repo =
#spring.cloud.config.server.git.uri=https://github.com/gxing19/${spring.application.name}
# 设置 HTTP 连接超时时长, 单位:秒
10 =
# 删除本地未跟踪的库
true =
Config Client
创建 Spring Boot 应用,引入 spring-cloud-starter-config 依赖和 spring-boot-starter-actuator 依赖。
这里创建一个订单服务的应用(orderService)来演示
pom.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springcloud</groupId>
<artifactId>service-order</artifactId>
<version>v1.0.0</version>
<name>service-order</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>作为 Config Client ,必须使用 bootstrap.properties 或 bootstrap.yml 作为引导配置,在应用上下文的引导阶段起作用。
bootstrap.properties1
2
3
4
5
6
7
8# Config Server 地址
http://localhost:9010 =
# 应用名
orderService =
# 使用的 profile
dev =
# Actuator 演示开放所有端点(生产只开放少数必要的端点)
* =在远程 Git 仓库创建客户端应用的配置文件
在 Git 配置存储库:https://github.com/gxing19/config-repo 创建配置文件 orderService-dev.properties
orderService-dev.properties1
2
38010 =
QWERTYUIO =
#logging.level.root=debug启动客户端应用,查看配置客户端和服务端的日志
客户端应用(Config Client)在启动时需要向配置服务器(Config Server) 发送请求获取配置属性来完成应用初始化。看日志是在打印出 Banner 后就向配置服务器发送请求。Config Client 启动日志:
1
2
3
4
5
6
7
8
9
10
1118:09:17.622 INFO 8944 --- [ restartedMain] c.c.c.ConfigServicePropertySourceLocator :
Fetching config from server at : http://localhost:9010
18:09:20.669 INFO 8944 --- [ restartedMain] c.c.c.ConfigServicePropertySourceLocator :
Located environment: name=orderService, profiles=[dev], label=null, version=5780a5068b7241badfebf68fffdc006c4a185545, state=null
18:09:20.669 INFO 8944 --- [ restartedMain] b.c.PropertySourceBootstrapConfiguration :
Located property source: CompositePropertySource {name='configService', propertySources=[MapPropertySource {name='configClient'},
MapPropertySource {name='https://github.com/gxing19/config-repo/orderService-dev.properties'}]}
18:09:20.673 INFO 8944 --- [ restartedMain] c.s.s.order.OrderServiceApplication : The following profiles are active: dev客户端应用启动成功,使用了远程配置文件中的 8010 端口。
Config Server 接收到请求日志:
克隆远程配置仓库到本地,在 Windows 系统默认是克隆到系统临时文件目录。1
2
3
4
509:17:52.292 INFO 10176 --- [nio-9010-exec-5] .c.s.e.MultipleJGitEnvironmentRepository :
Fetched for remote master and found 1 updates
09:17:52.517 INFO 10176 --- [nio-9010-exec-5] o.s.c.c.s.e.NativeEnvironmentRepository :
Adding property source: file:/C:/Users/gxing/AppData/Local/Temp/config-repo-4123568461296240052/orderService-dev.properties
Client 运行环境
客户端添加 spring-boot-starter-actuator 依赖,才可以通过调用端点来更新 Environment 。
根端点:http://localhost:8010/actuator
- 环境变量端点:http://localhost:8010/actuator/env
从该端点可以看到客户端应用完整的运行环境,包括环境变量。bootstrap.properties 在 /env 端点显示为高优先级属性源,端点数据如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62{
"activeProfiles": [
"dev"
],
"propertySources": [
{
"name": "server.ports",
"properties": {
"local.server.port": {
"value": 8010
}
}
},
{
"name": "configService:configClient",
"properties": {
"config.client.version": {
"value": "5780a5068b7241badfebf68fffdc006c4a185545"
}
}
},
{
"name": "configService:https://github.com/gxing19/config-repo/orderService-dev.properties",
"properties": {
"server.port": {
"value": "8010"
},
"common.properties.app-id": {
"value": "QWERTYUIO"
}
}
},
{...省略...},
{
"name": "applicationConfig: [classpath:/bootstrap.properties]",
"properties": {
"spring.cloud.config.uri": {
"value": "http://localhost:9010",
"origin": "class path resource [bootstrap.properties]:1:25"
},
"spring.application.name": {
"value": "orderService",
"origin": "class path resource [bootstrap.properties]:2:25"
},
"spring.profiles.active": {
"value": "dev",
"origin": "class path resource [bootstrap.properties]:3:24"
},
"management.endpoints.web.exposure.include": {
"value": "*",
"origin": "class path resource [bootstrap.properties]:4:43"
}
}
},
{
"name": "defaultProperties",
"properties": {
}
}
]
}
动态更新属性
客户端应用的远程配置文件 orderService-dev.properties 有个自定义属性,写个 Controller 从环境中获取该值,然后改变自定义属性的值,调用 /refresh 刷新环境和变量,重新请求该属性。
创建获取自定义属性的 Controller 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConfigController {
private Environment environment;
public String refreshProperties(){
String appId = environment.getProperty("common.properties.app-id");
return appId;
}
}返回结果:QWERTYUIO
修改远程配置文件该属性值
orderService-dev.properties1
abcdefghijk112233 =
调用刷新环境的端点 /refresh,POST 请求
1
2# post 请求
http://localhost:8010/actuator/refresh返回结果:
1
2
3
4[
"config.client.version",
"common.properties.app-id"
]调用执行步骤 1 创建的方法,会重新拉取远程的配置文件。
返回结果:abcdefghijk112233
不用重启应用,调用端点刷新环境和变量,即完成了配置文件属性重载。
与 Eureka 集成
在上面示例的基础上整合 Eureka ,通过服务发现来与 Config Server 通信获取远程配置源。
注册 Config Server
Config Server 作为 Eureka Server 的客户端,需要引入 eureka-client 依赖
pom.xml1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>Config Server 的配置文件添加注册到 Eureka Server 的属性
application.properties1
2# 添加注册Eureka Server
http://admin:123456@eureka.master.com:8761/eureka,http://admin:123456@eureka.slave.com:8762/eureka =
注册 Config Client
Config Client 作为 Eureka Server 的客户端,需要引入 eureka-client 依赖
pom.xml
1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>Config Client 的引导启动配置文件 bootstrap.properties 添加开启服务发现和设置 Eureka 服务地址:
bootstrap.properties
1
2
3
4
5
6
7
8# 添加开启服务发现 discovery
true =
# 添加注册Eureka Server
http://admin:123456@eureka.master.com:8761/eureka,http://admin:123456@eureka.slave.com:8762/eureka =
# 添加设置 Config Server 在 Eureka Server 中的 service ID
config-repo =
# 注释掉显式指定的 Config Server uri
#spring.cloud.config.uri=http://localhost:9010重启 Config Server 和 Config Client,查看日志
在 Eureka Server 的 Web 控制台可以看到 Config Server 和 Config Client 注册实例。
Config Client 在引导启动阶段,通过服务发现获取远程配置源。
调用 Config Client 获取环境变量的 Controller 方法成功。
Config Server 高可用
Spring Cloud Config Server 实现高可用主要有两种方式:
- 部署多个 Config Server 实例,指向同一个存储库(Git 或 JDBC),在上层通过负载均衡器来反向代理到 Config Server。
- 部署多个 Config Server,集成 Eureka Client,注册到 Eureka Server,通过服务治理来实现,即可高可用,也实现了自维护。
Config 详细配置
请参阅 Spring Cloud系列(十二):分布式配置管理 Config 之 配置详解
其它参考
Spring Coud Config Server 也支持使用 Vault 、CredHub作为存储后端,还支持复合环境存储(同时使用不同的存储库),支持通过代理访问存储后端。详细使用参考官网。
Spring Cloud(十):分布式配置管理 Config 之 Git 实现
http://blog.gxitsky.com/2019/04/06/SpringCloud-10-config-git-impl/