Spring Cloud Bus 整合 RabbitMQ

结合RabbitMQ,可以实现配置文件动态刷新。我们先来看下面一张架构图:

图片

整张图的工作流程我们之前也详细的说过,当我的微服务A/微服务B启动的时候,会从Config-Server中加载配置文件,而Config-Server则会通过git clone命令将配置中心的配置文件先clone下来在本地保存一份,然后再返回给微服务A/微服务B。
这是我们之前的工作流程,现在我们结合Spring Cloud Bus来实现配置文件的动态更新。使用Spring Cloud Bus来实现配置文件的动态更新原理很简单,如上图,当我的配置文件更新后,我向Config-Server中发送一个/bus/refresh请求,Config-Server收到这个请求之后,会将这个请求广播出去,这样所有的微服务就都收到这个请求了,微服务收到这个请求之后就会自动去更新自己的配置文件。在这个系统中,从RabbitMQ的角度来看,所有的微服务都是一样的,所以这个/bus/refresh请求我们也可以在微服务节点上发出,一样能够实现配置文件动态更新的效果,但是这样做就破坏了我们微服务的结构,使得微服务节点之间有了区别,所以刷新配置的请求我们还是放在Config-Server上来做比较合适,好了,下面我们就来看看如何实现这一需求。

当我的微服务需要注册到eureka注册中心时,我需要给它添加spring-cloud-starter-eureka依赖,而当我的微服务需要使用Spring Cloud Bus时,我就给它添加spring-cloud-starter-bus-amqp依赖,我们在之前的Config-Server和Config-Client上都添加如下依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

OK,添加好依赖之后,由于我们的Config-Server一会要连接到RabbitMQ服务器上,所以在Config-Server的application.properties中做如下配置:

1
2
3
4
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=sang
spring.rabbitmq.password=123456

配置好之后,分别启动我们的Eureka注册中心、Config-Server和Config-Client(Config-Client启动两个实例,方便我们测试),我们在Config-Server和Config-Client的启动日志中都可以看到如下内容:

Mapped “{[/bus/refresh],methods=[POST]}” onto public void org.springframework.cloud.bus.endpoint.RefreshBusEndpo
int.refresh(java.lang.String)

这个接口就是在我们添加了spring-cloud-starter-bus-amqp依赖之后具备的,Config-Server和Config-Client都具备这个接口。

此时我们访问http://localhost:2008/sang,我们看到的内容如下:

图片

然后我们通过git工具,修改配置仓库中文件的内容,修改之后提交,提交之后如果我们直接访问http://localhost:2008/sang,内容还是不变,此时我们可以利用POSTMAN或者RestClient发送一个POST请求到http://localhost:2007/bus/refresh地址,这个时候Config-Server收到通知之后,就会广播配置文件改变的消息,此时再访问http://localhost:2008/sang和http://localhost:2009/sang,就能看到改变后的配置文件了:

图片

此时我们发现两个实例http://localhost:2008/sang和http://localhost:2009/sang都改变了,如果我们我们只想通知其中一个微服务更新配置,那么在刷新请求的时候携带上一个参数就行了,比如当我的配置文件更新后,我只想端口号为2008的实例更新配置,端口号为2009的实例不更新配置,那么在配置文件修改之后,我只需发送如下请求即可:```http://localhost:2007/bus/refresh?destination=configClient:2008```,destination后面指定要更新的实例的instance-id,注意我这里的config-client的如下两条配置:

1
2
spring.application.name=configClient
eureka.instance.instance-id=${spring.application.name}:${server.port}

每一个实例的instance-id是{server.port}共同组成。