SpringCloud Bus
特点: Spring Cloud Bus将轻量级消息代理程序链接到分布式系统的节点。然后可以使用此代理来广播状态更改(例如配置更改)或其他管理指令。一个关键的想法是,总线就像是横向扩展的Spring Boot应用程序的分布式执行器。但是,它也可以用作应用之间的通信渠道。该项目为RabbitMQ或Kafka提供了入门服务。
如果你有兴趣可以去SpringCloud Bus官方文档了解更多。
在我们上节讲到的SpringCloud config 配置中心的搭建和使用这节的时候,已经有了一定的了解。
问题:
如果我们有多个微服务客户端,当我们在更新github或者gitee上面的配置以后,如果想要获取到最新的配置,需要手动刷新或者利用webhook的机制每次提交代码发送请求来刷新客户端,客户端越来越多的时候,需要每个客户端都执行一遍。这样用起来就很繁琐。所以我们想有没有一个工具,可以做到一次广播,一次通知,处处生效呢?
Springcloud Bus则可以做到这一点。它可以和SpringCloud Config配合使用实现配置的动态刷新。
是什么:
我们可以把它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,目前常用的有Kafka和RabbitMQ。利用bus的机制可以做很多的事情,比如广播状态更改,事件推送等。
其中配置中心客户端刷新就是典型的应用场景之一,我们用一张图来描述bus在配置中心使用的机制。
根据此图我们可以看出利用Spring Cloud Bus做配置更新的步骤:
- 提交代码触发post给客户端A发送bus/refresh
- 客户端A接收到请求从Server端更新配置并且发送给Spring Cloud Bus
- Spring Cloud bus接到消息并通知给其它客户端
- 其它客户端接收到通知,请求Server端获取最新配置
- 全部客户端均获取到最新的配置
改进
而刷新所有客户端配置的目的,但是这种方式并不合适,如下:
- 打破了微服务的职责单一性。微服务本身是业务模块,它本不应该承担配置刷新的职责。
- 破坏了微服务各节点的对等性。
- 如果客户端ip有变化,这时我们就需要修改WebHook的配置。
我们可以将上面的流程改进一下,配置中心server端刷新,而刷新所有的客户端的配置:
这时Spring Cloud Bus做配置更新步骤如下:
- 提交代码触发post给Server端发送bus/refresh
- Server端接收到请求并发送给Spring Cloud Bus
- Spring Cloud bus接到消息并通知给其它客户端
- 其它客户端接收到通知,请求Server端获取最新配置
- 全部客户端均获取到最新的配置
这样的话我们在server端的代码做一些改动,来支持/actuator/bus-refresh
显然,二图的架构更加适合,所以接下来我将以图二的架构进行实例
1.项目使用示例
我们使用上一篇文章中的config-server-3344,config-client-3355,config-client-3366(复制3355模块创建的3366模块)来进行改造,mq使用rabbitmq来做示例
1.1 config-client-3355,config-client-3366客户端改造
注意:为了演示,两个模块是一样的(除了端口),所以下面以3355为例,添加的时候,两个都要添加,
1.1.1 添加pom依赖
需要多引入spring-cloud-starter-bus-amqp包,增加对消息总线的支持
<!--添加消息总线rabbitmq支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
1.1.2 bootstrap.yml配置文件增加对rabbitmq的配置
server:
port: 3355
spring:
application:
name: config-client
cloud:
config:
label: master #分支名称
name: config #配置文件名称
profile: dev #读取后缀名称
#综上所述,结合起来就是读取master分支config-dev.yml的配置文件 得到地址:http://localhost:3344/master/config-dev.yml
#-和yml后缀系统自动配,所以创建配置文件名规范很重要
uri: http://localhost:3344
#新增的rabbitmq相关配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
#register-with-eureka: false
#暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
1.2 config-server-3344配置中心服务端改造
1.2.1 添加pom依赖
<!--添加消息总线rabbitmq支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
1.2.2 bootstrap.yml配置文件增加对rabbitmq的配置
server:
port: 3344
spring:
application:
name: cloud-config-center
cloud:
config:
server:
#gitee上面的git仓库
git:
uri: https://gitee.com/do_not_envy/springcloud2020-config.git
#搜索路径
search-paths:
- springcloud2020-config
#通过将属性设置为true(默认值为false),可以禁用配置服务器对Git服务器的SSL证书的验证。
skipSslValidation: true
#私有仓库需要配置用户名和密码
username: xxxxxxxxx
password: xxxxxxxxx
label: master
#rabbitmq相关配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
##rabbitmq相关配置,暴露bus刷新配置的端点
management:
endpoints: #暴露bus刷新配置的端点
web:
exposure:
include: "*"
endpoint:
bus-env:
enabled: true
1.3 测试
依次启动eureka,config-server-3344,config-client-3355,config-client-3366。
启动完成后,浏览器分别访问连接:http://localhost:3355/configInfo,http:///localhost:3366/configInfo, 可以发现页面显示的内容都是:
说明客户端都已经读取到了server端的内容。现在我们修改gitee上的配置文件,将 version改为4
我们刷新http://localhost:3355/configInfo,http://localhost:3366/configInfo发现内容并没有变化。
以前只能使用curl -X POST “http://localhost:客户端端口/actuator/refresh” 刷新指定的客户端,两个或多个要刷新多次,现在我们使用消息消息总线,刷新的方式有了改变,为了能一次广播,一次通知,处处生效,现在使用如下方式刷新:
curl -X POST “http://localhost:服务端端口/actuator/bus-refresh”
我们在去刷新http://localhost:3355/configInfo,http://localhost:3366/configInfo 两个客户端都更新成功了
1.4 基本原理
ConfigClient实例都监听MQ中同一个topic(默认是SpringCloudBus).当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样监听同一个Topic的服务就能得到通知,然后去更新自身的配置.
在刷新请求的时候,可以监测到,如下图:
1.5 SpringCloud Bus 动态刷新定点通知
指定某个实例生效而不是全部
刷新请求
curl -X POST “http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}”
/bus/refresh请求不在发送到具体的服务实例上,而是发给config server并通过destination参数指定需要更新配置的服务或者实例
如你刷新运行在3355端口上上config-client为例,只通知3355,不通知3366
curl -X POST “http://localhost:3344/actuator/bus-refresh/config-client:3355”
config-client:3355:服务名称:端口