从源码角度一步步窥探Nacos配置拉取与动态刷新、监听原理

从源码⾓度⼀步步窥探Nacos配置拉取与动态刷新、监听原理
在分析原理前,我们先来回顾⼀些nacos的⼏个⽤法。
⽤法⼀:读取nacos配置中⼼的值。这种语法跟注⼊本地的配置⼀样。是spring的注解,从当前环境的属性集合获取值。
@Value(value ="${info:Local Hello world}")
private String info;
配置中⼼的值变化后,应⽤⽆法实时感知,需要重启应⽤,值才会相应变更。
⽤法⼆:动态获取nacos配置中⼼的值。⽤的是nacos的注解NacosValue,autoRefreshed 表⽰是否⾃动更新。
@NacosValue(value ="${info:Local Hello world}", autoRefreshed =true)
private String autoInfo;
autoRefreshed 为true,配置中⼼的值变化后,应⽤中的该值会实时相应跟着改变。苜蓿根
⽤法三:动态监听配置,触发监听回调函数。这种⽤法可以适⽤于⼀些实时开关,⽽开关的改变会联动⼀些应⽤⾏为变化的场景。
@NacosConfigListener(dataId ="spring-cloud-nacos-config-example.yaml")
public void configListener(String configInfo){
log.info(configInfo);
}
例⼦:以天⽓服务为例。天⽓服务对接了多个外部第三⽅天⽓接⼝,当然我们要认定他们的接⼝是不稳定的,哪怕是跟他们合作也不能信任他们的接⼝⾜够稳定。⼀般这个时候我们会⽤服务熔断或者降级措施来保证我们的天⽓服务稳定运⾏。但是会有⼀种场景,某个第三⽅天⽓接⼝不想⽤了(合作终⽌),但是我不想改代码或者重启应⽤,这时候怎么办?这时候就可以⽤⽤法三了。每个三⽅天⽓接⼝对接⼀般都会有⼀个类来处理封装,这些类都可以注册为spring的bean。因此可以把启⽤哪个默认的三⽅服务作为开关。监听配置开关,回调函数⾥动态注册bean与删除bean,刷新spring IoC容器。
接下来我们从源码的⾓度来⼀步⼀步分析它们的实现原理。笔者划分为两部分:
1:应⽤系统如何拉取外部化配置。
2:配置动态刷新与监听是怎么实现的。
拉取外部化配置
⾸先我们要先来理解⼀个概念:什么叫外部化配置?在笔者认识⾥,除了不在应⽤系统⾥的配置都可以叫外部化配置。⽐如配置放在⼀个⽹络资源⾥(URL)、放在系统某个⽂件路径下(FileSystem)或者就是放在配置中⼼。
看到这个场景,有没有觉得特别像spring的配置⽂件(XML)的加载。是的,在没有注解时(spring⽼版本⽤法时) spring加载配置⽂件可以从⽹络资源⾥(URL)、放在系统某个⽂件路径下(FileSystem)或者类路径(ClassPath)来加载定位资源。
接下来源码分析。⾸先我们从启动类⼊⼿
@SpringBootApplication
public class NacosDiscoveryApplication {
public static void main(String[] args){
SpringApplication.run(NacosDiscoveryApplication.class, args);
}
}
run⽅法⼀直跟进去,来到ConfigurableApplicationContext 的run⽅法。先重点关注prepareEnvironment()与listeners.running().
public ConfigurableApplicationContext args){
// 省略部分代码
// 预备环境
ConfigurableEnvironment environment =prepareEnvironment(listeners, applicationArguments);
// 上下⽂准备期
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 省略部分代码
try{
// 启动
listeners.running(context);
}
catch(Throwable ex){
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
我们看看prepareEnvironment⽅法做些什么事,源码跟进去。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments){
// 省略部分代码
// 发布⼀个ApplicationEnvironmentPreparedEvent事件
// 省略部分代码
return environment;
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment){
// ⼴播ApplicationEnvironmentPreparedEvent事件
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application,this.args, environment));
}
既然有事件发布,就会有对改事件感兴趣并监听它的。BootstrapApplicationListener监听ApplicationEnvironmentPreparedEvent。我们看看它的监听实现⾥做了什么。
public class BootstrapApplicationListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event){
// 省略部分代码
if(context == null){
context =bootstrapServiceContext(environment, SpringApplication(),
configName);
.addListeners(new CloseContextOnFailureApplicationListener(context));
}
}
private ConfigurableApplicationContext bootstrapServiceContext(
ConfigurableEnvironment environment,final SpringApplication application,
String configName){
/
/ 省略部分代码
builder.sources(BootstrapImportSelectorConfiguration.class);
}
}
onApplicationEvent监听实现⾥有这么⼀个⾄关重要的操作builder.sources(BootstrapImportSelectorConfiguration.class)。⽽BootstrapImportSelectorConfiguration⾥@import 了BootstrapImportSelector类
@Configuration(proxyBeanMethods =false)
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {
}
@Import⾃动装配时,可以import@Configuration的类也可以import 实现ImportSelector接⼝的实现类。
这是springboot的⾃动装配原理的⼀个细节之⼀
public class BootstrapImportSelector implements EnvironmentAware, DeferredImportSelector {
// 省略部分代码
// 利⽤spring的SPI扩展机制加载BootstrapConfiguration
List<String> names =new ArrayList<>(SpringFactoriesLoader
.loadFactoryNames(BootstrapConfiguration.class, classLoader));
}
利⽤spring的SPI扩展机制加载BootstrapConfiguration类。那么是谁在暴露呢来达到让spring利⽤SPI加载到它们。了⼀下,它们就是spring-cloud-alibaba-nacos-config.jar与spring-cloud-contex.jar的spring.factories下配置中。其中有两个配置类
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration和
org.springframework.fig.PropertySourceBootstrapConfiguration
SPI机制⾮常灵活,有原⽣的Java SPI机制,也有Dubbo的SPI、Spring的SPI等。当然它们的思想都是⼀致的,遵循⼀定契约精神。
疑问:到⽬前为⽌,加载它们有什么⽤呢?
带着疑问,我们回推。⼀开始的run⽅法有个prepareContext()上下⽂准备期。
private  void  prepareContext (ConfigurableApplicationContext context , ConfigurableEnvironment environment ,
SpringApplicationRunListeners listeners , ApplicationArguments applicationArguments , Banner print
edBanner ) {
// 省略部分代码
// 这个⽅法会执⾏容器中的ApplicationContextInitializer ,让应⽤程序上下⽂初始化的时候做⼀些额外的操作。
applyInitializers (context );车辆排队长度
// 省略部分代码
protected  void  applyInitializers (ConfigurableApplicationContext context ) {铁皮枫斗口服液
油烟机油杯
for  (ApplicationContextInitializer initializer : getInitializers ()) {
Class <?> requiredType = GenericTypeResolver .resolveTypeArgument (initializer .getClass (),
ApplicationContextInitializer .class );
Assert .isInstanceOf (requiredType , context , "Unable to call initializer.");
initializer .initialize (context );
}
}
}
ApplicationContextInitializer是⼀个接⼝,那必然有它的实现类来实现执⾏。其中有⼀个实现类,那就是上⽂我们分析的SPI机制加载的PropertySourceBootstrapConfiguration。
我们来看看PropertySourceBootstrapConfiguration的initialize重写的逻辑是怎样的。
@Override
public void initialize(ConfigurableApplicationContext applicationContext){软膜布
// 省略部分代码
ConfigurableEnvironment environment = Environment();
for(PropertySourceLocator locator :this.propertySourceLocators){
// 根据当前环境定位配置
Collection<PropertySource<?>> source = locator.locateCollection(environment);
// 省略部分代码
}
人体意术}
PropertySourceLocator 是spring-cloud提供的⼀个配置属性定位规范。有⼀个默认实现⽅法locateCollection。
static Collection<PropertySource<?>>locateCollection(PropertySourceLocator locator,
Environment environment){
// 定位配置
PropertySource<?> propertySource = locator.locate(environment);
// 省略部分代码
那么是必有个PropertySourceLocator 的实现类来真正执⾏locate⽅法。猜到了没?那必然是与Nacos有关,⽽且的⾥⾯逻辑必然是去Nacos的配置中⼼拉取配置。它就是NacosPropertySourceLocator。NacosPropertySourceLocator的locate⽅法主要做了两步
初始化⼀个ConfigService对象,这是Nacos客户端提供的⽤于访问实现配置中⼼基本操作的类。
按顺序分别加载共享配置、扩展配置、应⽤名称对应的配置。

本文发布于:2024-09-21 00:27:00,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/3/160776.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:配置   监听   实现   动态   开关   服务   代码   加载
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议