在分布式微服务系统中,几乎所有服务的运行都离不开配置文件的支持,这些配置文件通常由各个服务自行管理,以 properties 或 yml 格式保存在各个微服务的类路径下,例如 application.properties 或 application.yml 等。
这种将配置文件散落在各个服务中的管理方式,存在以下问题:
(1) 管理难度大:配置文件散落在各个微服务中,难以管理。
(2) 安全性低:配置跟随源代码保存在代码库中,容易造成配置泄漏。
(3) 时效性差:微服务中的配置修改后,必须重启服务,否则无法生效。
(4) 局限性明显:无法支持动态调整,例如日志开关、功能开关。
为了解决这些问题,通常我们都会使用配置中心对配置进行统一管理。市面上开源的配置中心有很多,例如百度的 Disconf、淘宝的 diamond、360 的 QConf、携程的 Apollo 等都是解决这类问题的。Spring Cloud 也有自己的分布式配置中心,那就是 Spring Cloud Config。
1. Spring Cloud Config
Spring Cloud Config 是由 Spring Cloud 团队开发的项目,它可以为微服务架构中各个微服务提供集中化的外部配置支持。
简单点说就是,Spring Cloud Config 可以将各个微服务的配置文件集中存储在一个外部的存储仓库或系统(例如 Git 、SVN 等)中,对配置的统一管理,以支持各个微服务的运行。
Spring Cloud Config 包含以下两个部分:
(1) Config Server:也被称为分布式配置中心,它是一个独立运行的微服务应用,用来连接配置仓库并为客户端提供获取配置信息、加密信息和解密信息的访问接口。
(2) Config Client:指的是微服务架构中的各个微服务,它们通过 Config Server 对配置进行管理,并从 Config Sever 中获取和加载配置信息。
Spring Cloud Config 默认使用 Git 存储配置信息,因此使用 Spirng Cloud Config 构建的配置服务器天然就支持对微服务配置的版本管理。我们可以使用 Git 客户端工具方便地对配置内容进行管理和访问。除了 Git 外,Spring Cloud Config 还提供了对其他存储方式的支持,例如 SVN、本地化文件系统等。
Spring Cloud Config 工作流程如下:
(1) 开发或运维人员提交配置文件到远程的 Git 仓库。
(2) Config 服务端(分布式配置中心)负责连接配置仓库 Git,并对 Config 客户端暴露获取配置的接口。
(3) Config 客户端通过 Config 服务端暴露出来的接口,拉取配置仓库中的配置。
(4) Config 客户端获取到配置信息,以支持服务的运行。
Spring Cloud Config 具有以下特点:
(1) Spring Cloud Config 由 Spring Cloud 团队开发,可以说是 Spring 的亲儿子,能够与 Spring 的生态体系无缝集成。
(2) Spring Cloud Config 将所有微服务的配置文件集中存储在一个外部的存储仓库或系统(例如 Git)中,统一管理。
(3) Spring Cloud Config 配置中心将配置以 REST 接口的形式暴露给各个微服务,以方便各个微服务获取。
(4) 微服务可以通过 Spring Cloud Config 向配置中心统一拉取属于它们自己的配置信息。
(5) 当配置发生变化时,微服务不需要重启即可感知到配置的变化,并自动获取和应用最新配置。
(6) 一个应用可能有多个环境,例如开发(dev)环境、测试(test)环境、生产(prod)环境等等,开发人员(7) 可以通过 Spring Cloud Config 对不同环境的各配置进行管理,且能够确保应用在环境迁移后仍然有完整的配置支持其正常运行。
2. 搭建 Config 服务端
在 GitHub 或 Gitee 上创建一个名为 springcloud-demos-config 的仓库(Repository)并获取该仓库的地址。
GitHub:https://github.com/
Gitee:https://gitee.com/
本文将在 “ Springcloud基础知识(5)- Spring Cloud OpenFeign | 声明式服务调用 ” 里SpringcloudDemo03 项目基础上,添加一个 ConfigServer 子模块,来演示 Config 服务端的搭建和测试。
SpringcloudDemo03 的 Spring Boot 版本是 2.3.12.RELEASE。
1) 创建 ConfigServer 模块
选择左上的项目列表中的 SpringcloudDemo03,点击鼠标右键,选择 New -> Module 进入 New Module 页面:
Maven -> Project SDK: 1.8 -> Check "Create from archtype" -> select "org.apache.maven.archtypes:maven-archtype-quickstart" -> Next
Name: ConfigServer
GroupId: com.example
ArtifactId: ConfigServer
-> Finish
注:模块 ConfigServer 创建后,Maven 命令会自动修改主项目 SpringcloudDemo03 的 pom.xml,添加如下内容:
<modules>
...
<module>ConfigServer</module>
</modules>
2) 修改 ConfigServer 的 pom.xml 内容如下
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
5 http://maven.apache.org/xsd/maven-4.0.0.xsd">
6 <parent>
7 <artifactId>SpringcloudDemo03</artifactId>
8 <groupId>com.example</groupId>
9 <version>1.0-SNAPSHOT</version>
10 </parent>
11 <modelVersion>4.0.0</modelVersion>
12
13 <artifactId>ConfigServer</artifactId>
14
15 <name>ConfigServer</name>
16 <!-- FIXME change it to the project's website -->
17 <url>http://www.example.com</url>
18
19 <properties>
20 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
21 <maven.compiler.source>1.8</maven.compiler.source>
22 <maven.compiler.target>1.8</maven.compiler.target>
23 </properties>
24
25 <dependencies>
26 <dependency>
27 <groupId>junit</groupId>
28 <artifactId>junit</artifactId>
29 <version>4.11</version>
30 <scope>test</scope>
31 </dependency>
32 <dependency>
33 <groupId>org.springframework.boot</groupId>
34 <artifactId>spring-boot-starter-web</artifactId>
35 </dependency>
36 <dependency>
37 <groupId>org.springframework.boot</groupId>
38 <artifactId>spring-boot-starter-test</artifactId>
39 <scope>test</scope>
40 </dependency>
41 <dependency>
42 <groupId>org.springframework.cloud</groupId>
43 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
44 </dependency>
45 <dependency>
46 <groupId>org.springframework.cloud</groupId>
47 <artifactId>spring-cloud-config-server</artifactId>
48 </dependency>
49 </dependencies>
50
51 <build>
52 <plugins>
53 <plugin>
54 <groupId>org.springframework.boot</groupId>
55 <artifactId>spring-boot-maven-plugin</artifactId>
56 <configuration>
57 <mainClass>com.example.App</mainClass>
58 <layout>JAR</layout>
59 </configuration>
60 <executions>
61 <execution>
62 <goals>
63 <goal>repackage</goal>
64 </goals>
65 </execution>
66 </executions>
67 </plugin>
68 </plugins>
69 </build>
70
71 </project>
3) 创建 src/main/resources/application.yml 文件
1 server:
2 port: 3001 # 端口号
3 spring:
4 application:
5 name: spring-cloud-config-server # 服务名
6 cloud:
7 config:
8 server:
9 git:
10 uri: https://gitee.com/slksm/springcloud-demos-config.git # 仓库地址
11 search-paths:
12 - springcloud-demos-config # 仓库名
13 force-pull: true
14 username:
15 password:
16 label: master
17
18 eureka:
19 client: # 将客户端注册到 eureka 服务列表内
20 service-url:
21 # 将服务注册到 Eureka 集群
22 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
4) 修改 src/main/java/com/example/App.java 文件
1 package com.example;
2
3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.SpringBootApplication;
5 import org.springframework.cloud.config.server.EnableConfigServer;
6 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
7
8 @SpringBootApplication
9 @EnableEurekaClient
10 @EnableConfigServer
11 public class App {
12 public static void main(String[] args) {
13 SpringApplication.run(App.class, args);
14 }
15 }
5)创建 config-dev.yml 文件
内容如下:
config:
company: com.example
version: 1.0
将其上传到 springcloud-demos-config 仓库 master 分支下。
6) 运行
依次启动 server-7001、server-7002、server-7003,启动的间隔 5 ~ 10 秒,都启动后等待 10 秒左右。
启动 ConfigServer 模块,浏览器访问 http://localhost:3001/master/config-dev.yml,显示结果如下:
config:
company: com.example
version: 1.0
Spring Cloud Config 规定了一套配置文件访问规则,如下表。
访问规则 示例
/{application}/{profile}[/{label}] /config/dev/master
/{application}-{profile}.{suffix} /config-dev.yml
/{label}/{application}-{profile}.{suffix} /master/config-dev.yml
访问规则内各参数说明如下。
(1) {application}:应用名称,即配置文件的名称,例如 config-dev。
(2) {profile}:环境名,一个项目通常都有开发(dev)版本、测试(test)环境版本、生产(prod)环境版本,配置文件则以 application-{profile}.yml 的形式进行区分,例如 application-dev.yml、application-test.yml、application-prod.yml 等。
(3) {label}:Git 分支名,默认是 master 分支,当访问默认分支下的配置文件时,该参数可以省略,即第二种访问方式。
(4) {suffix}:配置文件的后缀,例如 config-dev.yml 的后缀为 yml。
通过这套规则,我们在浏览器上就直接对配置文件进行访问,http://localhost:3001/config-dev.yml
3. 搭建 Config 客户端
本文将在 SpringcloudDemo03 项目基础上,添加一个 ConfigClient 子模块,来演示 Config 客户端的搭建和测试。
SpringcloudDemo03 的 Spring Boot 版本是 2.3.12.RELEASE。
1) 创建 ConfigClient 模块
选择左上的项目列表中的 SpringcloudDemo03,点击鼠标右键,选择 New -> Module 进入 New Module 页面:
Maven -> Project SDK: 1.8 -> Check "Create from archtype" -> select "org.apache.maven.archtypes:maven-archtype-quickstart" -> Next
Name: ConfigClient
GroupId: com.example
ArtifactId: ConfigClient
-> Finish
注:模块 ConfigClient 创建后,Maven 命令会自动修改主项目 SpringcloudDemo03 的 pom.xml,添加如下内容:
<modules>
...
<module>ConfigClient</module>
</modules>
2) 修改 ConfigClient 的 pom.xml 内容如下
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
5 http://maven.apache.org/xsd/maven-4.0.0.xsd">
6 <parent>
7 <artifactId>SpringcloudDemo03</artifactId>
8 <groupId>com.example</groupId>
9 <version>1.0-SNAPSHOT</version>
10 </parent>
11 <modelVersion>4.0.0</modelVersion>
12
13 <artifactId>ConfigClient</artifactId>
14
15 <name>ConfigClient</name>
16 <!-- FIXME change it to the project's website -->
17 <url>http://www.example.com</url>
18
19 <properties>
20 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
21 <maven.compiler.source>1.8</maven.compiler.source>
22 <maven.compiler.target>1.8</maven.compiler.target>
23 </properties>
24
25 <dependencies>
26 <dependency>
27 <groupId>junit</groupId>
28 <artifactId>junit</artifactId>
29 <version>4.11</version>
30 <scope>test</scope>
31 </dependency>
32 <dependency>
33 <groupId>org.springframework.boot</groupId>
34 <artifactId>spring-boot-starter-web</artifactId>
35 </dependency>
36 <dependency>
37 <groupId>org.springframework.boot</groupId>
38 <artifactId>spring-boot-starter-test</artifactId>
39 <scope>test</scope>
40 </dependency>
41 <dependency>
42 <groupId>org.springframework.cloud</groupId>
43 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
44 </dependency>
45 <dependency>
46 <groupId>org.springframework.cloud</groupId>
47 <artifactId>spring-cloud-starter-config</artifactId>
48 </dependency>
49 </dependencies>
50
51 <build>
52 <plugins>
53 <plugin>
54 <groupId>org.springframework.boot</groupId>
55 <artifactId>spring-boot-maven-plugin</artifactId>
56 <configuration>
57 <mainClass>com.example.App</mainClass>
58 <layout>JAR</layout>
59 </configuration>
60 <executions>
61 <execution>
62 <goals>
63 <goal>repackage</goal>
64 </goals>
65 </execution>
66 </executions>
67 </plugin>
68 </plugins>
69 </build>
70
71 </project>
3) 创建 src/main/resources/bootstrap.yml 文件
1 # bootstrap.yml 是系统级别的,加载优先级高于 application.yml ,负责从外部加载配置并解析
2 server:
3 port: 4001 # 端口号
4 spring:
5 application:
6 name: spring-cloud-config-client # 服务名
7 cloud:
8 config:
9 label: master # 分支名称
10 name: config # 属性名称,config-dev.yml 中的 config
11 profile: dev # 环境名 config-dev.yml 中的 dev
12 uri: http://localhost:3001 # Spring Cloud Config 服务端(配置中心)地址
13
14 eureka:
15 client: # 将客户端注册到 eureka 服务列表内
16 service-url:
17 # 将服务注册到 Eureka 集群
18 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
4) 创建 src/main/java/com/example/controller/ConfigController.java 文件
1 package com.example.controller;
2
3 import org.springframework.beans.factory.annotation.Value;
4 import org.springframework.web.bind.annotation.GetMapping;
5 import org.springframework.web.bind.annotation.RestController;
6
7 @RestController
8 public class ConfigController {
9 @Value("${server.port}")
10 private String port;
11 @Value("${config.company}")
12 private String company;
13 @Value("${config.version}")
14 private String version;
15
16 @GetMapping(value = "/get/config")
17 public String getConfig() {
18 return "Company: " + company + "<br/>Version:" + version + "<br/>Port:" + port;
19 }
20 }
5) 修改 src/main/java/com/example/App.java 文件
1 package com.example;
2
3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.SpringBootApplication;
5 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
6
7 @SpringBootApplication
8 @EnableEurekaClient
9 public class App {
10 public static void main(String[] args) {
11 SpringApplication.run(App.class, args);
12 }
13 }
6) 运行
依次启动 server-7001、server-7002、server-7003,启动的间隔 5 ~ 10 秒,都启动后等待 10 秒左右。
启动 ConfigServer 模块,浏览器访问 http://localhost:3001/master/config-dev.yml,显示结果如下:
config:
company: com.example
version: 1.0
启动 ConfigClient 模块,浏览器访问 http://localhost:4001/get/config,显示结果如下:
Company: com.example
Version:1.0
Port:4001
修改 config-dev.yml 的 version 为 2.0,并提交到 Gitee 仓库,浏览器访问 http://localhost:3001/master/config-dev.yml,显示结果如下:
config:
company: com.example
version: 2.0
浏览器访问 http://localhost:4001/get/config,显示结果如下:
Company: com.example
Version:1.0
Port:4001
启动 ConfigClient 模块,浏览器访问 http://localhost:4001/get/config,显示结果如下:
Company: com.example
Version:2.0
Port:4001
结论:
(1) 配置更新后,ConfigServer 不需要重启,就从 Git 仓库中获取了最新的配置;
(2) ConfigClient 需要重启,才能获得最新的配置信息;