我们在前面讲到,如果需要客户端获取到最新的配置信息需要执行 refresh,我们可以利用 Webhook 的机制每次提交代码发送请求来刷新客户端,当客户端越来越多的时候,需要每个客户端都执行一遍,这种方案就不太适合了。使用 Spring Cloud Bus 可以完美解决这一问题。
Spring Cloud Bus 通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其他的消息指令。Spring Bus 的一个核心思想是通过分布式的启动器对 Spring Boot 应用进行扩展,也可以用来建立一个多个应用之间的通信频道。目前唯一实现的方式是用 Amqp 消息代理作为通道,同样特性的设置(有些取决于通道的设置)在更多通道的文档中。
Spring Cloud Bus 被国内很多都翻译为消息总线。大家可以将它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了 MQ 的广播机制在分布式的系统中传播消息,目前常用的有 Kafka 和 RabbitMQ。利用 Bus 的机制可以做很多的事情,其中配置中心客户端刷新就是典型的应用场景之一,我们用一张图来描述 Bus 在配置中心使用的机制。
改造配置中心
本文消息队列使用RabbitMQ,改造config-server-8009,config-server-8010模块,添加依赖 spring-cloud-starter-bus-amqp
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--使用消息总线时加入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
两个配置文件添加的一样
server:
port: 8009
spring:
application:
name: config-server
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
cloud:
config:
server:
git:
uri: https://github.com/ROAOR1/config-server.git
username:
password:
search-paths: config #要搜索的路径,填我们刚刚创建的文件夹
bus:
enabled: true
trace:
enabled: true
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka/,http://localhost:8002/eureka/
management:
endpoints:
web:
exposure:
include: bus-refresh #暴露出bus-refresh接口
启动类还是和之前一样
@EnableEurekaClient
@EnableConfigServer
@SpringBootApplication
public class ConfigServer8009Application {
public static void main(String[] args) {
SpringApplication.run(ConfigServer8009Application.class, args);
}
}
至此,服务端改造完成
改造客户端
改造config-client-8011,config-client-8012模块,添加依赖 spring-cloud-starter-bus-amqp
<!--是用来接收更新的消息,类似心跳检测-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
配置分为两个文件,首先是application.yml
server:
port: 8012
spring:
application:
name: config-client2
cloud:
bus:
enabled: true
trace:
enabled: true
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
data: ${spring.profiles.active} #用这个来加载配置中心中的数据
bootstrap.yml
spring:
cloud:
config:
name: cloud-config #对应 {application}
label: master #对应{label}
profile: dev #对应{profile}
discovery:
enabled: true #开启 Config 服务发现支持
service-id: config-server #配置中心Id
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka/,http://localhost:8002/eureka/
management:
endpoints:
web:
exposure:
include: refresh #暴露了refresh接口
启动类和之前一样
@EnableEurekaClient
@SpringBootApplication
public class ConfigClient8012Application {
public static void main(String[] args) {
SpringApplication.run(ConfigClient8012Application.class, args);
}
}
测试类也和之前一样,不要忘了@RefreshScope注解
@RefreshScope
@RestController
public class ConfigController {
@Value("${data}")
private String data;
@RequestMapping("/getData")
public void getData(){
System.out.println(data);
}
}
至此,客户端改造完成
测试
接着我们启动本地RabbitMQ,启动程序,分别访问测试接口http://localhost:8011/getData,http://localhost:8012/getData,看到如下结果
修改github上的配置文件,将dev改成dev bus,接着我们访问服务端的刷新接口http://localhost:8009/actuator/bus-refresh,再次访问测试接口http://localhost:8011/getData,http://localhost:8012/getData,会发现数据改变
思考
这里我们再想一个问题:如果对客户端使用 /actuator/bus-refresh 会发生什么呢?是只刷新了当前客户端还是刷新全部客户端,还是一个都没刷新呢?不如来试一下吧:
首先我们需要把客户端上的 bus-refresh 端点给放出来
management:
endpoints:
web:
exposure:
include: refresh,bus-refresh #暴露了refresh接口,bus-refresh接口是在添加消息总线时暴露
重启客户端,再进行上面的测试,这次访问客户端的刷新接口http://localhost:8012/actuator/bus-refresh
会发现两个客户端结果都改变了。
说明只要开启 Spring Cloud Bus 后,不管是对 config-server 还是 config-client 执行 /actuator/bus-refresh 都是可以更新配置的。
最后我们就可以使用git的Webhook(上章有讲到),达到当修改配置文件时,通过消息总线自动刷新配置的目的。
参考
Spring Cloud(九):配置中心(消息总线)【Finchley 版】
相关阅读
项目代码
SpringCloud 汇总【Greenwich 版】
SpringCloud(一):Eureka注册中心【Greenwich 版】
SpringCloud(二):Ribbon负载均衡【Greenwich 版】
SpringCloud(三):Feign声明式服务调用【Greenwich 版】
SpringCloud(四):Hystrix熔断器介绍【Greenwich 版】
SpringCloud(五):Hystrix的请求熔断与服务降级【Greenwich 版】
SpringCloud(六):Hystrix的请求合并【Greenwich 版】
SpringCloud(七):Hystrix仪表盘与Turbine集群监控【Greenwich 版】
SpringCloud(八):Zuul网关【Greenwich 版】
SpringCloud(九):Config配置中心【Greenwich 版】
SpringCloud(十):Bus消息总线【Greenwich 版】
SpringCloud(十一):Stream消息驱动 + RabbitMQ【Greenwich 版】
SpringCloud(十二):Sleuth链路跟踪【Greenwich 版】