使用Spring Cloud Config我们能实现服务配置的集中化管理,在服务启动时从Config Server获取需要的配置属性。但如果在服务运行过程中,我们需要将某个配置属性进行修改,比如将验证码的失效时间从五分钟调整为十分钟,如何将这个更新在服务端不重启服务就能动态生效,是本文讨论的内容。

Spring Cloud Bus

Spring Cloud Bus可以理解为Spring Cloud体系架构中的消息总线,通过一个轻量级的Message Broker来将分布式系统中的节点连接起来。可用来实现广播状态更新(如配置更新),或其它管理指令。

Spring Cloud Bus 就像是一个分布式的Spring Boot Actuator, 目前提供了两种类型的消息队列中间件支持:RabbitMQ与Kafka(对应的pom依赖分别为spring-cloud-starter-bus-amqp, spring-cloud-starter-bus-kafka)。

Spring Cloud 在spring-cloud-context中添加了两个actuator管理接口(POST请求): /actuator/env/actuator/refresh, 前者可用于更新当前服务实例Environment对象中的配置属性,后者可用于刷新当前服务实例的配置信息。

Spring Cloud Bus也提供了两个对应的接口

  1. /actuator/bus-env,相对于/actuator/env , 使用键值对更新每个实例的Environment,默认不暴露,需配置management.endpoints.web.exposure.include=bus-env 来开放接口访问
  2. /actuator/bus-refresh,相对于/actuator/refresh,对每个实例,清空RefreshScope缓存,重新绑定@ConfigurationProperties, 默认不暴露,可通过配置

    management.endpoints.web.exposure.include=bus-refresh 来开放接口访问

综上,/actuator/env/actuator/refresh 是针对单个服务实例修改或刷新其配置信息,而 /actuator/bus-env/actuator/bus-refresh 则是借助于Spring Cloud Bus的消息机制作用于分布式系统中的所有服务实例,因此前面有Spring Cloud Bus 就像是一个分布式的Spring Boot Actuator的说法。

使用Spring Cloud Bus来实现服务配置动态更新的结构图如下

  1. 更新配置仓库中的配置文件,push到远程Git仓库
  2. 远程Git仓库通过Webhook调用配置服务器的通知更新接口
  3. 配置服务器发送配置更新消息到消息总线
  4. 其它服务节点监听到配置服务器发送的配置更新消息
  5. 其它服务节点向配置服务器发送拉取最新配置的请求
  6. 配置服务器向配置仓库拉取最新的配置返回给其它服务节点

案例演示

我们还是以前面的springcloud-config, springcloud-eureka, springcloud-eureka-client三个项目来完成本文的案例演示。源码地址

使用Actuator

在不引入Spring Cloud Bus的情况下,我们可以通过Spring Cloud提供的actuator接口来实现单个实例的配置动态更新。

依次启动springcloud-eureka, springcloud-config, springcloud-eureka-client项目,然后修改springcloud-eureka-client的启动端口,将8080改为8081,再启动一个springcloud-eureka-client的服务实例。

springcloud-eureka-client 的测试接口代码如下

@RestController
@RefreshScope
public class HelloController { @Autowired
private Environment env; @Value("${app}")
private String app; @RequestMapping("/hello")
public String hello(){
return "Hello, welcome to spring cloud 2. env: " + env.getProperty("app") + ", value: " + app;
}
}

此时依次请求两个实例的hello接口,得到结果如下

我们通过/actuator/env接口来修改端口8080实例的属性app的值,使用postman操作如图

此时再请求接口返回结果如下

可以看到Environment对象中app属性的值已更新,但是 @Value注解的属性值未变,可见 /actuator/env 接口只是更新了Environment对象,并不负责刷新其它方式引用的属性值。此时请求另一个端口为8081的实例接口,其属性值都未更新,也可见 /actuator/env 只作用于当前实例本身。

如果要让8080实例的@Value属性也动态更新,则可再调用/actuator/refresh接口,如图

此时再请求测试接口,得到结果如下(@Value注解的属性也已经更新了)

使用Spring Cloud Bus

前面我们使用 /actuator/env/actuator/refresh 两个接口可以实现单个服务实例配置的动态更新,但在微服务架构中,服务实例可能达几十甚至几百个,一个个调用来做动态更新就有点太不方便了。这时就该Spring Cloud Bus登场了。

1.添加依赖与配置

在springcloud-config, 与springcloud-eureka-client两个项目中,添加spring cloud bus的依赖与配置。

在pom.xml文件中添加依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

在application.yml配置文件中添加RabbitMQ的相关配置

spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: rabbitmq
password: passw0rd

2.依次启动springcloud-eureka, springcloud-config, springcloud-eureka-client项目,并以8081端口再启动一个springcloud-eureka-client的服务实例。

3.我们使用postman对配置服务器调用/actuator/bus-env接口,

请求两个服务实例的测试接口,得到结果

