git 上源码地址为:
https://github.com/IChampionZhang/spring-cloud-config-server-demo.git
https://github.com/IChampionZhang/spring-cloud-bus-demo.git
假定现在的 Eureka 注册中心、配置中心以及客户端应用的简约配置如下所示:
Eureka Server 端口:10000
配置中心端口:9002
配置中心名称:config-server
客户端应用端口:10005
客户端应用名称:busdemo(busdemo-dev 表示的是开发环境的配置文件)
后面的请求都是基于上面这样的端口设定来执行的。
配置中心 config-server 搭配 Git 使用之后,当有新的配置更新时(假定这次更新叫做 A,更新内容是修改age的值:age from 28 to 30),我们首先会将更新A push 到 git 上面去,此时如果通过 config-server 的端口来访问 busdemo-dev.yml 文件的话,确实可以得到更新A,但是如果我们将监听 config-server 的客户端应用 SpringCloudBusDemoApplication 启动后,访问它的启动端口例如10005:http://localhost:10005/actuator/env 时却发现无法得到更新A 的更新内容。那么客户端如何才能得到最新的配置信息呢?
访问客户端应用的 /actuator/refresh 端点刷新配置
首先可以通过访问 http://localhost:10005/actuator/refresh 来将 SpringCloudBusDemoApplication 的配置刷新,再次访问 http://localhost:10005/actuator/env 就可以看到更新 A 的内容,但是这样的操作仅仅可以更改 /actuator/env 的数据,对于正在运行中的 SpringCloudBusDemoApplication,如果引用了更新 A 的更新内容 age,那么访问 SpringCloudBusDemoApplication 应用时得到的 age 还是 28,例如访问 SpringCloudBusDemoApplication 的 http://localhost:10005/consumer
启动后的客户端应用配置刷新:热更新
对于这种热更新的情况,我们可以在客户端应用 http://localhost:10005/consumer 请求对应的 Controller上面加上一个 @RefreshScope 注解,当 http://localhost:10005/actuator/env 的访问得到更新时,应用内的访问也会得到更新。
总结:当修改配置文件提交到 git 上面去,作为配置中心 config-server(访问config-server:http://localhost:9002/busdemo/dev) 会第一时间收到更新,而依赖于配置中心的 http://localhost:10005/actuator/env 需要通过 http://localhost:10005/actuator/refresh 手动刷新;而对于应用内的数据则需要通过在 controller 上添加 @RefreshScope 注解来使得应用的数据可以与 http://localhost:10005/actuator/env 的数据同步更新
Spring Cloud Bus 自动刷新的工作原理
spring cloud bus 的 spring.factories 里面的 BusAutoConfiguration 使用 EventListener 定义了监听 RemoteApplicationEvent 类型事件的监听器 acceptLocal 方法,该监听器收到事件之后就会将事件发送到 springCloudBusOutput 这个通道中:
同时也配置了 acceptRemote 来监听 springCloudBusInput 上面的流数据
另外 Spring Cloud Bus 还提供了 bus-refresh 这个端点来发出一个 RefreshRemoteApplicationEvent 事件,该事件是 RemoteApplicationEvent 类型,因此对于这个端点的请求将同时触发上面刚开始提到的 acceptLocal 方法(监听的是 RemoteApplicationEvent 类型事件);
另外还会触发 RefreshListener 监听器的 onApplicationEvent 事件从而又会触发另外两个类型的事件:EnvironmentChangeEvent 和RefreshScopeRefreshedEvent 来对应用本身的环境配置进行刷新
Spring Cloud Bus - Bus Refresh 端点的使用
上面的过程需要人工的去对每个部署的应用执行一次应用端的请求:http://localhost:10005/actuator/refresh 实在是不方便,那有没有更方便的方式呢?
通过上面对 Spring Cloud Bus 的简短介绍,我们可以想到:通过发出 post 请求到配置中心端的 bus-refresh 这个端点来发布一个刷新的事件到 bus 上,另外一端的客户端应用也要导入 bus 并且配置好 bus 通道,客户端应用在接收到来自配置中心发出的刷新事件后开始重新从 config-server 读取最新配置。
你可以通过调用 http://localhost:9002/actuator/bus-refresh 对所有监听 bus 应用进行更新,也可以调用 http://localhost:9002/actuator/bus-refresh/busdemo 来对单个应用进行配置更新
Spring Cloud Config Monitor 的使用
除了使用 Spring Cloud Bus 本身提供的 Bus Refresh 端点之外,我们还可以在 pom.xml 文件中引入 Config Monitor:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-monitor</artifactId>
</dependency>
通过 Config Monitor 提供的 /monitor 这个路径并附上body:{"path":"busdemo"} 来发布一个 RefreshRemoteApplicationEvent 事件(上面 Spring Cloud Bus 自动刷新的工作原理 中有提到)从而对本地 config-server 配置进行更新并且通过 Bus 将刷新事件发送到消息通道中,客户端应用通过监听消息通道中的流数据从而收到刷新事件,具体可以查看 Config Monitor 的 jar包中 spring.factories 提到的 EnvironmentMonitorAutoConfiguration 配置以及 PropertyPathEndpoint 这个Controller的作用
以下是使用 Postman 模拟的 post 请求:
另外如果将配置中心端以及客户端 log.level 设置为 DEBUG 级别,可以看到如下消息:
自动刷新配置
以上还是需要人工的调用 config-server 端的配置,那么如何才能真正实现自动刷新配置呢?通过上面对于 Spring Cloud Bus 提供的 Bus-Refresh 端点以及 Spring Cloud Config Monitor 的 /monitor 路径的介绍,配合使用 git 上的 webhook 就可以实现自动刷新配置的功能:当你把新的配置文件 push 到 git 上之后,git 监听到这个 push 的动作就会将这个事件发送到你设定的 url 来进行通知。
配置中心相关配置
以下是配置中心的配置文件:
server:
port: 9002
spring:
application:
name: config-server-demo
# security:
# user:
# name: user
# password: 123456
cloud:
stream:
binders:
rabbit1:
type: rabbit
environment:
spring:
rabbitmq:
host: rabbitmq 服务器ip
port: 5672
username: admin
password: admin
publisher-confirms: true
bindings:
springCloudBusInput: # spring cloud bus 默认的输入通道名
binder: rabbit1
springCloudBusOutput: # spring cloud bus 默认的输出通道名
binder: rabbit1
bus:
destination: bus-queue # spring cloud bus 的目的地(也就是 exchange 的名字)
trace:
enabled: true
config:
server:
git:
uri: https://github.com/IChampionZhang/spring-cloud-config-server-demo.git
username: github 用户名
password: github 密码
basedir: git 上拉取到的配置文件的本地存放目录
clone-on-start: true
proxy: # 不需要代理可以去掉下面这段
http:
host: 代理 ip
port: 代理端口
https:
host: 代理 ip
port: 代理端口
encrypt:
enabled: true # 启用加密功能,秘钥的key在 bootstrap.xml 文件中,应用启动
# 后可以访问 http://localhost:9002/encrypt post 请求的方
# 式输入要加密的内容,也可以访问 http://localhost:9002/decrypt
# post 的请求的方式输入需要解密的密文
eureka: # 注册中心配置
client:
service-url:
defaultZone: http://localhost:10000/eureka/
management: # 开放所有的端点
endpoints:
web:
exposure:
include: "*"
配置中心的 pom 文件中的关键依赖:
<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.cloud</groupId>
<artifactId>spring-cloud-config-monitor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
客户端应用相关配置
以下是客户端应用配置文件:
spring:
cloud:
config:
name: busdemo
uri:
- http://localhost:9002/ # 配置中心地址
profiles:
active: dev
下面是它的 pom 文件中的关键依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
最后附上文章描述图: