Spring Cloud(八):使用Spring Cloud Bus来实现配置动态更新

2023-11-15

使用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来实现服务配置动态更新的结构图如下

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接口,得到结果如下

springcloudbus-test1

springcloudbus-test2

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

springcloudbus-test3

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

springcloudbus-test4

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

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

springcloudbus-test5

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

springcloudbus-test6

使用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接口,

springcloudbus-test7

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

springcloudbus-test9

springcloudbus-test8

两个实例的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等的支持

springcloud-config-monitor

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

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

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

config-github-webhook

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.instanceid存在,使用vcap.application.instanceid,否则给一个随机值

我们可以在服务项目中打开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等系列技术文章公众号二维码

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • Junit:如何测试从属性文件读取属性的方法

    嗨 我有课ReadProperty其中有一个方法ReadPropertyFile返回类型的Myclass从属性文件读取参数值并返回Myclass目的 我需要帮助来测试ReadPropertyFile方法与JUnit 如果可能的话使用模拟文件
  • 使用 LinkedList 实现下一个和上一个按钮

    这可能是一个愚蠢的问题 但我很难思考清楚 我编写了一个使用 LinkedList 来移动加载的 MIDI 乐器的方法 我想制作一个下一个和一个上一个按钮 以便每次单击该按钮时都会遍历 LinkedList 如果我硬编码itr next or
  • 为 java 游戏创建交互式 GUI

    大家好 我正在创建一个类似于 java 中的 farmville 的游戏 我只是想知道如何实现用户通常单击以与游戏客户端交互的交互式对象 按钮 我不想使用 swing 库 通用 Windows 看起来像对象 我想为我的按钮导入自定义图像 并
  • 如何在 Spring 中禁用使用 @Component 注释创建 bean?

    我的项目中有一些用于重构逻辑的通用接口 它看起来大约是这样的 public interface RefactorAwareEntryPoint default boolean doRefactor if EventLogService wa
  • Java 集合的并集或交集

    建立并集或交集的最简单方法是什么Set在 Java 中 我见过这个简单问题的一些奇怪的解决方案 例如手动迭代这两个集合 最简单的单行解决方案是这样的 set1 addAll set2 Union set1 retainAll set2 In
  • java.lang.IllegalStateException:提交响应后无法调用 sendRedirect()

    这两天我一直在尝试找出问题所在 我在这里读到我应该在代码中添加一个返回 我做到了 但我仍然得到 java lang IllegalStateException Cannot call sendRedirect after the respo
  • 无法创建请求的服务[org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]-MySQL

    我是 Hibernate 的新手 我目前正在使用 Spring boot 框架并尝试通过 hibernate 创建数据库表 我知道以前也问过同样的问题 但我似乎无法根据我的环境找出如何修复错误 休眠配置文件
  • Eclipse Maven Spring 项目 - 错误

    I need help with an error which make me crazy I started to study Java EE and I am going through tutorial on youtube Ever
  • 帮助将图像从 Servlet 获取到 JSP 页面 [重复]

    这个问题在这里已经有答案了 我目前必须生成一个显示字符串文本的图像 我需要在 Servlet 上制作此图像 然后以某种方式将图像传递到 JSP 页面 以便它可以显示它 我试图避免保存图像 而是以某种方式将图像流式传输到 JSP 自从我开始寻
  • 内部类的构造函数引用在运行时失败并出现VerifyError

    我正在使用 lambda 为内部类构造函数创建供应商ctx gt new SpectatorSwitcher ctx IntelliJ建议我将其更改为SpectatorSwitcher new反而 SpectatorSwitcher 是我正
  • 如何在用户输入数据后重新运行java代码

    嘿 我有一个基本的java 应用程序 显示人们是成年人还是青少年等 我从java开始 在用户输入年龄和字符串后我找不到如何制作它它们被归类为 我希望它重新运行整个过程 以便其他人可以尝试 的节目 我一直在考虑做一个循环 但这对我来说没有用
  • 当 OnFocusChangeListener 应用于包装的 EditText 时,TextInputLayout 没有动画

    不能比标题说得更清楚了 我有一个由文本输入布局包裹的 EditText 我试图在 EditText 失去焦点时触发一个事件 但是 一旦应用了事件侦听器 TextInputLayout 就不再对文本进行动画处理 它只是位于 editText
  • 尝试将 Web 服务部署到 TomEE 时出现“找不到...的 appInfo”

    我有一个非常简单的项目 用于培训目的 它是一个 RESTful Web 服务 我使用 js css 和 html 创建了一个客户端 我正在尝试将该服务部署到 TomEE 这是我尝试部署时遇到的错误 我在这里做错了什么 刚刚遇到这个问题 我曾
  • java for windows 中的文件图标叠加

    我正在尝试像 Tortoise SVN 或 Dropbox 一样在文件和文件夹上实现图标叠加 我在网上查了很多资料 但没有找到Java的解决方案 Can anyone help me with this 很抱歉确认您的担忧 但这无法在 Ja
  • 使用 AsyncTask 传递值

    我一直在努力解决这个问题 但我已经到了不知道该怎么办的地步 我想做的是使用一个类下载文件并将其解析为字符串 然后将该字符串发送到另一个类来解析 JSON 内容 所有部件都可以单独工作 并且我已经单独测试了所有部件 我只是不知道如何将值发送到
  • 不接受任何内容也不返回任何内容的函数接口[重复]

    这个问题在这里已经有答案了 JDK中是否有一个标准的函数式接口 不接受也不返回任何内容 我找不到一个 像下面这样 FunctionalInterface interface Action void execute 可运行怎么样 Functi
  • 我如何在java中读取二进制数据文件

    因此 我正在为学校做一个项目 我需要读取二进制数据文件并使用它来生成角色的统计数据 例如力量和智慧 它的设置是让前 8 位组成一个统计数据 我想知道执行此操作的实际语法是什么 是不是就像读文本文件一样 这样 File file new Fi
  • 如何使用mockito模拟构建器

    我有一个建造者 class Builder private String name private String address public Builder setName String name this name name retur
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • 双枢轴快速排序和快速排序有什么区别?

    我以前从未见过双枢轴快速排序 是快速排序的升级版吗 双枢轴快速排序和快速排序有什么区别 我在 Java 文档中找到了这个 排序算法是双枢轴快速排序 作者 弗拉基米尔 雅罗斯拉夫斯基 乔恩 本特利和约书亚 布洛赫 这个算法 在许多数据集上提供

