Spring Cloud Config

随着线上项目变的日益庞大,每个项目都散落着各种配置文件,如果采用分布式的开发模式,需要的配置文件随着服务增加而不断增多。某一个基础服务信息变更,都会引起一系列的更新和重启,运维苦不堪言也容易出错。配置中心便是解决此类问题的灵丹妙药。Spring Cloud Config,因为它功能全面强大,可以无缝的和spring体系相结合。

Spring Cloud ConfigSpring Cloud 团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分。其中服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息、加密 / 解密信息等访问接口;而客户端则是微服务架构中的各个微服务应用或基础设施,它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。Spring Cloud Config 实现了对服务端和客户端中环境变量和属性配置的抽象映射,所以它除了适用于 Spring 构建的应用程序之外,也可以在任何其他语言运行的应用程序中使用。由于 Spring Cloud Config 实现的配置中心默认采用 Git 来存储配置信息,所以使用 Spring Cloud Config 构建的配置服务器,天然就支持对微服务应用配置信息的版本管理,并且可以通过 Git 客户端工具来方便的管理和访问配置内容。当然它也提供了对其他存储方式的支持,比如:SVN 仓库、本地化文件系统。

配置中心提供的功能:

  • 提供服务端和客户端支持
  • 集中管理各环境的配置文件
  • 配置文件修改之后,可以快速的生效
  • 可以进行版本管理
  • 支持大的并发查询
  • 支持各种语言

准备工作

Github 上面创建了一个文件夹 config-repo 用来存放配置文件,创建以下三个配置文件:

1
2
3
4
5
6
// 开发环境
test-config-dev.yml
// 测试环境
test-config-test.yml
// 生产环境
test-config-prod.yml

每个文件都写一个test.hello属性,属性值分别是devtestprod,如:

1
2
test:
hello: test

Server端

创建一个基础的 Spring Boot 工程,命名为:config-server-git

POM

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
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</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>

添加spring-cloud-config-server依赖。

配置文件

在配置文件中配置服务的基本信息以及git的地址:

1
2
3
4
5
6
7
8
9
10
11
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/chenanyu/SpringCloudDemos # 配置git仓库的地址
search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。
server:
port: 28088

Spring Cloud Config 也提供本地存储配置的方式。我们只需要设置属性spring.profiles.active=nativeConfig Server 会默认从应用的src/main/resource目录下检索配置文件。也可以通过spring.cloud.config.server.native.searchLocations=file:E:/properties/属性来指定配置文件的位置。虽然 Spring Cloud Config 提供了这样的功能,但是为了支持更好的管理内容和版本控制的功能,还是推荐使用 Git 的方式。

如果我们的 Git 仓库需要权限访问,那么可以通过配置下面的两个属性来实现;
spring.cloud.config.server.git.username:访问 Git 仓库的用户名
spring.cloud.config.server.git.password:访问 Git 仓库的用户密码

启动类

启动类添加@EnableConfigServer,激活对配置中心的支持:

1
2
3
4
5
6
7
8
9
@EnableConfigServer
@SpringBootApplication
public class ConfigServerGitApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigServerGitApplication.class, args);
}

}

Server端的配置到此为止

测试

测试 Server 端是否可以读取到 github 上面的配置信息,通过postman请求http://localhost:28088/test-config/test,返回如下信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"name": "test-config",
"profiles": [
"test"
],
"label": null,
"version": "cd3f7acc23d556b64499d08ea9a14d2ee23c4534",
"state": null,
"propertySources": [
{
"name": "https://github.com/chenanyu/\
SpringCloudDemos/config-repo/test-config-test.yml",
"source": {
"test.hello": "test"
}
}
]
}

注:路径中的test-config是指config-repo中的文件名前缀。

修改test-config-test.yml中的内容,再次在postman请求,发现返回的内容是最新的了,说明Server端会自动读取最新提交的内容。

仓库中的配置文件会被转换成 Web 接口,访问可以参照以下的规则:

  • /{application}/{profile}[/{label}]
  • /{application}-{profile}.yml
  • /{label}/{application}-{profile}.yml
  • /{application}-{profile}.properties
  • /{label}/{application}-{profile}.properties

上面的 URL 会映射{application}-{profile}.yml对应的配置文件,其中{label}对应 Git 上不同的分支,默认为 master。以 config-client-dev.yml 为例子,它的 applicationconfig-clientprofiledev

Client端

在微服务应用中获取上述的配置信息。再创建一个基础的 Spring Boot 应用,命名为 config-client

POM

修改pom文件,引入如下配置:

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
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</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>

spring-boot-starter-webflux 是为了方便 Web 测试。Spring WebFlux 是随 Spring 5 推出的响应式 Web 框架。

配置文件

需要两个配置文件,application.ymlbootstrap.yml

application.yml

1
2
3
4
5
spring:
application:
name: config-client-git
server:
port: 28089