两个实例的Environment对象都已经更新,如果要将@Value注解的属性也更新,则可再调用配置服务器的/actuator/bus-refresh接口。

/actuator/bus-env接口是直接更新的内存Environment实例属性,如果服务重启,则又还原到之前的配置了, 所以还是需要借助配置仓库来永久更新。配置更新后还需要手动调用接口使其生效?DevOps时代了,能自动化的就自动化吧,我们可以借助Git的webhook机制来实现自动化。

自动化

本文开头的“使用Spring Cloud Bus来实现服务配置动态更新的结构图”已经示例了使用Git仓库的webhook来触发自动更新配置的流程。但是在Git(如Github)中,我们不能直接使用/actuator/bus-refresh接口来作为webhook(因为接口协议不一致,会出现解析异常),也有人通过提供自己的接口来作为webhook,在自己接口中再转发请求到/actuator/bus-refresh来实现。但实际上,spring-cloud-config-monitor已经提供了对Git webhook的支持。

如下图,spring-cloud-config-monitor提供了对Github,Gitlab,Gitee,BitBucket等的支持

1.在配置服务器springcloud-config的pom.xml文件中添加依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-monitor</artifactId>
</dependency>

2.在配置仓库的设置页面配置webhook,比如Github的配置如图

Payload URL 配置为配置服务器的monitor接口地址,path参数必须。如果你的配置服务器在内网,比如做本地测试时,还需要实现一下内网穿透(如frp)。

在配置仓库项目中修改配置属性,提交代码,Github webhook就会触发自动更新,上图下方红色框为触发自动更新的记录。

自动更新配置未生效排查

如果出现Github触发了自动更新,但服务的配置更新未生效的情况,则需要查看webhook的匹配规则与服务实例的ServiceID是否匹配,webhook的匹配规则为 spring.application.name:spring.cloud.config.profile:**,服务实例的ServiceID可通过spring.cloud.bus.id配置,如果没有配置,则默认为

${vcap.application.name:${spring.application.name:application}}:${vcap.application.instance_index:${spring.application.index:${local.server.port:${server.port:0}}}}:${vcap.application.instance_id:${random.value}}

遵循app:index:id的格式,

  • app:如果vcap.application.name存在,使用vcap.application.name,否则使用spring.application.name,默认值为application
  • index:优先使用vcap.application.instance_index,如果不存在则依次使用spring.application.index、local.server.port、server.port, 默认值为0
  • id:如果vcap.application.instance_id存在,使用vcap.application.instance_id,否则给一个随机值

我们可以在服务项目中打开spring cloud bus的debug日志

logging:
level:
org.springframework.cloud.bus: debug

通过DefaultBusPathMatcher的debug日志来查看是否匹配,如

DEBUG 286196 --- [7O8XC9KNWbyDA-1] o.s.cloud.bus.DefaultBusPathMatcher      : In match: hello-service:8081:c96f04c81dfce6dffaa9d116811d127c, hello-service:8081:c96f04c81dfce6dffaa9d116811d127c

如果没有匹配则可以按照webhook的匹配规则设置spring.cloud.bus.id值或vcap.application.instance_index值,如

spring:
application:
name: hello-service
cloud:
config:
discovery:
service-id: config-server
enabled: true
profile: ${spring.profiles.active:default}
bus:
id: ${spring.application.name}:${spring.cloud.config.profile}:${random.value}
#或
vcap:
application:
instance_index: ${spring.cloud.config.profile}

配置更新未生效的另一个情况是查看是否用了@RefreshScope注解。

@RefreshScope

细心的人会发现本文开头的测试接口类上加了@RefreshScope注解。 @RefreshScope是Spring Cloud提供的用来实现配置、实例热加载的注解。被@RefreshScope修饰的@Bean都是延迟加载的,即在第一次访问(调用方法)时才会被初始化,并且这些bean存于缓存中。当收到配置更新的消息时,缓存中的@RefreshScope bean会被清除,这样下次访问时将会重新创建bean,此时使用的就是最新的配置信息,从而实现配置的热加载。

总结

本文分别示例了使用spring boot actuator与spring cloud bus来实现服务配置的更新及两者之间的区别, spring cloud bus一定程度上像是一个分布式的spring boot actuator。同时演示了使用webhook与spring cloud bus,monitor结合来实现配置自动更新的具体流程及可能遇到的问题。


认真生活,快乐分享!如果你觉得文章对你有帮助,欢迎分享转发!

欢迎关注微信公众号:空山新雨的技术空间

获取Spring Boot,Spring Cloud,Docker等系列技术文章

