Springcloud基础知识(10)- Spring Cloud Config (二) | 刷新 Config 配置
在 “Springcloud基础知识(9)- Spring Cloud Config (一) | 搭建 Config 服务端和客户端” 里 SpringcloudDemo03 项目基础上,我们创建了 ConfigServer 和 ConfigClient 子模块。
我们修改 Gitee 上的配置文件 config-dev.yml 后,得到的测试结论:
(1) 配置更新后,ConfigServer 不需要重启,就从 Git 仓库中获取了最新的配置;
(2) ConfigClient 需要重启,才能获得最新的配置信息;
很显然,在实际应用中,经常重启一个应用会影响用户体验,而且,有些场合的应用是不允许经常重启的。为了解决不重启 ConfigClient 无法获取最新配置的问题,我们需要手动刷新配置或动态刷新配置。
1. 手动刷新配置
这里我们对 ConfigClient 进行改造,改造步骤如下。
1) 修改 ConfigClient 的 pom.xml 文件,引入 Spring Boot actuator 监控模块。
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-actuator</artifactId> 4 </dependency>
2) 修改 src/main/resources/bootstrap.yml 文件,对外暴露 Spring Boot actuator 的监控节点。
1 # Spring Boot actuator 监控 2 management: 3 endpoints: 4 web: 5 exposure: 6 include: "*" # "*" 表示开启全部节点,多个节点用逗号分隔,比如 "beans,bus-refresh,channels"
3) 修改 src/main/java/com/example/controller/ConfigController.java 文件,使用 @RefreshScope 注解开启配置刷新。
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 import org.springframework.cloud.context.config.annotation.RefreshScope; 7 8 @RefreshScope 9 @RestController 10 public class ConfigController { 11 @Value("${server.port}") 12 private String port; 13 @Value("${config.company}") 14 private String company; 15 @Value("${config.version}") 16 private String version; 17 18 @GetMapping(value = "/get/config") 19 public String getConfig() { 20 return "Company: " + company + "<br/>Version:" + version + "<br/>Port:" + port; 21 } 22 }
4) 运行
重启 ConfigClient 模块,修改 config-dev.yml 的 version 为 3.0,并提交到 Gitee 仓库,浏览器访问 http://localhost:3001/master/config-dev.yml,显示结果如下:
config:
company: com.example
version: 3.0
浏览器访问 http://localhost:4001/get/config,显示结果如下:
Company: com.example
Version:2.0
Port:4001
注:很显然,改造后的 ConfigClient,也无法直接获取到最新配置。
在命令行下,运行如下命令:
curl -X POST "http://localhost:4001/actuator/refresh"
浏览器再次访问 http://localhost:4001/get/config,显示结果如下:
Company: com.example
Version:3.0
Port:4001
5) 手动刷新配置的局限
通过在 ConfigClient 中引入 Spring Boot actuator 监控组件来监控配置的变化,使我们可以在不重启 Config 客户端的情况下获取到了最新配置。
这种方式虽然解决了重启 ConfigClient 才能获取最新配置的问题,但另一个问题却也接踵而至,那就是只要配置仓库中的配置发生改变,就需要我们逐个向 ConfigClient 手动发送 POST 请求,通知它们重新拉取配置。
ConfigClient 是一个一个的服务。在微服务架构中,一个系统往往包含十几甚至几十个服务,如果因为某一个配置文件的修改而向几十个微服务发送 POST 请求,这显然是不合理的。
那么有没有 “一次通知,处处生效” 的方式呢?答案是肯定的。Spring Cloud Config 配合 Bus 就可以实现配置的动态刷新。
2. Config + Bus 实现配置的动态刷新
Spring Cloud Bus 又被称为消息总线,它能够通过轻量级的消息代理(例如 RabbitMQ、Kafka 等)将微服务架构中的各个服务连接起来,实现广播状态更改、事件推送等功能,还可以实现微服务之间的通信功能。
目前 Spring Cloud Bus 支持两种消息代理:RabbitMQ 和 Kafka。
Spring Cloud Bus 的基本原理:
Spring Cloud Bus 会使用一个轻量级的消息代理来构建一个公共的消息主题 Topic(默认为“springCloudBus”),这个 Topic 中的消息会被所有服务实例监听和消费。当其中的一个服务刷新数据时,Spring Cloud Bus 会把信息保存到 Topic 中,这样监听这个 Topic 的服务就收到消息并自动消费。
Spring Cloud Bus 动态刷新配置的原理:
利用 Spring Cloud Bus 的特殊机制可以实现很多功能,其中配合 Spring Cloud Config 实现配置的动态刷新就是最典型的应用场景之一。
当 Git 仓库中的配置发生了改变,只需要向某一个服务(既可以是 Config 服务端,也可以是 Config 客户端)发送一个 POST 请求,Spring Cloud Bus 就可以通过消息代理通知其他服务重新拉取最新配置,以实现配置的动态刷新。
Spring Cloud Bus 实现配置的动态刷新的步骤:
(1) 当 Git 仓库中的配置发生改变后,运维人员向 Config 服务端发送一个 POST 请求,请求路径为“/actuator/refresh”;
(2) Config 服务端接收到请求后,会将该请求转发给服务总线 Spring Cloud Bus;
(3) Spring Cloud Bus 接到消息后,会通知给所有 Config 客户端;
(4) Config 客户端接收到通知,请求 Config 服务端拉取最新配置;
(5) 所有 Config 客户端都获取到最新的配置;
1) 全局广播
本文以 Kafka 为例,来演示如何使用 Config + Bus 实现配置的动态刷新,关于 Kafka 请参考 “ Springboot 系列 (16) - Springboot+Kafka 实现发布/订阅消息 ”。
(1) 修改 ConfigServer 模块的 pom.xml 文件,添加 Spring Boot actuator 监控模块和 Spring Cloud Bus 的依赖
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-actuator</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>org.springframework.cloud</groupId> 7 <artifactId>spring-cloud-starter-bus-kafka</artifactId> 8 </dependency>
(2) 修改 ConfigServer 模块的 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/xxx/springcloud-demos-config.git # 仓库地址 11 search-paths: 12 - springcloud-demos-config # 仓库名 13 force-pull: true 14 username: xxx 15 password: xxx 16 label: master 17 bus: 18 trace: 19 enabled: true 20 stream: 21 kafka: 22 binder: 23 brokers: 192.168.0.5:9092 # 192.168.0.5 是 kafka 运行的主机,下同 24 zk-nodes: 192.168.0.5:2181 25 auto-create-topics: true 26 27 eureka: 28 client: # 将客户端注册到 eureka 服务列表内 29 service-url: 30 # 将服务注册到 Eureka 集群 31 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ 32 33 # Spring Boot actuator 监控 34 management: 35 endpoints: 36 web: 37 exposure: 38 include: 'bus-refresh'
(3) 修改 ConfigClient 模块的 pom.xml 文件,添加 Spring Cloud Bus 的依赖
1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-starter-bus-kafka</artifactId> 4 </dependency>
(4) 修改 ConfigClient 模块的 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 bus: 14 trace: 15 enabled: true 16 stream: 17 kafka: 18 binder: 19 brokers: 192.168.0.5:9092 20 zk-nodes: 192.168.0.5:2181 21 auto-create-topics: true 22 23 eureka: 24 client: # 将客户端注册到 eureka 服务列表内 25 service-url: 26 # 将服务注册到 Eureka 集群 27 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ 28 29 # Spring Boot actuator 监控 30 management: 31 endpoints: 32 web: 33 exposure: 34 include: "*" # "*" 表示开启全部节点,多个节点用逗号分隔,比如 "beans,bus-refresh,channels"
(5) 打包,配置 configserver-3001、configclient-4001、configclient-4002
1 eureka 2 |- configserver-3001 3 | |- ConfigServer-1.0-SNAPSHOT.jar 4 | |- application.yml 5 | |- start.bat 6 | 7 |- configclient-4001 8 | |- ConfigClient-1.0-SNAPSHOT.jar 9 | |- application.yml (由 bootstap.yml 改名) 10 | |- start.bat 11 | 12 |- configclient-4002 13 |- ConfigClient-1.0-SNAPSHOT.jar 14 |- application.yml (由 bootstap.yml 改名) 15 |- start.bat
configclient-4002 和 configclient-4001 运行的端口号是 4002 和 4001,其它配置一样。
(6) 运行
确保 Kafka 已经处于正常运行状态,本文的 Kafka 运行在内网的 192.168.0.5 主机。
依次启动 server-7001、configserver-3001、configclient-4001 和 configclient-4002。
浏览器访问 http://localhost:4001/get/config 和 http://localhost:4002/get/config,显示结果如下:
Company: com.example
Version:3.0
Port:4001
修改 config-dev.yml 的 version 为 4.0,并提交到 Gitee 仓库。
在命令行下,运行如下命令:
curl -X POST "http://localhost:3001/actuator/bus-refresh"
浏览器访问 http://localhost:4001/get/config 和 http://localhost:4002/get/config,应该都显示结果如下:
Company: com.example
Version:4.0
Port:4001 (或 4002 )
2) 定点通知
定点通知就是不再通知所有的 Config 客户端,而是根据需求只通知其中某一个 Config 客户端。
使用 Spring Cloud Bus 实现定点通知的方法十分简单,只要我们在发送 POST 请求时使用以下格式即可。
http://{hostname}:{port}/actuator/bus-refresh/{destination}
参数说明如下:
{hostname}: 表示 Config 服务端的主机地址,既可以是域名,也可以是 IP 地址。
{port}:表示 Config 服务端的端口号.
{destination}:表示需要定点通知的 Config 客户端(微服务),由 Config 客户端的服务名(spring.application.name)+ 端口号(server.port)组成。
示例:
修改 config-dev.yml 的 version 为 5.0,并提交到 Gitee 仓库。
在命令行下,运行如下命令:
curl -X POST "http://localhost:3001/actuator/bus-refresh/spring-cloud-config-client:4002"
浏览器访问 http://localhost:4001/get/config,显示结果如下:
Company: com.example
Version:4.0
Port:4001
浏览器访问 http://localhost:4002/get/config,显示结果如下:
Company: com.example
Version:5.0
Port:4002