bootstrap.yml

1
2
3
4
5
6
7
spring:
cloud:
config:
uri: http://localhost:28088 # 配置中心的具体地址,即 config-server
name: test-config # 对应 {application} 部分
profile: test # 对应 {profile} 部分
label: master # 对应 {label} 部分,即 Git 的分支。如果配置中心使用的是本地存储,则该参数无用
  • spring.application.name:对应{application}部分
  • spring.cloud.config.profile:对应{profile}部分
  • spring.cloud.config.label:对应git的分支。如果配置中心使用的是本地存储,则该参数无用
  • spring.cloud.config.uri:配置中心的具体地址
  • spring.cloud.config.discovery.service-id:指定配置中心的service-id,便于扩展为高可用配置集群。

上面这些与 Spring Cloud Config 相关的属性必须配置在 bootstrap.yml 中,config 部分内容才能被正确加载。因为 config 的相关配置会先于 application.yml,而 bootstrap.yml 的加载也是先于 application.yml

启动类

不需要修改:

1
2
3
4
5
6
7
8
@SpringBootApplication
public class ConfitClientApplication {

public static void main(String[] args) {
SpringApplication.run(ConfitClientApplication.class, args);
}

}

新建HelloController,使用@value注解来获取Server端参数的值

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class HelloController {

@Value("${test.hello:error}")
private String profile;

@GetMapping("/info")
public Mono<String> hello() {
return Mono.justOrEmpty(profile);
}

}

测试

使用postman请求http://localhost:28089/info,返回如下结果,则说明Client端成功获取到了Server端的配置值。

postman
postman

至此,SpringCloudGit版配置中心就完成了。

Refresh

Spring Cloud Config分服务端和客户端,服务端负责将git(svn)中存储的配置文件发布成REST接口,客户端可以从服务端REST接口获取配置。但客户端并不能主动感知到配置的变化,从而主动去获取新的配置。客户端如何去主动获取新的配置信息呢,springcloud已经给我们提供了解决方案,每个客户端通过POST方法触发各自的/refresh

仅修改客户端项目,就可以实现 refresh 的功能。

添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

增加了spring-boot-starter-actuator包,spring-boot-starter-actuator是一套监控的功能,可以监控程序在运行时状态,其中就包括/actuator/refresh的功能。

开启更新机制

需要给加载变量的类上面加载@RefreshScope,在客户端执行/actuator/refresh的时候就会更新此类下面的变量值。

1
2
3
4
5
6
7
8
9
10
11
12
13
@RefreshScope
@RestController
public class HelloController {

@Value("${test.hello:error}")
private String profile;

@GetMapping("/info")
public Mono<String> hello() {
return Mono.justOrEmpty(profile);
}

}

配置文件

SpringBoot1.5以后需要添加以下配置以将/actuator/refresh这个 Endpoint 暴露出来:

1
2
3
4
5
management:
endpoints:
web:
exposure:
include: refresh

测试

重启 config-client,我们以 POST 请求的方式来访问http://localhost:28089/actuator/refresh就会更新配置文件至最新版本。

  1. 修改test-config-test.yml文件的内容
  2. 使用postman请求http://localhost:28089/actuator/refresh接口
  3. 再请求http://localhost:28089/info接口
  4. 发现配置内容已经更新到最新了
postman
postman

至此,配置中心的配置刷新就算完成了,但是这样做有个弊端,就是每次更新了配置之后都要请求refresh接口,这就很麻烦。。

Webhook

Webhook 是当某个事件发生时,通过发送 HTTP POST 请求的方式来通知信息接收方。Webhook 来监测你在 Github.com 上的各种事件,最常见的莫过于 push 事件。如果你设置了一个监测 push 事件的 Webhook,那么每当你的这个项目有了任何提交,这个 Webhook 都会被触发,这时 Github 就会发送一个 HTTP POST 请求到你配置好的地址。

如此一来,你就可以通过这种方式去自动完成一些重复性工作,比如,你可以用 Webhook 来自动触发一些持续集成(CI)工具的运作,比如 Travis CI;又或者是通过 Webhook 去部署你的线上服务器。下图就是 Github 上面的 Webhook 配置。

Webhook
Webhook
  • Payload URL :触发后回调的 URL
  • Content type :数据格式,两种一般使用 json
  • Secret :用作给 POST 的 body 加密的字符串。采用 HMAC 算法
  • events :触发的事件列表。

-

events 事件类型 描述
push 仓库有 push 时触发。默认事件
create 当有分支或标签被创建时触发
delete 当有分支或标签被删除时触发

-

这样我们就可以利用 hook 的机制去触发客户端的更新,但是当客户端越来越多的时候,hook 机制也不够优雅了,另外每次增加客户端都需要改动 hook 也是不现实的。其实,Spring Cloud 给了我们更好解决方案——Spring Cloud Bus

参考


隐藏关卡