当前位置:首页 > 科技  > 软件

8000字+22张图探秘SpringCloud配置中心的核心原理

来源: 责编: 时间:2023-10-17 09:38:54 339观看
导读大家好,我是三友~~这篇文章来扒一扒SpringCloud配置中心的核心原理。不知你是否跟我一样,在刚开始使用SpringCloud配置中心的时候也有很多的疑惑:SpringCloud是什么时候去拉取配置中心的?配置中心客户端的配置信息为什么

大家好,我是三友~~CPM28资讯网——每日最新资讯28at.com

这篇文章来扒一扒SpringCloud配置中心的核心原理。CPM28资讯网——每日最新资讯28at.com

不知你是否跟我一样,在刚开始使用SpringCloud配置中心的时候也有很多的疑惑:CPM28资讯网——每日最新资讯28at.com

  • SpringCloud是什么时候去拉取配置中心的?
  • 配置中心客户端的配置信息为什么要写在bootstrap文件中?
  • 对象中注入的属性是如何动态刷新的?
  • 一些开源的配置中心是如何整合SpringCloud的?
  • ...

本文就通过探讨上述问题来探秘SpringCloud配置中心核心的底层原理。CPM28资讯网——每日最新资讯28at.com

从SpringBoot的启动过程说起

在SpringBoot启动的时候会经历一系列步骤,核心就是SpringApplication的run方法的逻辑CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

整个过程大致可以划分为三个阶段:CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

ApplicationContext刷新前阶段,这个阶段主要也干三件事CPM28资讯网——每日最新资讯28at.com

  • 准备Environment(注意我这里加粗了,你懂得),也就是准备SpringBoot的整个外部化配置的对象
  • 创建一个ApplicationContext
  • 为ApplicationContext做一些准备工作

图片图片CPM28资讯网——每日最新资讯28at.com

ApplicationContext刷新阶段,这个阶段其实就是调用ApplicationContext#refresh方法来刷新容器CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

刷新的整个过程可以看我之前写的万字+20张图剖析Spring启动时12个核心步骤这篇文章CPM28资讯网——每日最新资讯28at.com

ApplicationContext刷新后阶段,这个阶段其实就是收尾的阶段,这个过程其实没有什么非常核心的事CPM28资讯网——每日最新资讯28at.com

ok,在说完上面这三个阶段之后,思考一个问题CPM28资讯网——每日最新资讯28at.com

你觉得在上面的三个阶段,哪个阶段最有可能从配置中心拉取配置?CPM28资讯网——每日最新资讯28at.com

其实稍微思考一下,肯定是想到的就是刷新前阶段CPM28资讯网——每日最新资讯28at.com

因为我已经明示了,准备EnvironmentCPM28资讯网——每日最新资讯28at.com

玩笑归玩笑,为什么是这个阶段?CPM28资讯网——每日最新资讯28at.com

很好理解,因为这个阶段是准备Environment,也就是准备外部化配置CPM28资讯网——每日最新资讯28at.com

只需要在这个阶段加载配置中心的配置,放到Environment中,后面在整个ApplicationContext刷新阶段创建Bean的时候,就可以使用到配置中心的配置了CPM28资讯网——每日最新资讯28at.com

其实不光是配置中心的配置,比如配置文件的配置,也是在这里阶段读取的CPM28资讯网——每日最新资讯28at.com

至于如何实现的,我们接着往下瞅CPM28资讯网——每日最新资讯28at.com

准备Environment的核心操作

上一节得出一个结论CPM28资讯网——每日最新资讯28at.com

准备Environment,也就是prepareEnvironment方法的实现,是拉取配置的核心CPM28资讯网——每日最新资讯28at.com

prepareEnvironment方法prepareEnvironment方法CPM28资讯网——每日最新资讯28at.com

不过在说这个方法之前,先来讲一下一些前置操作CPM28资讯网——每日最新资讯28at.com

前置操作

在SpringApplication创建的时候,会去加载spring.factories中的一些对象,其中就包括:CPM28资讯网——每日最新资讯28at.com

  • org.springframework.context.ApplicationListener键对应的ApplicationListener的实现

图片CPM28资讯网——每日最新资讯28at.com

  • org.springframework.boot.SpringApplicationRunListener键对应的SpringApplicationRunListener的实现类

图片CPM28资讯网——每日最新资讯28at.com

SpringApplicationRunListener仅仅只有一个实现EventPublishingRunListenerCPM28资讯网——每日最新资讯28at.com

