Spring Cloud Config配置信息自动更新原理解析

2024-06-17 14:44

本文主要是介绍Spring Cloud Config配置信息自动更新原理解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我们知道Spring Cloud Config是Spring Cloud提供的配置中心实现工具,我们可以通过它把配置信息存放在Git等第三方配置仓库中。每当Spring Cloud Config客户端启动时,就会发送HTTP请求到服务器端获取配置信息,这点比较好理解。但事实上,在Git中更改了配置信息之后,客户端并不会主动再次请求最新配置,而是使用缓存到本地的原有配置信息。


那么问题就来了,在这种情况下,Spring Cloud Config是如何能够实时获取更改后的配置呢?这就是今天我们要讨论的内容。通过理解Spring Cloud Config配置信息自动更新的执行过程,有助于我们深入把握框架的底层原理。

在对底层原理进行详细展开之前,我们先来给出Spring Cloud Config应对这一问题的具体做法。事实上,Spring Cloud Config能够做到配置信息的自动更新,是依赖于Spring Cloud中的另一个组件,即Spring Cloud Bus。

Spring Cloud Bus是Spring Cloud中用于实现消息总线的专用组件,集成了RabbitMQ、Kafka等主流消息中间件。当我们在Spring Cloud Config 服务器端代码工程的类路径中添加Spring Cloud Bus的引用并启动应用程序之后,Spring Boot Actuator就为我们提供了/actuator/bus-refresh端点,通过访问该端点就可以达到对客户端所有服务实例的配置信息进行自动更新的效果。在这种方案中,服务端会主动通知所有客户端进行配置信息的更新,这样我们就无需关注各个客户端,而只对服务端进行操作即可。

是不是听起来有点神奇?整个实现过程我们至少要搞清楚三大问题,如下图所示。


针对这三个问题,接下去我们将结合源码逐一展开讨论。

问题一:如何自动调用服务器端所暴露的/actuator/bus-refresh端点?

在现代软件开发过程中,开放式平台是一种常见的软件服务形态。我们可以把Spring Cloud Config Server所提供的HTTP端点视为一种开放式的接口,以供Git等第三方工具进行访问和集成。基于这种思想,我们可以把服务器端/actuator/bus-refresh端点对外进行暴露,然后第三方工具通过这个暴露的端点进行集成。例如,在Github中就设计了一种Webhook机制,并提供了用户界面供我们配置所需要集成的端点以及对应的操作,操作方法如下图所示。


我们可以在上图的Payload URL中设置/actuator/bus-refresh端点地址。所谓的Webhook,实际上就是一种回调。通过Webhook,当我们提交代码时,Github就会自动调用所配置的HTTP端点。也就是说,可以根据配置项信息的更新情况自动实现对/actuator/bus-refresh端点的访问。基于Github的配置仓库实现方案,我们可以得到如下图所示的系统结构图。


现在,配置信息一旦有更新,Spring Cloud Config Server就能从Github中获取最新的配置信息了。

问题二:客户端如何得知服务器端的配置信息已经更新?

接下来我们关注第二个问题,即客户端如何得知服务器端的配置信息已经更新?我们首先需要明确调用了/actuator/bus-refresh端点之后,系统内部会发生了么。这里我们快速浏览Spring Cloud Bus中的代码工程,发现存在一个RefreshBusEndpoint端点类,如下所示。

@Endpoint(id = "bus-refresh")

public class RefreshBusEndpoint extends AbstractBusEndpoint {

@WriteOperation

public void busRefreshWithDestination(@Selector String destination) {

//发布RefreshRemoteApplicationEvent事件

publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), destination));

}

@WriteOperation

public void busRefresh() {

//发布RefreshRemoteApplicationEvent事件

publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), null));

}

}

显然,RefreshBusEndpoint类对应于我们前面访问的/bus-refresh端点。可以看到,Spring Cloud Bus在这里做的事情仅仅只是发布了一个新的RefreshRemoteApplicationEvent事件。

既然发送了事件,我们就需要寻找该事件的监听者。我们在Spring Cloud Bus中找到了RefreshRemoteApplicationEvent事件的监听器RefreshListener,如下所示。

public class RefreshListener implements ApplicationListener<RefreshRemoteApplicationEvent> {

@Override

public void onApplicationEvent(RefreshRemoteApplicationEvent event) {

//执行配置属性的刷新操作

Set<String> keys = contextRefresher.refresh();

}

}

从类的定义中不难看出该监听器就是用来处理RefreshRemoteApplicationEvent事件。可以看到,在它的onApplicationEvent方法中同样也是调用了ContextRefresher中的refresh方法进行配置属性的刷新。

