SpringCloud Bus

特点: Spring Cloud Bus将轻量级消息代理程序链接到分布式系统的节点。然后可以使用此代理来广播状态更改(例如配置更改)或其他管理指令。一个关键的想法是,总线就像是横向扩展的Spring Boot应用程序的分布式执行器。但是,它也可以用作应用之间的通信渠道。该项目为RabbitMQ或Kafka提供了入门服务。

如果你有兴趣可以去SpringCloud Bus官方文档了解更多。

在我们上节讲到的SpringCloud config 配置中心的搭建和使用这节的时候,已经有了一定的了解。

问题:
如果我们有多个微服务客户端,当我们在更新github或者gitee上面的配置以后,如果想要获取到最新的配置,需要手动刷新或者利用webhook的机制每次提交代码发送请求来刷新客户端,客户端越来越多的时候,需要每个客户端都执行一遍。这样用起来就很繁琐。所以我们想有没有一个工具,可以做到一次广播,一次通知,处处生效呢?

Springcloud Bus则可以做到这一点。它可以和SpringCloud Config配合使用实现配置的动态刷新。

是什么:
我们可以把它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,目前常用的有KafkaRabbitMQ。利用bus的机制可以做很多的事情,比如广播状态更改,事件推送等。

其中配置中心客户端刷新就是典型的应用场景之一,我们用一张图来描述bus在配置中心使用的机制。

springcloud config 高可用 springcloud config bus_spring


根据此图我们可以看出利用Spring Cloud Bus做配置更新的步骤:

  1. 提交代码触发post给客户端A发送bus/refresh
  2. 客户端A接收到请求从Server端更新配置并且发送给Spring Cloud Bus
  3. Spring Cloud bus接到消息并通知给其它客户端
  4. 其它客户端接收到通知,请求Server端获取最新配置
  5. 全部客户端均获取到最新的配置

改进

而刷新所有客户端配置的目的,但是这种方式并不合适,如下:

  • 打破了微服务的职责单一性。微服务本身是业务模块,它本不应该承担配置刷新的职责。
  • 破坏了微服务各节点的对等性。
  • 如果客户端ip有变化,这时我们就需要修改WebHook的配置。

我们可以将上面的流程改进一下,配置中心server端刷新,而刷新所有的客户端的配置:

springcloud config 高可用 springcloud config bus_spring cloud_02


这时Spring Cloud Bus做配置更新步骤如下:

  1. 提交代码触发post给Server端发送bus/refresh
  2. Server端接收到请求并发送给Spring Cloud Bus
  3. Spring Cloud bus接到消息并通知给其它客户端
  4. 其它客户端接收到通知,请求Server端获取最新配置
  5. 全部客户端均获取到最新的配置
    这样的话我们在server端的代码做一些改动,来支持/actuator/bus-refresh

显然,二图的架构更加适合,所以接下来我将以图二的架构进行实例

1.项目使用示例

我们使用上一篇文章中的config-server-3344,config-client-3355,config-client-3366(复制3355模块创建的3366模块)来进行改造,mq使用rabbitmq来做示例

springcloud config 高可用 springcloud config bus_spring_03

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, 可以发现页面显示的内容都是:

springcloud config 高可用 springcloud config bus_Cloud_04


springcloud config 高可用 springcloud config bus_Cloud_05


说明客户端都已经读取到了server端的内容。现在我们修改gitee上的配置文件,将 version改为4

springcloud config 高可用 springcloud config bus_Cloud_06


我们刷新http://localhost:3355/configInfo,http://localhost:3366/configInfo发现内容并没有变化。

以前只能使用curl -X POST “http://localhost:客户端端口/actuator/refresh” 刷新指定的客户端,两个或多个要刷新多次,现在我们使用消息消息总线,刷新的方式有了改变,为了能一次广播,一次通知,处处生效,现在使用如下方式刷新:

curl -X POST “http://localhost:服务端端口/actuator/bus-refresh”

springcloud config 高可用 springcloud config bus_分布式_07


我们在去刷新http://localhost:3355/configInfo,http://localhost:3366/configInfo 两个客户端都更新成功了

springcloud config 高可用 springcloud config bus_分布式_08


springcloud config 高可用 springcloud config bus_Cloud_09

1.4 基本原理

ConfigClient实例都监听MQ中同一个topic(默认是SpringCloudBus).当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样监听同一个Topic的服务就能得到通知,然后去更新自身的配置.

在刷新请求的时候,可以监测到,如下图:

springcloud config 高可用 springcloud config bus_分布式_10

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:服务名称:端口