一、概述
前文说到:假如我们有很多的服务3355、3366、3377……,那是不是要给每一个服务发送一次手动刷新的post请求呢? 能不能做到只发送一次请求,让每个服务都收到更新通知,一次广播,处处生效呢?这就需要用到我们接下来要讲到的Bus消息总线。
一、什么是Bus
Spring Cloud Bus 配合 Spring Cloud Config 使用可以实现配置的动态刷新。
Spring Cloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架,它整合了Java的事件处理机制和消息中间件的功能。
Spring Clud Bus目前支持RabbitMQ和Kafka。
二、Bus能做什么
Spring Cloud Bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当作微服务间的通信通道。
三、为什么被称为总线
什么是总线:
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线。在总线上的各个实例,都可以方便地广播一些需要让其他连接在该主题上的实例都知道的消息。
基本原理:
ConfigClient实例都监听MQ中同一个topic(默认是springCloudBus)。当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样其它监听同一Topic的服务就能得到通知,然后去更新自身的配置。
二、Windows本地配置RabbitMQ
1、安装Erlang
http://erlang.org/download/otp_win64_21.3.exe
双击运行后,选择路径,无脑下一步
2、安装RabbitMQ
https://github.com/rabbitmq/rabbitmq-server/releases/tag/v3.7.14
双击运行后,选择路径,无脑下一步
3、添加可视化插件
进入RabbitMQ Server\rabbitmq_server-3.7.14\sbin ,打开命令行工具,输入命令
rabbitmq-plugins enable rabbitmq_management
4、看看是否安装成功
会出现登陆页面,默认账号密码均为guest
三、Bus动态刷新全局广播
1、为了展示广播效果,需要再来一个客户端。
(1)新建模块
新建普通maven模块 cloud-config-client-3366
(2)修改pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud</artifactId> <groupId>com.shang.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-config-client-3366</artifactId> <dependencies> <!--添加消息总线RabbitMQ支持--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> </project>
(3)编写bootstrap.yml文件
server: port: 3366 spring: application: name: config-client cloud: #Config客户端配置 config: label: master #分支名称 name: config #配置文件名称 profile: dev #读取后缀名称 上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.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 # 暴露监控端点 management: endpoints: web: exposure: include: "*"
(4)编写主启动类和逻辑代码
均与 3355一致 。
2、设计思想
实现广播共有两种设计思想:
1)利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
2)利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置
图二的架构显然更合适,图一不合适的原因:
1) 打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新的职责;
2)破坏了微服务各节点的对等性;
3)有一定的局限性。例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就会增加更多的修改
3、给配置中心服务端3344增加 消息总线支持
(1)pom增加消息总线RabbitMQ支持
<!--添加消息总线RabbitMQ支持--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
(2)application.yml增加相关配置
server: port: 3344 spring: application: name: cloud-config-center #注册进Eureka服务器的微服务名 cloud: config: server: git: uri: https://gitee.com/jade-faced-dragon/springcloud-config.git #Gitee上面的git仓库名字 ####搜索目录 search-paths: - springcloud-config ####读取分支 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: 'bus-refresh'
4、给客户端3355和3366增加消息总线支持
(1)pom增加消息总线RabbitMQ支持
<!--添加消息总线RabbitMQ支持--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
(2)bootstap.yml增加相关配置
server: port: 3366 spring: application: name: config-client cloud: #Config客户端配置 config: label: master #分支名称 name: config #配置文件名称 profile: dev #读取后缀名称 上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.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 # 暴露监控端点 management: endpoints: web: exposure: include: "*"
注意:bootstrap.yml里的端口号,3355和3366不同
5、启动7001、3344、3355、3366
http://config-3344.com:3344/config-dev.yml
http://localhost:3355/configInfo
http://localhost:3366/configInfo
看看此时版本号
6、模拟运维人员修改和刷新
修改Gitee上配置文件的版本号,模拟运维人员修改配置。
打开命令行工具,输入命令,模拟运维人员手动刷新3344配置中心服务端
curl -X POST "http://localhost:3344/actuator/bus-refresh"
7、再次查看
刷新3344、3355、3366 ,可以看到,版本号发生改变
我们没有重启3355和3366,也没有向他们发送刷新请求,但是他们已经可以刷新了。
这就是Bus总线的作用。
四、Bus动态刷新定点广播
我们已经使用Bus实现了全局广播通知。但是,以我们的刷新通知为例,有的时候我们并不想所有的客户端都刷新,只想让指定的客户端刷新,这该如何实现呢?
只需要将刷新的命令略作修改就行了
http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}
例子:假如我们只想通知3355,不想通知3366
curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"
这样就能实现只通知3355.
注意:config-client是3355的 spring.application.name