SpringCloud 详解(十二)

微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。

Config

SpringCloud Config 为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。分为服务端客户端两部分。

  • 服务端:也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息、加密/解密信息等访问接口。
  • 客户端:通过指定的配置中心来管理应用资源以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。配置服务器默认采用 git 来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过 git 客户端工具来方便地管理和获取配置内容。

服务端搭建

准备工作

在 Github 或 Gitee 上新建个仓库,并将三个配置文件上传上去。(推荐使用国内的 Gitee)

1
2
3
#config-dev.yaml
config:
info: "master branch,springcloud-config/config-dev.yaml version=1.0"
1
2
3
#config-prod.yaml
config:
info: "master branch,springcloud-config/config-prod.yaml version=1.0"
1
2
3
#config-test.yaml
config:
info: "master branch,springcloud-config/config-test.yaml version=1.0"

创建一个 Module,一个空的 Maven 项目。并导入下面的依赖。

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
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

编写配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server:
port: 3344

spring:
application:
name: configServer
cloud:
config:
server:
git:
uri: https://gitee.com/Guest997/springcloud-config.git #仓库地址
username: #使用的是邮箱
password:
label: master

eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka

主启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
package ml.guest997;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer //启动 ConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class, args);
}
}

测试

分别启动 Eureka-Server 和 Config-Server 模块后,浏览器访问:127.0.0.1:3344/master/config-test.yaml,发现返回了相应的配置。

配置读取规则:

  • /{label}/{application}-{profile}.yaml(推荐)
  • /{application}-{profile}.yaml
  • /{application}/{profile}/{label}

客户端搭建

准备工作

创建一个 Module,一个空的 Maven 项目。并导入下面的依赖。

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
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

创建主启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
package ml.guest997;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class ConfigClient {
public static void main(String[] args) {
SpringApplication.run(ConfigClient.class, args);
}
}

编写配置文件(bootstrap.yaml)

applicaiton.yaml 是用户级的资源配置项,bootstrap.yaml 是系统级的,优先级更加高。

Bootstrap 属性有高优先级,默认情况下,它们不会被本地配置覆盖。Bootstrap context 和 Application Context 有着不同的约定,所以新增了一个 bootstrap.yaml 文件,保证 Bootstrap Context 和 Application Context 配置的分离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 3355

spring:
application:
name: configClient
cloud:
config:
#下面的配置合起来就是读取:http://localhost:3344/master/config-dev.yaml
label: master
name: config
profile: dev
uri: http://localhost:3344

eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka

Controller 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package ml.guest997.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigController {
@Value("${config.info}")
private String configInfo;

@GetMapping("/configInfo")
public String getConfigInfo() {
return configInfo;
}
}

测试

分别启动 Eureka-Server、Config-Server 和 Config-Client 模块后,浏览器访问:127.0.0.1:3344/master/config-dev.yaml,能够正常返回配置信息。再次访问:127.0.0.1:3355/configInfo,也能够正常返回配置信息。

动态刷新配置

如果在 git 上修改了配置文件,会发现服务端能够及时刷新配置,但是客户端却不会,只有重启或重新加载客户端才能够刷新配置。如果有很多个客户端,岂不是每个都要重启或重新加载,这显然是不合适的。

添加配置

1
2
3
4
5
6
#暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"

添加 Controller 注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package ml.guest997.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;

@RestController
@RefreshScope //实现配置自动更新
public class ConfigController {
@Value("${config.info}")
private String configInfo;

@GetMapping("/configInfo")
public String getConfigInfo() {
return configInfo;
}
}

测试

分别启动 Eureka-Server、Config-Server 和 Config-Client 模块后,git 上修改 config-dev.yaml 文件后,浏览器访问:127.0.0.1:3344/master/config-dev.yaml,能够及时刷新配置。再次访问:127.0.0.1:3355/configInfo,还是没能够及时刷新配置。需要发送一次 post 请求进行刷新。

1
curl -X POST "http://localhost:3355/actuator/refresh"

再次访问:127.0.0.1:3355/configInfo,发现已经刷新配置了。

备注:

存在的问题:

  • 有多个客户端,每个都要发送—次 post 请求进行刷新。(这个可以通过自动化脚本实现)
  • 能否通过广播,一次通知,全部生效。
  • 只想要通知部分客户端,并不想全部都刷新。

想要解决上面的问题,可以通过消息总线实现。