Spring Cloud(八)分布式消息总线

消息总线的定义

消息总线是一种通信工具,可以在机器之间互相传输消息、文件等,他扮演着一种消息路由的角色,拥有一套完备的路由机制来决定消息传输方向。发送端只需要向消息总线发出消息,而不用管消息被如何转发。

Spring Cloud Bus通过轻量消息代理连接各个分布的节点。管理和传输所有分布式项目中的消息,本质是利用了MQ的广播机制在分布式的系统中传输消息,目前常用的有Kafka和RabbitMQ等。

消息总线常见的设计模式

在消息总线中,常见的设计模式有点对点模式及订阅/发布模式。

点对点

点对点模式包含三个角色:消息队列(Queue)、生产者(Producer)、消费者(Consumer)。点对点模式中的每个消息都被发送到一个特定的队列,消费中从队列中获取消息。队列中保留着消息,直到他们被消费或超时。点对点模式的运行流程图如下图:

Spring Cloud(八)分布式消息总线_第1张图片

点对点模式具有以下特点:

  • 每个消息只有一个消费者,即消息一旦被消费,就不在消息队列中了。
  • 生产者和消费者之间在时间上没有依赖性,也就是说当生产者发送了消息之后,不管消费者有没有正在运行,都不会影响到消息被发送到队列。
  • 消费者在成功接收消息之后需向队列应答成功,这样消息队列才能知道消息是否被成功消费。

订阅/发布(Pub/Sub)

订阅/发布模式包含三个角色:主题(topic)、发布者(Publisher)、订阅者(Subscriber)。

订阅/发布模式中,多个发布者将消息发送到对应的主题,系统将这些消息传递给多个订阅者。下图展示了订阅/发布模式的运行流程图:

Spring Cloud(八)分布式消息总线_第2张图片

订阅/发布模式具有以下特点:

  • 每个消息可以有多个消费者
  • 主题可以被认定是消息的传输中介,发布者发送消息到主题,订阅者从主题订阅消息。
  • 主题使得消息订阅者和消息发布着保持互相独立,不需要接触即可保证消息的传送。 

消息总线的意义

 在微服务架构中,经常会使用REST服务作为服务间的通信机制。REST以其轻量、简单、易理解而著称,但这种通信机制也并非适合所有的场景。例如,在一些高并发、高可靠、实时的场景,则需要消息总线来帮忙。消息总线具有以下几个特点:

  • 实时性高

与REST服务的“请求-相应”模式不同,消息总线的实时性非常高。使用了消息总线,生产者一方只要把消息往队列里一扔,就可以立马返回,相应用户了。无需等待处理结果,实现了异步处理。

同时,对于消费者而言,消费者对于消息的到达感知也非常及时。消费者会对消息总线进行监听,只要有消息进入队列,就可以马上得到通知。这种优势是REST服务所不能具备的。在REST服务中,要想及时获取到更新消息,就不得不进行轮询,这往往非常低效。

  • 生产者与消费者解耦

在消息总线中,生产者负责将消息发送到队列中,而消费者把消息从队列中取出来。生产者无需等待消费者启动,消费者也无需关心生产者是否已经处于就绪状态。所以,这种模式能很好的实现生产者与消费者的解耦。

然而,如果是在REST服务中,服务调用方必须等待服务的提供方准备好了才能调用,否则就会调用失效。

  • 故障率低

消息总线拥有对其他通信方式更高的成功率。一方面,生产者与消费者之间实现了解耦,所以,生产者和消费者之间不存在强关联关系,即便是生产者或消费者任意一方掉线了,也不会影响消息最终的送达;另一方面,消息总线往往会结合数据库来实现消息的持久化,并设置状态标识。只有消息消费成功,才会去修改状态标志。

消息总线同时还承担着缓存区的作用。大量业务消息首先会进入消息队列进行缓存,消息的消费者可以根据自己的处理能力来进行消费,所以不管消息的数据量有多少,都不会对消费者造成冲击。

Spring Cloud Bus实现消息总线

