-
config-repo:定义在Git仓库中的一个目录,其中存储了应用名为didispace的多环境配置文件,配置文件中有一个from参数。
-
config-server-eureka:配置了Git仓库,并注册到了Eureka的服务端。
-
config-client-eureka:通过Eureka发现Config Server的客户端,应用名为didispace,用来访问配置服务器以获取配置信息。该应用中提供了一个
/from
接口,它会获取config-repo/didispace-dev.properties
中的from属性返回。 -
扩展config-client-eureka应用
-
修改
pom.xml
增加spring-cloud-starter-bus-amqp
模块(注意spring-boot-starter-actuator
模块也是必须的)。
org.springframework.cloud
spring-cloud-starter-bus-amqp
- 在配置文件中增加关于RabbitMQ的连接和用户信息
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=springcloud
spring.rabbitmq.password=123456
- 启动config-server-eureka,再启动两个config-client-eureka(分别在不同的端口上,比如7002、7003),我们可以在config-client-eureka中的控制台中看到如下内容,在启动时候,客户端程序多了一个
/bus/refresh
请求。
o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped “{[/bus/refresh],methods=[POST]}” onto public void org.springframework.cloud.bus.endpoint.RefreshBusEndpoint.refresh(java.lang.String)
-
先访问两个config-client-eureka的
/from
请求,会返回当前config-repo/didispace-dev.properties
中的from属性。 -
接着,我们修改
config-repo/didispace-dev.properties
中的from属性值,并发送POST请求到其中的一个/bus/refresh
。 -
最后,我们再分别访问启动的两个config-client-eureka的
/from
请求,此时这两个请求都会返回最新的config-repo/didispace-dev.properties
中的from属性。
到这里,我们已经能够通过Spring Cloud Bus来实时更新总线上的属性配置了。
原理分析
我们通过使用Spring Cloud Bus与Spring Cloud Config的整合,并以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的PathMatecher(路径匹配)来实现,比如:/bus/refresh?destination=customers:**
,该请求会触发customers
服务的所有实例进行刷新。