EventPublishingRunListener

构造的时候会创建一个SimpleApplicationEventMulticaster,再将加载的ApplicationListener添加进去CPM28资讯网——每日最新资讯28at.com

SimpleApplicationEventMulticaster是用来发布事件用的,不清楚的话可以看三万字盘点Spring 9大核心基础功能这篇文章CPM28资讯网——每日最新资讯28at.com

按照传统,画张图来理一下这部分前置操作CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

prepareEnvironment的核心逻辑

接着来讲一下prepareEnvironment方法CPM28资讯网——每日最新资讯28at.com

prepareEnvironment方法prepareEnvironment方法CPM28资讯网——每日最新资讯28at.com

这个方法会首先创建一个Environment对象CPM28资讯网——每日最新资讯28at.com

之后会执行这么一行方法,传入刚刚创建的Environment对象CPM28资讯网——每日最新资讯28at.com

listeners.environmentPrepared(environment);

这个方法最终会走到这个方法

EventPublishingRunListener#environmentPrepared

图片图片CPM28资讯网——每日最新资讯28at.com

这个方法最终会发布一个ApplicationEnvironmentPreparedEvent事件CPM28资讯网——每日最新资讯28at.com

而对这个事件有两个特别重要的监听器:CPM28资讯网——每日最新资讯28at.com

  • ConfigFileApplicationListener
  • BootstrapApplicationListener

这些监听器都是通过前置操作从spring.factories配置文件中加载的CPM28资讯网——每日最新资讯28at.com

ConfigFileApplicationListener,用来处理配置文件的,他会解析配置文件的配置,放到Environment中CPM28资讯网——每日最新资讯28at.com

BootstrapApplicationListener这个跟本文探讨的主题相关了,它是用来专门来跟配置中心交互的CPM28资讯网——每日最新资讯28at.com

到这,我们就找到了SpringCloud配置中心配置拉取的整个入口逻辑CPM28资讯网——每日最新资讯28at.com

不过在分析BootstrapApplicationListener是如何从配置中心拉取配置的之前,先来张图总结一下这部分prepareEnvironment的操作CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

SpringCloud是如何巧妙地拉取配置的?

在BootstrapApplicationListener中,他首先也会创建一个SpringApplication去执行CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

其实本质上就是创建一个Spring容器,也就是ApplicationContextCPM28资讯网——每日最新资讯28at.com

这个容器非常重要,这个容器是专门用来跟配置中心交互的CPM28资讯网——每日最新资讯28at.com

这个容器在创建的时候会给它两个比较重要的配置CPM28资讯网——每日最新资讯28at.com

第一个就是设置这个容器所用的配置文件的名称CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

默认就是bootstrapCPM28资讯网——每日最新资讯28at.com

这就解释了为什么配置中心的配置信息需要写在bootstrap配置文件中CPM28资讯网——每日最新资讯28at.com

第二个就是会加入一个配置类CPM28资讯网——每日最新资讯28at.com

BootstrapImportSelectorConfiguration

图片图片CPM28资讯网——每日最新资讯28at.com

这个配置类又会通过@Import注解导入另一个配置类CPM28资讯网——每日最新资讯28at.com

BootstrapImportSelector

图片图片CPM28资讯网——每日最新资讯28at.com

BootstrapImportSelector实现了(间接)ImportSelector接口CPM28资讯网——每日最新资讯28at.com

那么这个容器在启动的时候,就会调用BootstrapImportSelector的selectImports方法的实现获取到一些配置类CPM28资讯网——每日最新资讯28at.com

而BootstrapImportSelector的selectImports实现从截图中也就可以看出CPM28资讯网——每日最新资讯28at.com

他会加载所有的spring.factories中的键为org.springframework.cloud.bootstrap.BootstrapConfiguration的配置类CPM28资讯网——每日最新资讯28at.com

其实这里@BootstrapConfiguration的作用其实跟@EnableAutoConfiguration的作用是差不多的,都是用来导入配置类的CPM28资讯网——每日最新资讯28at.com

所以,总的来说,这个用来跟配置中心交互的Spring容器最最主要就是干两件事:CPM28资讯网——每日最新资讯28at.com

  • 加载bootstrap配置文件
  • 加载所有的spring.factories中的键为org.springframework.cloud.bootstrap.BootstrapConfiguration对应的配置类

图片图片CPM28资讯网——每日最新资讯28at.com

而在spring-cloud-context包下,@BootstrapConfiguration会导入一个很重要的配置类CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

PropertySourceBootstrapConfiguration

图片图片CPM28资讯网——每日最新资讯28at.com

PropertySourceBootstrapConfiguration

这个配置类中会注入这么一个集合对象CPM28资讯网——每日最新资讯28at.com

PropertySourceLocator

PropertySourceLocatorPropertySourceLocatorCPM28资讯网——每日最新资讯28at.com

这个接口非常非常重要,先来看看注释CPM28资讯网——每日最新资讯28at.com

Strategy for locating (possibly remote) property sources for the Environment. Implementations should not fail unless they intend to prevent the application from starting.CPM28资讯网——每日最新资讯28at.com

我用我的四级英语功力给大家翻译一下CPM28资讯网——每日最新资讯28at.com

以一种策略的方式为Environment定位(可能是远程)属性配置(PropertySource)。实现不应该失败,除非打算阻止应用程序启动。CPM28资讯网——每日最新资讯28at.com

从这个翻译后的意思就是说,这个接口是用来定位,也就是说获取属性配置的CPM28资讯网——每日最新资讯28at.com

并且可能是远程告诉我们一个很重要的信息,那就是获取的配置信息不仅仅可以存在本地,而且还可以存在远程。CPM28资讯网——每日最新资讯28at.com

远程?作者这里就差直接告诉你可以从配置中心获取了。。CPM28资讯网——每日最新资讯28at.com

所以这个接口的作用就是用配置中心获取配置的!CPM28资讯网——每日最新资讯28at.com

那么自然而然不同的配置中心要想整合到SpringCloud就得实现这个接口CPM28资讯网——每日最新资讯28at.com

当注入完PropertySourceLocator集合之后,在某个阶段会调用所有的PropertySourceLocator,获取配置中心中的配置图片CPM28资讯网——每日最新资讯28at.com

之后在把这些配置放到Environment中CPM28资讯网——每日最新资讯28at.com

这样在ApplicationContext的刷新阶段就可以使用到配置中心的那些配置了CPM28资讯网——每日最新资讯28at.com

小总结

到这我们就弄明白了在项目启动中加载配置中心的配置了CPM28资讯网——每日最新资讯28at.com

其实就是项目在启动时会额外创建一个跟配置中心相关的Spring容器CPM28资讯网——每日最新资讯28at.com

这个容器会去加载bootstrap配置文件和所有的spring.factories中的键为org.springframework.cloud.bootstrap.BootstrapConfiguration对应的配置类CPM28资讯网——每日最新资讯28at.com

之后会去调用这个容器中所有的PropertySourceLocator对象,从配置中心获取配置CPM28资讯网——每日最新资讯28at.com

再放到Environment中就完成了启动时从配置中心获取配置的方式CPM28资讯网——每日最新资讯28at.com

最后,来张全家福概括一下前面整体的步骤CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

如何动态刷新Bean的属性?

我们都知道,要想实现配置属性的动态刷新,需要在类上加上一个注解CPM28资讯网——每日最新资讯28at.com

@RefreshScopeCPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

重点来了CPM28资讯网——每日最新资讯28at.com

加了@RefreshScope注解的Bean,就拿上图中的UserService举例CPM28资讯网——每日最新资讯28at.com

Spring在生成的时候会生成两个UserService的Bean:CPM28资讯网——每日最新资讯28at.com

  • 第一个是UserService的代理动态代理的Bean,后面我称为第一个Bean
  • 第二个就是UserService这个Bean,后面我称为第二个Bean

当你在其它类中需要注入一个UserService时,真正注入的是第一个Bean,也就是动态代理的BeanCPM28资讯网——每日最新资讯28at.com

当你使用这个注入的动态代理的Bean的时候,他会去找第二个Bean,也就是真正的UserService这个Bean,然后调用对应的方法CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

比如你调用注入的UserService代理对象的getUsername方法,最终就会调用到第二个BeangetUsername方法CPM28资讯网——每日最新资讯28at.com

获取到的username属性值自然也就是第二个Bean中的username值CPM28资讯网——每日最新资讯28at.com

那么为什么要生成两个Bean?CPM28资讯网——每日最新资讯28at.com

接着往下瞅CPM28资讯网——每日最新资讯28at.com

在SpringCloud中有这么一项规定CPM28资讯网——每日最新资讯28at.com

当配置中心客户端一旦感知到服务端的某个配置有变化的时候,需要发布一个RefreshEvent事件来告诉SpringCloud配置有变动CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

在SpringCloud中RefreshEventListener类会去监听这个事件CPM28资讯网——每日最新资讯28at.com

RefreshEventListenerRefreshEventListenerCPM28资讯网——每日最新资讯28at.com

一旦监听到这个事件,SpringCloud会再次从配置中心拉取配置CPM28资讯网——每日最新资讯28at.com

这个拉取配置的核心逻辑跟启动时拉取配置的核心逻辑是一样的CPM28资讯网——每日最新资讯28at.com

也是通过 BootstrapApplicationListener 来实现的CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

这部分代码逻辑在ContextRefresher类中,顺着RefreshEventListener就能看到,有兴趣可以扒一扒CPM28资讯网——每日最新资讯28at.com

怕你忘了,我再把上面拉取配置的图拿过来CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

有了最新的配置之后,就会进行一步骚操作来移花接木”刷新“注入到对象的属性CPM28资讯网——每日最新资讯28at.com

这个骚操作就是销毁所有的前面提到的第二个Bean,但是第一个Bean,也就是代理对象保持不变CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

当程序运行调用代理对象的方法的时候,发现第二个Bean没有了,此时他就会去重新创建第二个Bean,也就是重新创建一个UserService对象CPM28资讯网——每日最新资讯28at.com

由于此时已经拉到最新的配置了,也就是这个被重新创建的UserService对象注入的就是最新的属性了CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

之后再调用的这个新创建的第二个Bean,拿到的自然就是最新的配置CPM28资讯网——每日最新资讯28at.com

所以,给你的感觉是对象的属性发生了变化,实际上是真正被调用的对象重新创建了CPM28资讯网——每日最新资讯28at.com

所以这招移花接木还是有点意思的!CPM28资讯网——每日最新资讯28at.com

小总结

其实到这就弄明白了Bean的属性动态刷新的原理CPM28资讯网——每日最新资讯28at.com

其实就是当配置中心客户端发现服务端的配置有变化,需要发送一个RefreshEvent事件来告诉SpringCloud配置有变动CPM28资讯网——每日最新资讯28at.com

SpringCloud会去监听这个事件,按照项目启动的方式重新拉取配置中心最新的属性配置CPM28资讯网——每日最新资讯28at.com

当拉取完属性配置之后,就会销毁所有的第二个Bean,也就是真正被使用的BeanCPM28资讯网——每日最新资讯28at.com

之后当第一个Bean(动态代理的Bean)需要使用这个第二个Bean时,就会重新创建这个第二个BeanCPM28资讯网——每日最新资讯28at.com

此时由于已经有最新的配置了,那么创建的这个第二个Bean就会被注入最新的属性,这样就实现了属性的”刷新“CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

补充个东西:@RefreshScope的秘密

上面大致说了@RefreshScope动态刷新的原理CPM28资讯网——每日最新资讯28at.com

这里我补充一下@RefreshScope代码层面的实现原理CPM28资讯网——每日最新资讯28at.com

本来这部分原理我是写在前面的,但是我发现这块比较绕,怕打断文章的节奏,所以就准备删除了CPM28资讯网——每日最新资讯28at.com

但是想想既然都写了,那么就给放到补充里面吧,看不懂也不耽误前面的理解CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

这个注解是个衍生注解,真正起作用的就是@Scope注解CPM28资讯网——每日最新资讯28at.com

@Scope注解并不陌生,他其实是定义Bean的作用域CPM28资讯网——每日最新资讯28at.com

比如多例(原型),就可以加上@Scope("prototype")注解CPM28资讯网——每日最新资讯28at.com

还有一些八股文常背的作用域,比如session作用域等等CPM28资讯网——每日最新资讯28at.com

而@RefreshScope也可以看做是一种Bean的作用域,名字叫做refreshCPM28资讯网——每日最新资讯28at.com

这些除了单例和多例之外的作用域的底层实现逻辑都是一样的CPM28资讯网——每日最新资讯28at.com

这些带有作用域的Bean相比于普通的单例Bean主要有以下几点不同:CPM28资讯网——每日最新资讯28at.com

  • 会注册两个Bean,这个前面已经提到过
  • 保存的地方不同,比如单例Bean最终会存在三级缓存中的第一级缓存中,而不同作用域的Bean是存在不同的地方的

先说会注册两个Bean,还是以前面提到的UserService举个例子,这两个Bean分别是CPM28资讯网——每日最新资讯28at.com

  • 第一个Bean的Bean名称为userService,Bean class为ScopedProxyFactoryBean.class,这个scope为默认,也就是单例
  • 第二个Bean的Bean名称为scopedTarget.userService,Bean class为UserService.class,scope为refresh(如果是session作用域就是session)

第一个Bean的class为ScopedProxyFactoryBean,是个FactoryBean的实现CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

这个最终会生成一个代理对象,上面的例子就是为UserService生成一个代理对象,并且由于是单例的,所以最终这个对象会被放到一级缓存中,我们使用时注入的也就是这个对象CPM28资讯网——每日最新资讯28at.com

第二个Bean的class是UserService,所以生成的就是真正的UserService对象,但是由于scope为refresh,所以不会存在第一级缓存中CPM28资讯网——每日最新资讯28at.com

这部分注册两个Bean的代码是在ScopedProxyUtils#createScopedProxy方法中,有兴趣的可以扒扒CPM28资讯网——每日最新资讯28at.com

再来讲一讲保存的地方不同CPM28资讯网——每日最新资讯28at.com

不同的作用域都需要实现一个Scope接口来存放对应的BeanCPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

比如refresh、session作用域都有对应的实现CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

也就是通过Scope就可以管理不同作用域的BeanCPM28资讯网——每日最新资讯28at.com

所以,对于refresh这个作用域来说,他的所有的Bean都在RefreshScope中CPM28资讯网——每日最新资讯28at.com

后面说的销毁,只需要移除RefreshScope中的Bean就可以了CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

代码也在ContextRefresher类中

开源配置中心是如何整合SpringCloud的?

首先我们再来梳理一下拉取配置和刷新配置的核心关键点CPM28资讯网——每日最新资讯28at.com

拉取配置关键点就是项目启动的时候(也包括重新拉取配置),会去创建一个容器CPM28资讯网——每日最新资讯28at.com

这个容器只读取bootstrap配置文件和spring.factories中的键为org.springframework.cloud.bootstrap.BootstrapConfiguration对应的配置类CPM28资讯网——每日最新资讯28at.com

之后会获取这个容器中的PropertySourceLocator,从而获取配置中心的配置CPM28资讯网——每日最新资讯28at.com

刷新配置关键点就是一旦配置中心配置变动,就需要发送RefreshEvent事件,之后一系列刷新操作都是由SpringCloud的来完成的CPM28资讯网——每日最新资讯28at.com

所以,配置中心整合到SpringCloud其实就很简单,就两点CPM28资讯网——每日最新资讯28at.com

第一点就是需要实现PropertySourceLocator,并且配置中心一些相关的Bean需要通过org.springframework.cloud.bootstrap.BootstrapConfiguration来装配到这个容器中CPM28资讯网——每日最新资讯28at.com

第二点,当配置发生变更需要发送RefreshEvent事件,这部分配置中心一些相关的Bean配置肯定是需要通过自动装配来完成CPM28资讯网——每日最新资讯28at.com

有了这两点我们来看看Nacos作为配置中心是如何整合到SpringCloud的CPM28资讯网——每日最新资讯28at.com

我们直接看Nacos的spring.factories文件CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

NacosConfigBootstrapConfiguration是用来实现第一点的CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

除了Nacos自己的一些Bean,他还声明了一个NacosPropertySourceLocator这个BeanCPM28资讯网——每日最新资讯28at.com

NacosPropertySourceLocatorNacosPropertySourceLocatorCPM28资讯网——每日最新资讯28at.com

这个Bean就实现了PropertySourceLocator接口CPM28资讯网——每日最新资讯28at.com

第二点的实现就是通过NacosConfigAutoConfiguration配置类来实现的CPM28资讯网——每日最新资讯28at.com

这里面有这么一个BeanCPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

这个Bean就实现了配置变化发送事件的操作CPM28资讯网——每日最新资讯28at.com

图片图片CPM28资讯网——每日最新资讯28at.com

除了Nacos,比如说Consul作为配置中心的时候也是这么一套实现逻辑CPM28资讯网——每日最新资讯28at.com

但是值的注意的是,像Apollo配置中心,他并没有适配SpringCloud这套规范CPM28资讯网——每日最新资讯28at.com

当然,如果你有兴趣,可以自己实现Apollo适配SpringCloud这套规范CPM28资讯网——每日最新资讯28at.com


CPM28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-13640-0.html8000字+22张图探秘SpringCloud配置中心的核心原理

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 掌握这五种多线程方法,提高Java代码效率

下一篇: 从0手写一个多线程日志包

标签:
  • 热门焦点
Top