Spring Cloud Bus通过轻量消息代理连接各个分布的节点,管理和传播所有分布式项目中的消息,本质是利用了消息中间件的广播机制在分布式的系统中传播消息。

目前,Spring Cloud Bus所支持的常用的消息中间件有RabbitMQ和Kafka,使用时只需要添加spring-cloud-starter-bus-amqp或spring-cloud-starter-bus-kafka依赖即可。同时,需要确保相关的消息中间件连接配置正确。

Spring Cloud Bus支持消息发送到所有已监听的节点,或者某个特定服务的所有节点。同时,Spring Cloud Bus提供了一些HTTP接口/bus/*,用于触发Spring Cloud Bus内部的事件。

目前,Spring Cloud Bus主要有以下两个接口实现:

  • /bus/env:发送键值对去更新每个节点的Spring Environment。
  • /bus/refresh:重新加载每一个应用的配置信息,类似于/refresh。

所以,Spring Cloud Bus结合Spring Cloud Config的使用,可以实现配置文件的自动更新。接下来的例子我们来实现这个功能。

集成Bus,实现配置文件的自动更新

集成Bus

pom文件添加依赖


    org.springframework.cloud
    spring-cloud-starter-bus-amqp

修改application.properties

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#值设为false,用于禁用安全管理设置,利于本地调试。如果实在公司内网部署,不仅有物理隔离,也可以禁用安全管理设置
management.security.enabled=false 

下载安装运行RabbitMQ

动态加载变量的类上面加载@RefreshScope注解

@RestController
@RefreshScope
public class HelloController {
   @Value("${hello}")
   private String hello;
   @RequestMapping("/hello")
   public String hello() {
       return hello;
   }
}

 

 实现配置信息的自动更新

刷新配置信息

Spring Cloud Bus提供了多种方式来更新微服务实例的配置信息。总结如下:

  • 使用/refresh方法

使用/refresh方法,可以更新单个微服务实例配置。例如,微服务实例 msa-weather-city-service 部署在8082端口,则发送POST请求到http://localhost:8082/refresh,可以出发该微服务实例,去获取最新的配置信息。

同样的,发送POST请求到http://localhost:8082/bus/refresh,可以出发该微服务实例,去获取最新的配置信息。同时,使用/bus/refresh方法,可以更新多个微服务实例的配置信息。例如,在8081和8083上都不熟了微服务实例,当使用/bus/refresh方法在任意一个微服务实例上触发时,另外一个微服务实例也能自动更新。这就是Spring Cloud Bus所带来的好处,让更多信息在多个微服务实例之间进行广播,从而能够通知到所有的微服务实例。

一般,当为服务的配置需要更新时,并不会在每个微服务实例上去触发更新信息,而是去触发配置服务器上的/bus/refresh方法,从而将更新事件发送给所有的微服务实例。

局部刷新 

  • 使用/bus/refresh方法

 某些场景下(如灰度发布),可能只想刷新部分微服务的配置,此时,可通过/bus/refresh断点的destination参数来定位要刷新的微服务实例。

例如,/bus/refresh?destination=msa-weather-city-service:8082,这样消息总线上的微服务实例就会根据destination参数的值来判断是否需要刷新。

实现配置信息的自动更新

虽然使用触发/bus/refresh请求到配置服务器,可以避免手动刷新微服务实例配置的繁琐过程,但该触发过程仍然是手动的。是否可以自动来刷新配置呢?比如当配置的Git仓库中变更了,可否能够及时通知到配置服务器呢?当然是可以的,借助Git仓库的Webhook功能就能实现这个目的。

配置信息自动更新的流程为:

  • 将配置修改信息推送到Git仓库;
  • 当Git仓库接收到配置信息后,会通过Webhook发送/bus/refresh到Bus;
  • Bus发送变更事件给所有的微服务实例;
  • 微服务实例从配置中心获取到最新的配置。

使用GitHub的Webhook,只需要在GitHub的Payload URL填写相应的配置中心触发刷新的地址即可。URL需是外网能访问到的。

Spring Cloud(八)分布式消息总线_第3张图片

你可能感兴趣的:(Java)