请注意,RefreshRemoteApplicationEvent是一个远程事件,将通过消息中间件进行发送,并被Spring Cloud Config客户端所监听,处理流程如下图所示。


问题三:客户端如何实时获取服务器端所更新的配置信息?

最后需要明确的第三个问题是,客户端如何获取服务器端所更新的配置信息,这就需要梳理Spring Cloud Config Server与注册中心之间的关系。

我们知道配置中心作为整个微服务架构运行所需的基础服务,需要确保其可用性。因为配置服务本身也是一个独立的微服务,所以Spring Cloud Config实现高可用的方式很简单。跟其他微服务一样,它把自己注册到注册中心上,让其他服务提供者或消费者通过注册中心进行服务发现和获取。


显然,在这种方式下,注册中心的服务治理机制同时提供了服务器端的负载均衡和客户端的配置功能,从而也就间接实现了高可用性。从另一个角度,我们也可以理解为可以通过注册中心获取所有Spring Cloud Config客户端服务的实例,从而在分布式环境下为获取配置信息提供了一种简便的手段。

Spring Cloud Config提供了一个工具类ConfigServerInstanceProvider来完成与注册中心之间的交互,如下所示。

public class ConfigServerInstanceProvider {

private final DiscoveryClient client;

  

@Retryable(interceptor = "configServerRetryInterceptor")

public List<ServiceInstance> getConfigServerInstances(String serviceId) {

List<ServiceInstance> instances = this.client.getInstances(serviceId);

if (instances.isEmpty()) {

//抛出异常

}

return instances;

}

}

在这里,我们看到了熟悉的DiscoveryClient,DiscoveryClient通过同样熟悉的getInstances方法从注册中心中获取Spring Cloud Config服务器实例,如下所示。

List<ServiceInstance> instances = this.client.getInstances(serviceId);

ConfigServerInstanceProvider的调用者是DiscoveryClientConfigServiceBootstrapConfiguration。我们来看这个Spring Boot自动配置类的定义,如下所示。

public class DiscoveryClientConfigServiceBootstrapConfiguration

implements SmartApplicationListener {

public void startup(ContextRefreshedEvent event) {

refresh();

}

}

可以看到,如果系统中生成了ContextRefreshedEvent事件就会触发如下所示的refresh方法。

private void refresh() {

try {

获取Spring Cloud Config客户端服务实例

String serviceId = this.config.getDiscovery().getServiceId();

List<String> listOfUrls = new ArrayList<>();

List<ServiceInstance> serviceInstances = this.instanceProvider.getConfigServerInstances(serviceId);

//遍历服务实例列表

for (int i = 0; i < serviceInstances.size(); i++) {

ServiceInstance server = serviceInstances.get(i);

String url = getHomePage(server);

  //获取配置路径

if (server.getMetadata().containsKey("configPath")) {

String path = server.getMetadata().get("configPath");

if (url.endsWith("/") && path.startsWith("/")) {

url = url.substring(0, url.length() - 1);

}

url = url + path;

}

  //填充配置路径

listOfUrls.add(url);

}

String[] uri = new String[listOfUrls.size()];

uri = listOfUrls.toArray(uri);

this.config.setUri(uri);

}

}

在上述refresh方法中,Spring Cloud Config首先会获取配置文件中配置项spring.cloud.config.discovery.serviceId所指定的服务实例id,然后根据serviceId从ConfigServerInstanceProvider中获取注册服务的实例对象集合serviceInstances,最后循环遍历serviceInstances来更新存储在内存中的配置属性值。

至此,我们通过解答三个问题,引出了Spring Cloud Config中实现配置信息自动更新的三个步骤,并基于框架内部一系列组件之间的交互过程剖析了底层实现原理。

总结

作为一个配置中心技术实现上的难点,今天的内容基于Spring Cloud Config框架剖析了实现配置信息自动更新的工作原理。我们抛出了三个与这个主题相关的核心问题,然后基于源码对这些问题都做了一一解答。

事实上,Spring Cloud Config作为Spring自研的配置中心框架,其内部大量使用了Spring现有的功能特性,比方说本讲中提到的Spring容器的事件发布和监听机制,又比方说Spring Boot Acuator中的端点机制以及Spring Cloud Bus所具备的消息通信总线机制。我们需要首先对Spring容器相关的知识体系有足够的了解,才能更好的理解Spring Cloud Config的设计和实现方式。

这篇关于Spring Cloud Config配置信息自动更新原理解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1069734

相关文章

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

mybatis映射器配置小结

《mybatis映射器配置小结》本文详解MyBatis映射器配置,重点讲解字段映射的三种解决方案(别名、自动驼峰映射、resultMap),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定... 目录select中字段的映射问题使用SQL语句中的别名功能使用mapUnderscoreToCame

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo