书接上文,虽然可以通过刷新端点来刷新配置,但是如果有几百个服务的话,一个一个手动刷新,同样会累死人的。所以我们可以使用rabbitMq来广播配置文件的更改。以让所有的服务都刷新配置。
架构图:
整个方案的架构如上图所示,其中包含了Git仓库、Config Server、以及微服务“Service A”的三个实例,这三个实例中都引入了Spring Cloud Bus,所以他们都连接到了RabbitMQ的消息总线上。
当我们将系统启动起来之后,“Service A”的三个实例会请求Config Server以获取配置信息,Config Server根据应用配置的规则从Git仓库中获取配置信息并返回。
此时,若我们需要修改“Service A”的属性。首先,通过Git管理工具去仓库中修改对应的属性值,但是这个修改并不会触发“Service A”实例的属性更新。我们向“Service A”的实例3发送POST请求,访问/bus/refresh
接口。此时,“Service A”的实例3就会将刷新请求发送到消息总线中,该消息事件会被“Service A”的实例1和实例2从总线中获取到,并重新从Config Server中获取他们的配置信息,从而实现配置信息的动态更新。
而从Git仓库中配置的修改到发起/bus/refresh
的POST请求这一步可以通过Git仓库的Web Hook来自动触发。由于所有连接到消息总线上的应用都会接受到更新请求,所以在Web Hook中就不需要维护所有节点内容来进行更新,从而解决了通过Web Hook来逐个进行刷新的问题。
上面的例子中,我们通过向服务实例请求Spring Cloud Bus的/bus/refresh
接口,从而触发总线上其他服务实例的/refresh
。但是有些特殊场景下(比如:灰度发布),我们希望可以刷新微服务中某个具体实例的配置。
Spring Cloud Bus对这种场景也有很好的支持:/bus/refresh
接口还提供了destination
参数,用来定位具体要刷新的应用程序。比如,我们可以请求/bus/refresh?destination=customers:9000
,此时总线上的各应用实例会根据destination
属性的值来判断是否为自己的实例名,若符合才进行配置刷新,若不符合就忽略该消息。
destination
参数除了可以定位具体的实例之外,还可以用来定位具体的服务。定位服务的原理是通过使用Spring的PathMatecher(路径匹配)来实现,比如:/bus/refresh?destination=customers:**
,该请求会触发customers
服务的所有实例进行刷新。
既然Spring Cloud Bus的/bus/refresh
接口提供了针对服务和实例进行配置更新的参数,那么我们的架构也相应的可以做出一些调整。在之前的架构中,服务的配置更新需要通过向具体服务中的某个实例发送请求,再触发对整个服务集群的配置更新。虽然能实现功能,但是这样的结果是,我们指定的应用实例就会不同于集群中的其他应用实例,这样会增加集群内部的复杂度,不利于将来的运维工作,比如:我们需要对服务实例进行迁移,那么我们不得不修改Web Hook中的配置等。所以我们要尽可能的让服务集群中的各个节点是对等的。
因此,我们将之前的架构做了一些调整,如下图所示:
我们主要做了这些改动:
/bus/refresh
请求不在发送到具体服务实例上,而是发送给Config Server,并通过destination
参数来指定需要更新配置的服务或实例。通过上面的改动,我们的服务实例就不需要再承担触发配置更新的职责。同时,对于Git的触发等配置都只需要针对Config Server即可,从而简化了集群上的一些维护工作。
改造我们的user-action和points-action服务:
pom里面添加
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-bus-amqp
application.yml里面添加
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
cloud:
bus:
enabled: true
trace:
enabled: true
management:
endpoints:
web:
exposure:
include: refresh,bus-refresh
启动RabbitMq,user-action和points-action服务
登录http://127.0.0.1:15672/ 我们发现多了两个连接,连个队列
此时我们修改git上的配置文件,将base-name修改为summer4。
我们用post请求http://localhost:8083/actuator/bus-refresh 注意一定要是bus-refresh接口
然后访问http://localhost:8083/getConfig,发现界面上显示summer4
再访问http://localhost:8082/getConfig,发现界面上显示summer4
这样就通过发送一个刷新请求,刷新所有微服务。
注意:
这是博主用血换来的教训
1. 如果报错spring cloud bus socket closed。注意账号密码和端口,不要用web端15672,要用内部端口5672。
2. 如果报错unexpected connection driver error occured,注意权限,rabbitmqctl set_permissions -p / admin ".*" ".*" “.*”,
3. 消息总线刷新务必要用/actuator/bus-refresh,/actuator/refresh只是单点刷新不通知总线。