1、简介
传统配置的痛点:
在以前的项目中,我们通过配置文件、操作系统变量、Java 系统属性等方式配置 Java 项目;在 spring boot 爆火之后我们的配置信息都写在 application.yml 或 application.properties 文件中,这些配置文件随着项目的打包与应用一起发布;但是当我们需要修改配置文件中的配置信息的时候,需要更新配置文件重新构建、重新发布;如果配置信息配置在操作系统环境变量或者 Java 系统属性中则需要重启应用。
配置文件中往往有一些敏感信息,比如数据库密码、Redis 密码、加密秘钥等信息。这些信息如果直接配置在配置文件中,容易泄露。
针对这些问题,Spring Cloud 早期发布了 Spring Cloud Config 进行集中式配置管理,成功解决了这些问题。Spring Cloud Config 分为 Server 端和 Client 端。其中 Spring Cloud Config Server 是 Spring Cloud 为指定应用中所有服务提供集中式配置的一个服务,借助 Spring Cloud Config Server 可以实现集中管理所有应用的配置,避免重复配置。Spring Cloud Config 带来了诸多好处:
Spring Cloud Config Server 通过 Git 仓库给微服务提供配置属性架构图:
2、正文
正文通过 Spring Boot 项目展开对 Spring Cloud Config 的探讨。分别会有以下几个方面来展开:
Spring Cloud Config + Git 手动刷新
Spring Cloud Config + Git + WebHook 实现自动刷新
Spring Cloud Config + Eureka
Spring Cloud Bus 多端刷新
注意整个项目的搭建是一步一步来的,重复的步骤不会重复出现。
2.1 Spring Cloud Config + 手动刷新
Spring Cloud Config Server 首先需要搭建 Spring Cloud Config Server 服务,Spring Cloud Config Server 服务应该作为一个单独的应用运行和维护,所以我们单独为 Spring Cloud Config Server 启动一个服务。依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
复制代码
我这里选择的 Spring Boot 版本和 Spring Cloud 版本如下(版本如果不对应会出现异常,大家可以选择自己需要的对应版本):
<!--spring boot 版本 2.3.4.RELEASE-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<!--spring cloud 版本 Hoxton.RELEASE-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>>Hoxton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
复制代码
application.yml 配置文件:下面的配置文件中有几个点比较重要,配置错误将无法获取配置信息 a、default-label,配置文件所在分支,默认值为 master
b、search-paths,配置文件所在根目录
c、uri ,仓库地址
## 服务名
spring:
application:
name: config-service
## config git相关配置
cloud:
config:
server:
git:
uri: https://gitee.com/leonplious/config-server-demo.git # 仓库地址
username: xxxx # git 登录账户
password: xxxx # git 登录密码
default-label: master # 分支
search-paths: userservice # 分支下根文件夹名
## 服务端口
server:
port: 28888
复制代码
编写启动类,启动类上需要添加 @EnableConfigServer 注解
@SpringBootApplication
@EnableConfigServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
复制代码
此时我们可以开始测试 Spring Cloud Config Server。推送一个配置文件到 git 仓库中,配置文件的名称为 userservice-dev.yml,配置文件的内容如下:
user:
username: "liziba"
password: "hello"
复制代码
启动 Spring Cloud Config Server 应用,访问http://localhost:28888/userservice/dev/master,可以得到如下信息,证明 Spring Cloud Config Server 服务启动成功。
{
"name":"userservice",
"profiles":[
"dev"
],
"label":"master",
"version":"285fd1b9f068cec6def6ba14ab787807a9ffecbc",
"state":null,
"propertySources":[
{
"name":"https://gitee.com/leonplious/config-server-demo.git/userservice/userservice-dev.yml",
"source":{
"user.username":"liziba",
"user.password":"hello"
}
}
]
}
复制代码
注意 Spring Cloud Config 有它的一套访问规则,通过这套规则可以获取相应数据,数据的响应格式略有不同。
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
复制代码
我这里使用的是第一种方式,这种方式能够返回详细的配置信息,以及分支信息、profile 信息、应用名等,默认的分支名 master 可以省略。http://localhost:28888/userservice/dev等同于http://localhost:28888/userservice/dev/master
接下来就可以开始配置 Spring Cloud Config Client 新建 Spring Cloud Config Client 服务,该服务会从 Spring Cloud Config Server 中获取配置信息。依赖:
<!--config客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--web提供rest访问端点-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--actuator提供端点触发更新-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
复制代码
配置文件 bootstrap.yml:需要注意使用 config 获取配置信息时,我们需要将 config 相关配置提取到优先级最高的 bootstrap.yml 配置文件中,否则不会生效。spring-cloud-starter-config 默认会访问 8888 端口,如果你的 Spring Cloud Config Server 并未使用该端口启动,可以在 bootstrap.yml 文件中指定 Spring Cloud Config Server 端口信息,这样才能覆盖,否则获取不到 Spring Cloud Config Server 上的配置信息。我这里的配置文件演示了多环境 dev 和 prod,注意我的 config.uri 地址时http://localhost:28888,并不是http://localhost:8888
server:
port: 18888
spring:
application:
name: userservice
profiles:
active: dev
## 加载并暴露所有端点,用于或许刷新端点
management:
endpoints:
refresh:
enabled: true
web:
exposure:
include: '*'
## 配置中心无法访问,返回此数据
user:
username: NaN
password: NaN
---
spring:
profiles: dev
cloud:
config:
uri: http://localhost:28888
label: master
profile: dev
fail-fast: true
---
spring:
profiles: prod
cloud:
config:
uri: http://localhost:28888
label: master
profile: prod
fail-fast: true
复制代码
编写两个 User 和 User2 用于获取配置信息,两个类获取配置的方式不一样,分别通过 @Value 或和**@ConfigurationProperties **来获取:
@Data
@ToString
@Component
public class User {
@Value("${user.username}")
private String username;
@Value("${user.password}")
private String password;
}
复制代码
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "user")
public class User2 {
private String username;
private String password;
}
复制代码
编写 rest 访问端点,获取两个 User 的配置信息:
@RequiredArgsConstructor
@RestController
@RequestMapping("/user")
public class UserController {
private final User user;
private final User2 user2;
@GetMapping("/user1")
public String user() {
return user.toString();
}
@GetMapping("/user2")
public String user2() {
return user2.toString();
}
}
复制代码
启动服务,分别访问两个 rest 端点,此时可以分别获取到如下信息,说明我们配置信息已经获取到了。http://localhost:18888/user/user1
User(username=liziba, password=hello)
复制代码
http://localhost:18888/user/user2
User2(username=liziba, password=hello)
复制代码
此时我们将 username 修改成中文‘李子捌’,并推送到 Gitee;修改后的配置文件如下:
user:
username: "李子捌"
password: "hello"
复制代码
再次访问两个 rest 端点,发现两个请求返回的都是旧数据,并没有获取到最新的配置。别慌,没人通知他更新它肯定是旧数据呀!这个时候我们引入的 actuator 依赖和 management.endpoints 配置就派上用场了。我们可以借助 postman、curl 等 http 工具向http://localhost:18888/actuator/refresh端点发起 post 请求。
再次访问两个 rest 端点,分别返回如下数据http://localhost:18888/user/user1
User(username=liziba, password=hello)
复制代码
http://localhost:18888/user/user2
User2(username=李子捌, password=hello)
复制代码
@Value 获取配置信息的方式并未获取到最新数据,而**@ConfigurationProperties 获取配置信息获取到了更新后的数据,所以我们在开发的时候记得使用 @ConfigurationProperties 来结合 config 获取配置信息。**
2.2 Spring Cloud Config + Git + WebHook 实现自动刷新
实现自动刷新的功能,我们需要借助两个东西;第一个是 GitHub、Gitee、GitLab 提供的 WebHook 功能,第二个是**@RefreshScope 注解。**第一步:配置 WebHook(我这里采用的是 Gitee)进入你的配置文件所在仓库地址,选择管理页签之后选择 WebHooks,点击添加 WebHook
注意 URL 中填写的是 actuator 提供的 refresh 端点,也就是我们上面用 postman 请求的地址,你可以选择你需要触发调用该地址的事件,一般选择 Push。特别需要注意的是,你是要 Gitee 或 GitHub 需要提供一个公网地址,一般公司内部都会搭建 GitLab 代码仓库,公司内部可以使用内网地址。
第二步:添加 @RefreshScope 注解
@RequiredArgsConstructor
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController {
// ...
}
复制代码
2.3 Spring Cloud Config + Eureka
大部分情况下,在微服环境中我们都会使用配置中心,这里采用 Eureka 配置中心,结合 Spring Cloud Config 实现配置动态刷新。实现这个功能我们需要引入 Eureka 的依赖,启动一个注册中心服务,并修改 Spring Cloud Config Server 相关配置和 Spring Cloud Config Client 相关配置。依赖:
<!--Eureka Server依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
复制代码
配置文件:
server:
port: 8888
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
复制代码
启动类:
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
复制代码
修改 Spring Cloud Config Server 增加依赖:
<!--Eureka Client依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
复制代码
修改配置文件(增加 eureka 客户端配置):
spring:
application:
name: config-service
cloud:
config:
server:
git:
uri: https://gitee.com/leonplious/config-server-demo.git
username: xxxx
password: xxxx
default-label: master
search-paths: userservice
server:
port: 28888
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8888/eureka/
instance:
prefer-ip-address: true
复制代码
修改启动类(增加 @EnableEurekaClient 注解):
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
复制代码
此时启动 EurekaServer 和 Spring Cloud Config Server,可以在 Eureka 上看到 Spring Cloud Config Server 注册信息。
修改 Spring Cloud Config Client 增加依赖:
<!--Eureka Client依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
复制代码
修改配置文件(增加 eureka 客户端配置,修改 Config 配置信息):未使用注册中心我们指定 uri,使用注册中心之后我们可以直接使用服务名。
server:
port: 18888
spring:
application:
name: userservice
profiles:
active: dev
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8888/eureka
instance:
prefer-ip-address: true
## 加载所有端点
management:
endpoints:
refresh:
enabled: true
web:
exposure:
include: '*'
## 配置中心无法访问,返回此数据
user:
username: NaN
password: NaN
---
spring:
profiles: dev
cloud:
config:
discovery:
enabled: true
service-id: config-service # 配置Config Server服务名
# uri: http://localhost:28888
label: master
profile: dev
fail-fast: true
---
spring:
profiles: prod
cloud:
config:
discovery:
enabled: true
service-id: config-service
# uri: http://localhost:28888
label: master
profile: prod
fail-fast: true
复制代码
修改启动类(增加 @EnableEurekaClient 注解):
@SpringBootApplication
@EnableEurekaClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
复制代码
重新访问两个 rest 端点,可以效果一致。如果需要配置 Eureka 的高可用集群,在我的《Spring Cloud 系列专栏》中有文章可以参考。
2.4 Spring Cloud Bus 多端刷新
在生产环境中,我们往往会集群部署,此时我使用 WebHook 来刷新单个端点就显得很鸡肋了。这个时候我们可以使用 Spring Cloud Bus 来实现多端刷新。它是通过 Message Queue 来广播配置更新通知来实现的。官方介绍地址:
https://spring.io/projects/spring-cloud-bus
我们先安装 RabbitMQ 来使用 Spring Cloud Bus(官方支持 rabbit 和 kafka)
修改 Spring Cloud Config Client 应用中的相关配置和依赖依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
复制代码
修改配置文件(引用 rabbit 配置,其他配置不变):
server:
port: 18888
spring:
application:
name: userservice
profiles:
active: dev
rabbitmq:
host: localhost
port: 5672
password: guest
username: guest
virtual-host: /
复制代码
此时我们使用 IDEA 将 Spring Cloud Config Client 应用使用不同端口启动两个服务选中服务后点击 Edit Configurations...
修改配置,指定端口和服务名,并勾选 Allow parallel run
复制一份配置,使用另一端口启动
此时可以访问单点 Eureka,查看注册信息
RabbitMQ 上有两个匿名队列
此时我们根据不同的端口访问 rest 端口http://localhost:19999/user/user2http://localhost:18888/user/user2
User2(username=liziba, password=123456)
复制代码
修改 Gitee 上的配置文件
user:
username: "Edg"
password: "champion"
复制代码
如果没有配置 WebHook 则使用 postman 发起一个 post 请求到http://localhost:18888/actuator/bus-refresh端点,注意这里是 bus-refresh。重新访问两个 rest 端点,此时配置信息已刷新:
评论