随机推荐

  • 字节和比特简单介绍

    字节 byte 字节为Byte 多数用B表示 字节为计算机中数据处理的基本单位 比特 bit 又称位 表示二进制位 为计算内部数据存储的最小单位 关系 1Byte 8bit 其他单位 1B Byte 字节 8bit 1KB Kilobyte
  • redis的数据类型string和list

    学习笔记记录 方便以后查看 一 Redis简介 1 关于关系型数据库和nosql数据库 关系型数据库是基于关系表的数据库 最终会将数据持久化到磁盘上 而nosql数据 库是基于特殊的结构 并将数据存储到内存的数据库 从性能上而言 nosql
  • 文献精讲-弱小目标检测-ADMD方法

    分享一下个人对于近期读过的一篇文献 Fast and robust small infrared target detection using absolute directional mean difference algorithm 的
  • Python打包的艺术(二)- Packaging Tools的昨天,今天,明天

    本文转载至 http blog chinaunix net uid 15174104 id 4036539 html 截至今年3月 Python的打包工具链处于的状态可以用 非常混乱 来形容 除了官方的distutils 第三方有setup
  • Python 包发布

    包结构 准备工作 twine 安装 pip3 install twine pypi 账号注册 https pypi org account register 注册成功后 记得到邮箱激活 否则无法使用 三个文件 setup py import
  • 【华为OD机试真题】MVP争夺战(python)100%通过率 超详细代码注释 代码解读

    华为OD机试真题 2022 2023 真题目录 点这里 华为OD机试真题 信号发射和接收 试读 点这里 华为OD机试真题 租车骑绿道 试读 点这里 MVP争夺战 知识点DFS搜索 时间限制 1s 空间限制 256MB 限定语言 不限 题目描
  • 【Java数据结构】Map和Set的使用

    章节目标 掌握Map Set及实际实现类 HashMap TreeMap HashSet TreeSet的使用 掌握 TreeMap 和 TreeSet 背后的数据结构搜索树的原理和简单实现 掌握 HashMap 和 HashSet 背后的
  • Ant Desgin Pro v6 正式版本企业级从零开始实现动态菜单与权限视频教程(28 个视频)

    Ant Desgin Pro v6 正式版本企业级从零开始实现动态菜单与权限视频教程 28 个视频 欢迎加入我们的Ant Design Pro v6视频教程 如果您正在寻找一门教您如何从零开始构建企业级动态菜单和权限管理的课程 那么这个视频
  • spring boot学习(十二)整合Druid

    Druid简介 Java程序很大一部分要操作数据库 为了提高性能操作数据库的时候 又不得不使用数据库连接池 Druid 是阿里巴巴开源平台上一个数据库连接池实现 结合了 C3P0 DBCP 等 DB 池的优点 同时加入了日志监控 Druid
  • linux上zookeeper单机搭建伪集群

    Zookeeper 一 下载zookeeper 解压到指定文件夹下 tar zxvf apache zookeeper 3 5 8 bin tar gz C home zk 改个名字 这个随意 mv apache zookeeper 3 5
  • OpenCV单目标跟踪:实现目标追踪的简易指南

    OpenCV单目标跟踪 实现目标追踪的简易指南 了解如何使用OpenCV库实现单目标跟踪 本指南介绍了OpenCV中主要的目标跟踪算法 包括CSRT KCF MIL GOTURN和DaSiamRPN 通过选择初始目标区域并使用适当的跟踪器
  • (2019.5.29)关于drawable你可能不知道的一些事

    转载公司内部论坛本人文章2019 5 29 导读 Android项目中 官方建议将不同分辨率的资源图片分别放在drawable mdpi drawable hdpi drawable xhdpi等不同的drawable文件夹下 以便不同分辨
  • 解决java.sql.SQLException: com.mysql.cj.jdbc.Driver报错

    当有 com alibaba druid support logging JakartaCommonsLoggingImpl error 严重 create connection error url java sql SQLExceptio
  • js 限制输入框不能为空_javascript如何验证不能为空?

    JavaScript中可以使用replace g 方法验证是否为空 replace g 可以将文本框开始与结尾的空格去除 然后使用 号判断是否为空 使用正则方法验证不能为空 正则表达式 replace g 整体为如果以一个或多个空格开始 替
  • VMwareWorkstationPro16的下载与安装,以及vm账号注册的问题

    VMwareWorkstationPro16的下载与安装 以及vm账号注册的问题 查看虚拟化支持是否开启 vm的安装 vm账号注册的常见问题 VM 16的安装步骤 查看虚拟化支持是否开启 可以从任务管理器中的性能去查看CPU是否开启虚拟化支
  • UNIX环境编程——信号量与互斥量对比

    本期主题 信号量与互斥量的对比 发现CSDN一篇文章的大小有限 所以新开了一篇讲这一部分 前面的内容在 UNIX环境编程 线程详解 往期地址 UNIX环境编程 文件IO UNIX环境编程 标准IO UNIX环境编程 进程环境与进程控制 文章
  • ubuntu RPM should not be used directly install RPM packages, use Alien instead!

    ubuntu RPM should not be used directly install RPM packages use Alien instead 所以我们最好下载deb版本的安装包 安装 参考文章
  • 关于相机与激光雷达数据采集与标定

    最近在做一个关于车路协同的项目 需要做一个路侧系统 传感器有摄像头和激光雷达 相机和激光雷达联合标定费了老半天劲 在此记录一下 雷达时间戳不对 导致摄像头和雷达的数据无法对齐 解决办法 修改雷达驱动发布点云消息时的时间戳 相机内参标定可以使
  • A* 算法

    简单描述一下A 原理 两个集合 OpenList 可到达的格子 CloseList 已到达的格子 一个公式 F G H 解释一下 F G H是格子的3个属性 G 从起点走到当前格子的成本 也就是已经花费了多少步 H 在不考虑障碍的情况下 从
  • Spring Cloud(八):使用Spring Cloud Bus来实现配置动态更新

    使用Spring Cloud Config我们能实现服务配置的集中化管理 在服务启动时从Config Server获取需要的配置属性 但如果在服务运行过程中 我们需要将某个配置属性进行修改 比如将验证码的失效时间从五分钟调整为十分钟 如何将