Spring Cloud(八):使用Spring Cloud Bus来实现配置动态更新的更多相关文章

  1. Spring点滴八:Spring注入集合

    在Spring中我们通过value属性来配置基本数据类型,通过标签的ref属性来配置对象的引用.这两种情况只能给bean传递一个值,那么如何传递多个值呢?Spring提供了四种Collection类型 ...

  2. spring boot / cloud (八) 使用RestTemplate来构建远程调用服务

    spring boot / cloud (八) 使用RestTemplate来构建远程调用服务 前言 上周因家里突发急事,请假一周,故博客没有正常更新 RestTemplate介绍: RestTemp ...

  3. spring cloud 使用spring cloud bus自动刷新配置

    Spring Cloud Bus提供了批量刷新配置的机制,它使用轻量级的消息代理(例如RabbitMQ.Kafka等)连接分布式系统的节点,这样就可以通过Spring Cloud Bus广播配置的变化 ...

  4. 【Spring Cloud】Spring Cloud之整合Spring Cloud Bus以及最佳实践

    一.整合步骤 1)加入Maven坐标 <!-- actuator监控模块 --> <dependency> <groupId>org.springframework ...

  5. Spring Cloud(十一)高可用的分布式配置中心 Spring Cloud Bus 消息总线集成(RabbitMQ)

    详见:https://www.w3cschool.cn/spring_cloud/spring_cloud-jl8a2ixp.html 上一篇文章,留了一个悬念,Config Client 实现配置的 ...

  6. 【八】Spring Cloud Config

    一.分布式系统面临的--配置问题 微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的力度相对较小,因此系统中会出现大量的服务.由于每个服务都需要必要的配置信息才能运行,所以一套集中式的.动 ...

  7. spring cloud微服务快速教程之(八) Spring Cloud Alibaba--nacos(二)、配置中心

    0-前言 上一篇我们介绍了nacos作为服务注册发现组件的功能,nacos还具有配置中心的功能,而且支持热加载: 在此之前,配置中心有Spring Cloud Config,实际上,用这个有很多风险和 ...

  8. Spring Cloud Config(一):聊聊分布式配置中心 Spring Cloud Config

    目录 Spring Cloud Config(一):聊聊分布式配置中心 Spring Cloud Config Spring Cloud Config(二):基于Git搭建配置中心 Spring Cl ...

  9. Spring顶级项目以及Spring cloud组件

    作为java的屌丝,基本上跟上spring屌丝的步伐,也就跟上了主流技术. spring 顶级项目: Spring IO platform:用于系统部署,是可集成的,构建现代化应用的版本平台,具体来说 ...

随机推荐

  1. Pandas中merge和join的区别

    可以说merge包含了join的操作,merge支持通过列或索引连表,而join只支持通过索引连表,只是简化了merge的索引连表的参数 示例 定义一个left的DataFrame left=pd.D ...

  2. 超越队西柚考勤系统——beta冲刺3

    一.成员列表 姓名 学号 蔡玉蓝(组长) 201731024205 郑雪 201731024207 何玉姣 201731024209 王春兰 201731024211 二.SCRUM部分 (1)各成员 ...

  3. Java程序员学习Go语言—之一

    转载:https://www.luozhiyun.com/archives/206 GOPATH 工作空间 GOPATH简单理解成Go语言的工作目录,它的值是一个目录的路径,也可以是多个目录路径,每个 ...

  4. Java中的Swap,如何实现?

    程序员都知道,在C/C++里面交换值的方法: void swap(int &a,int &b) { int temp; temp=a; a=b; b=temp; } 但是在Java中这 ...

  5. 自学前端开发,现在手握大厂offer,我的故事还在继续

    简要背景 我是一个非科班出身的程序员,而且是连续跨专业者,用一句话总结就是:16 届本科学完物流,保送研究生转交通,自学前端开发的休学创业者. 17 年休学创业,正式开始学习前端,离开创业公司后,我又 ...

  6. chrome浏览器无法开启同步功能 request cancel

    解决办法 添加代理规则*.googleapis.com

  7. JDK源码之String类解析

    一 概述 String由final修饰,是不可变类,即String对象也是不可变对象.这意味着当修改一个String对象的内容时,JVM不会改变原来的对象,而是生成一个新的String对象 主要考虑以 ...

  8. 机器学习:没有免费午餐定理(No Free Lunch Theorem)

    思考 机器学习中哪个算法好?哪个算法差呢? 下面两条线,哪个更好呢? 没有免费午餐定理 如果我们不对特征空间有先验假设,则所有算法的平均表现是一样的. 假设我们的计算机只有两个存储单元,而且每个存储单 ...

  9. 聊聊SpringBoot | 第一章:快速搭建SpringBoot第一个应用

    快速搭建SpringBoot第一个应用 1.简介 本章仅介绍如何快速搭建第一个SpringBoot应用,细节内容下一章再做讲解,如果有需要,各位可以直接到Spring官网去了解. 从 Spring B ...

  10. 【干货】零基础30分钟让你拥有一个完整属于自己的短视频APP系统

      目录 一.附言: 1 二.购买域名和购买服务器: 2 三.搭建服务器环境: 5 四.配置APP前端部分: 8 1.工具以及文件准备: 9 2.配置后端接口地址 11 3.配置APP启动图和启动图标 ...