Nacos配置文件读取源码解析
Nacos配置文件读取
本篇文章是探究,springboot启动时nacos是如何将配置中心的配置读取到springboot环境中的
PropertySourceLocator
org.springframework.cloud.bootstrap.config.PropertySourceLocator 是 springcloud 定义的一个顶级接口,用来定义所有实现类加载自定义配置的类,数据的调用则是在 PropertySourceBootstrapConfiguration 自动装配中
public interface PropertySourceLocator {/*** 创建配置源** @param environment 环境* @return {@code PropertySource<?> }* @date 2023-08-09 11:43:03*/PropertySource<?> locate(Environment environment);default Collection<PropertySource<?>> locateCollection(Environment environment) {return locateCollection(this, environment);}static Collection<PropertySource<?>> locateCollection(PropertySourceLocator locator,Environment environment) {//调用 locate() 方法构建配置对象PropertySource<?> propertySource = locator.locate(environment);//如果为空直接返回空的集合if (propertySource == null) {return Collections.emptyList();}//判断当前对象是否是 CompositePropertySource 多配置文件对象聚合类,这是nacos的默认实现类,会构建多个配置对象if (CompositePropertySource.class.isInstance(propertySource)) {Collection<PropertySource<?>> sources = ((CompositePropertySource) propertySource).getPropertySources();List<PropertySource<?>> filteredSources = new ArrayList<>();//将数据获取出来for (PropertySource<?> p : sources) {if (p != null) {filteredSources.add(p);}}return filteredSources;}else {return Arrays.asList(propertySource);}}}
PropertySourceBootstrapConfiguration
配置属性的全局自动配置类,由 springcloud 定义并且在 springboot初始化时进行调用;配置类中的属性就注入了所有的 PropertySourceLocator接口的实现类
public class PropertySourceBootstrapConfiguration implementsApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
/*** 自定注入所有的实现类 PropertySourceLocator*/@Autowired(required = false)private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();}
initialize 方法在springboot调用 ApplicationContextInitializer 是进行执行
public void initialize(ConfigurableApplicationContext applicationContext) {//将nacos中的配置构建为 PropertySource 配置属性对象List<PropertySource<?>> composite = new ArrayList<>();//根据@Order注解进行排序AnnotationAwareOrderComparator.sort(this.propertySourceLocators);boolean empty = true;//获取到环境对象ConfigurableEnvironment environment = applicationContext.getEnvironment();//循环遍历for (PropertySourceLocator locator : this.propertySourceLocators) {//调用实现方法进行构建配置对象 PropertySourceCollection<PropertySource<?>> source = locator.locateCollection(environment);//如果配置出来的 PropertySource 为空,那么直接跳过到下一个if (source == null || source.size() == 0) {continue;}List<PropertySource<?>> sourceList = new ArrayList<>();//根据配置出来的 PropertySource 在进行包装,除了 EnumerablePropertySource枚举类型配置源,其他的都是 SimpleBootstrapPropertySourcefor (PropertySource<?> p : source) {if (p instanceof EnumerablePropertySource) {EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) p;sourceList.add(new BootstrapPropertySource<>(enumerable));}else {sourceList.add(new SimpleBootstrapPropertySource(p));}}logger.info("Located property source: " + sourceList);composite.addAll(sourceList);empty = false;}//如果配置不为空的话if (!empty) {//获取到环境中的属性配置类,MutablePropertySources 这个类中包含了一个 List<PropertySource<?>>MutablePropertySources propertySources = environment.getPropertySources();//解析日志配置的占位符String logConfig = environment.resolvePlaceholders("${logging.config:}");//解析日志文件出来LogFile logFile = LogFile.get(environment);//移除掉 bootstrapProperties 这个配置源,因为这个配置源一般在容器启动之前就已经全局加载了,不需要再使用for (PropertySource<?> p : environment.getPropertySources()) {if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {propertySources.remove(p.getName());}}//将构建出来的配置源添加到环境中去insertPropertySources(propertySources, composite);//再次构建日志系统相关的配置reinitializeLoggingSystem(environment, logConfig, logFile);//设置日志级别setLogLevels(applicationContext, environment);//这里处理需要包含的环境配置,将配置文件中存在 spring.profiles.include 设置到环境中handleIncludedProfiles(environment);}}
NacosPropertySourceLocator
nacos实现的配置定位器,包含的属性值
- nacosPropertySourceBuilder:nacos的配置属性构建器,就是它去拉取配置然后构建 PropertySource
- nacosConfigProperties:nacos的配置属性类
- nacosConfigManager:nacos配置管理器
public class NacosPropertySourceLocator implements PropertySourceLocator {private static final String NACOS_PROPERTY_SOURCE_NAME = "NACOS";//分隔符private static final String DOT = ".";private static final String SEP1 = "-";//配置的构建器private NacosPropertySourceBuilder nacosPropertySourceBuilder;//nacos配置private NacosConfigProperties nacosConfigProperties;//nacos配置管理器private NacosConfigManager nacosConfigManager;
}
实现方法中,创建了一个配置构建器对象 **nacosPropertySourceBuilder **传入 ConfigService 属性,ConfigService,发起请求的就是 ConfigService类
public PropertySource<?> locate(Environment env) {nacosConfigProperties.setEnvironment(env);//配置服务(NacosConfigService),比较核心通过http长轮询的方式获取配置,2.0之后改为grpcConfigService configService = nacosConfigManager.getConfigService();if (null == configService) {log.warn("no instance of config service found, can't load config from nacos");return null;}long timeout = nacosConfigProperties.getTimeout();//创建一个配置的构建器nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,timeout);//先获取到配置文件中指定的名称String name = nacosConfigProperties.getName();//获取到配置的前缀String dataIdPrefix = nacosConfigProperties.getPrefix();if (StringUtils.isEmpty(dataIdPrefix)) {dataIdPrefix = name;}//如果配置都为空,那么直接获取 spring.application.name 配置的名称if (StringUtils.isEmpty(dataIdPrefix)) {dataIdPrefix = env.getProperty("spring.application.name");}//创建一个配置名称为 nacos 的聚合配置属性对象CompositePropertySource composite = new CompositePropertySource(NACOS_PROPERTY_SOURCE_NAME);//加载共享配置文件,分组为默认loadSharedConfiguration(composite);//加载扩展配置文件loadExtConfiguration(composite);//加载配置文件loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);return composite;}/*** 根据文件名称和分组加载配置文件,拼接激活的环境后缀** @param compositePropertySource 复合属性来源* @param dataIdPrefix 数据id前缀* @param properties 配置属性* @param environment 环境* @date 2023-08-09 13:43:32*/
private void loadApplicationConfiguration(CompositePropertySource compositePropertySource, String dataIdPrefix,NacosConfigProperties properties, Environment environment) {//获取到指定的文件后缀,默认是propertiesString fileExtension = properties.getFileExtension();//获取到配置的分组String nacosGroup = properties.getGroup();// 加载没有带后缀的配置文件例如:product 因为这里直接将服务名称作为了dataIdloadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,fileExtension, true);// 加载带后缀的配置文件例如:product.propertiesloadNacosDataIfPresent(compositePropertySource,dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);// 根据激活的环境去拼接对应的配置文件名称并且进行加载for (String profile : environment.getActiveProfiles()) {String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;/*** 1. 先通过 NacosPropertySourceRepository 判断配置文件是否已经加载了* 2. 在 nacosPropertySourceBuilder 通过 configService 加载配置文件,加载完成了后会将其注册到 NacosPropertySourceRepository* 3. 在上下文创建完成后根据配置文件再注册对应的监听器 NacosContextRefresher*/loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,fileExtension, true);}}/*** 通过 ConfigService 服务去拉取配置文件,然后在构建为 NacosPropertySource** @param dataId 数据标识* @param group 组名* @param fileExtension 文件扩展名* @param isRefreshable 是否可刷新* @return {@code NacosPropertySource }* @date 2023-08-09 13:42:22*/private NacosPropertySource loadNacosPropertySource(final String dataId,final String group, String fileExtension, boolean isRefreshable) {if (NacosContextRefresher.getRefreshCount() != 0) {if (!isRefreshable) {return NacosPropertySourceRepository.getNacosPropertySource(dataId,group);}}return nacosPropertySourceBuilder.build(dataId, group, fileExtension,isRefreshable);}
NacosConfigService
nacos的配置服务,用于发送请求跟nacos注册中心进行交互的组件
public class NacosConfigService implements ConfigService {private static final Logger LOGGER = LogUtils.logger(NacosConfigService.class);/*** 2.0版本之后将会删除,2.0之前采用的是http长轮询的方式,2.0之后就采用rpc*/@DeprecatedServerHttpAgent agent = null;/*** 客户端工作器,客户端将配置的监听器注册到这里,然后由这里通过线程池进行长轮询监听配置文件的改变*/private final ClientWorker worker;//命名空间private String namespace;//配置文件的过滤器链管理器private final ConfigFilterChainManager configFilterChainManager;}
- serverListManager :先构建服务器列表的管理器
- worker:真正执行配置监听和创建的服务
public NacosConfigService(Properties properties) throws NacosException {//验证参数ValidatorUtils.checkInitParam(properties);//初始化命名空间的名称initNamespace(properties);//通过 ServiceLoader 加载出来的 ConfigFilter 实现类,然后将其添加到 ConfigFilterChainManager 中this.configFilterChainManager = new ConfigFilterChainManager(properties);/*** 启动服务端列表管理器,如果nacos是集群配置,其中会将配置的对应的服务器集群的地址进行解析*/ServerListManager serverListManager = new ServerListManager(properties);//定时每30秒拉取一次服务的列表serverListManager.start();/*** 启动本地配置信息处理器,2.0之前采用的http长轮询的方式进行监听,2.0之后采用rpc的方式进行监听* 通过一个阻塞队列进行线程的阻塞,默认是5秒钟便会唤醒一次,然后进行一次配置变动的监听更新,如果* 有监听器进行注册,那么会立即执行一次*/this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, properties);// will be deleted in 2.0 later versionsagent = new ServerHttpAgent(serverListManager);}
根据对应的配置名称和组名拉取对应的配置信息
/*** 根据对应的命名空间、dataId、group、超时时间获取配置文件** @param dataId dataId* @param group group* @param timeoutMs read timeout* @return* @throws NacosException*/@Overridepublic String getConfig(String dataId, String group, long timeoutMs) throws NacosException {return getConfigInner(namespace, dataId, group, timeoutMs);}
添加配置文件变动的监听器
/*** 添加监听器** @param dataId 数据标识* @param group 组* @param listener 侦听器* @author zhonghaijun* @date 2023-08-09 13:45:52*/@Overridepublic void addListener(String dataId, String group, Listener listener) throws NacosException {worker.addTenantListeners(dataId, group, Arrays.asList(listener));}
手动推送配置
/*** 发布配置** @param dataId 数据标识* @param group 组* @param content 内容* @return boolean* @author zhonghaijun* @date 2023-08-09 14:44:10*/@Overridepublic boolean publishConfig(String dataId, String group, String content) throws NacosException {return publishConfig(dataId, group, content, ConfigType.getDefaultType().getType());}
ClientWorker
客户端工作器,这个组件专门用于推送配置、监听配置、获取配置等服务
- ConfigRpcTransportClient:用于发起rpc的客户端,start() 方法将会回调到 ClientWorker.startInternal() 方法中
public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ServerListManager serverListManager,final Properties properties) throws NacosException {this.configFilterChainManager = configFilterChainManager;//初始化参数超时时间、配置重试时间、是否开启远程配置同步(开启后在配置配置文件监听时会先去远程服务器上对内容进行同步一次)init(properties);//创建rpc客户端agent = new ConfigRpcTransportClient(properties, serverListManager);//创建一个定时任务的线程池,提交一个守护线程ScheduledExecutorService executorService = Executors.newScheduledThreadPool(ThreadUtils.getSuitableThreadCount(1), r -> {Thread t = new Thread(r);t.setName("com.alibaba.nacos.client.Worker");t.setDaemon(true);return t;});agent.setExecutor(executorService);/*** 启动rpc客户端,* 调用 当期类的 startInternal() 方法,这个方法监听 listenExecutebell阻塞队列的任务* 在客户端添加配置监听服务时,会向 listenExecutebell阻塞队列中添加一个任务,这个任务会去远程服务器上获取配置内容*/agent.start();}
/*** 根据命名空间添加一个配置的监听器** @param dataId dataId of data* @param group group of data* @param listeners listeners* @throws NacosException nacos exception*/public void addTenantListeners(String dataId, String group, List<? extends Listener> listeners)throws NacosException {//如果分组为空,那么就直接以默认的分组进行处理group = blank2defaultGroup(group);//获取到对应的租户信息,就是namespace命名空间String tenant = agent.getTenant();//创建缓存数据,如果开启了 enableRemoteSyncConfig,会去远程服务拉取配置信息CacheData cache = addCacheDataIfAbsent(dataId, group, tenant);synchronized (cache) {//添加监听器for (Listener listener : listeners) {cache.addListener(listener);}cache.setSyncWithServer(false);//向队列listenExecutebell中添加一个元素,用于唤醒监听线程,在startInternal() 方法中进行监听agent.notifyListenConfig();}}
相关文章:
Nacos配置文件读取源码解析
Nacos配置文件读取 本篇文章是探究,springboot启动时nacos是如何将配置中心的配置读取到springboot环境中的 PropertySourceLocator org.springframework.cloud.bootstrap.config.PropertySourceLocator 是 springcloud 定义的一个顶级接口,用来定义所…...
Linux0.11内核源码解析-fcntl.c/iotcl.c/stat.c
fcntl fcntl.c实现了文件控制系统调用fcntl和两个文件句柄描述符的复制系统调用dup()和dup2()。 dup返回当前值最小的未用句柄,dup2返回指定新句柄的数值,句柄的复制操作主要用在文件的标准输入、输出重定向和管道方面。 dupfd 复制文件句柄ÿ…...
OpenStack简介
OpenStack简介 目录 OpenStack简介 1、云计算模式2、云计算 虚拟化 openstack之间的关系?3、OpenStack 中有哪些组件?4、计算节点负责虚拟机运行5、网络节点负责对外网络与内网之间的通信 5.1 网络节点仅包含Neutron服务5.2 网络节点包含三个网络端口6、…...

二分法的应用
文章目录 什么是二分法🎮二分查找的优先级二分查找的步骤💥图解演示🧩 代码演示🫕python程序实现🐈⬛C程序实现🐕🦺C程序实现🐯Java程序实现🐳 非常规类二分查找&…...
ChatGPT在大规模数据处理和信息管理中的应用如何?
ChatGPT作为一种强大的自然语言处理模型,在大规模数据处理和信息管理领域有着广泛的应用潜力。它可以利用其文本生成、文本理解和问答等能力,为数据分析、信息提取、知识管理等任务提供智能化的解决方案。以下将详细介绍ChatGPT在大规模数据处理和信息管…...

【算法篇C++实现】五大常规算法
文章目录 🚀一、分治法⛳(一)算法思想⛳(二)相关代码 🚀二、动态规划算法⛳(一)算法思想⛳(二)相关代码 🚀三、回溯算法⛳(一…...

MySQL和钉钉单据接口对接
MySQL和钉钉单据接口对接 数据源系统:钉钉 钉钉(DingTalk)是阿里巴巴集团打造的企业级智能移动办公平台,是数字经济时代的企业组织协同办公和应用开发平台。钉钉将IM即时沟通、钉钉文档、钉闪会、钉盘、Teambition、OA审批、智能人事、钉工牌…...

layui的基本使用-日期控件的业务场景使用入门实战案例一
效果镇楼; 1 前端UI层面; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&…...

【2.1】Java微服务:详解Hystrix
✅作者简介:大家好,我是 Meteors., 向往着更加简洁高效的代码写法与编程方式,持续分享Java技术内容。 🍎个人主页:Meteors.的博客 💞当前专栏: Java微服务 ✨特色专栏: 知识分享 &am…...

Apache2.4源码安装与配置
环境准备 openssl-devel pcre-devel expat-devel libtool gcc libxml2-devel 这些包要提前安装,否则httpd编译安装时候会报错 下载源码、解压缩、软连接 1、wget下载[rootnode01 ~]# wget https://downloads.apache.org/httpd/httpd-2.4.57.tar.gz --2023-07-20 …...

Flume原理剖析
一、介绍 Flume是一个高可用、高可靠,分布式的海量日志采集、聚合和传输的系统。Flume支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制&…...
【leetcode】202. 快乐数(easy)
编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1,…...

如何用瀑布图分析公司年报
原创: MicroStrategy微策略中国 , Jiping Sun 微策略企业级数据分析与移动应用9月21日2018年 摘要:利用达析报告开箱即用的瀑布图来展示各个度量值如何增加或减少。下载MicroStrategy Desktop 10.11以上版本,自己动手创建瀑布图。 瀑布图是由…...

Asynq: 基于Redis实现的Go生态分布式任务队列和异步处理库
Asynq[1]是一个Go实现的分布式任务队列和异步处理库,基于redis,类似Ruby的sidekiq[2]和Python的celery[3]。Go生态类似的还有machinery[4]和goworker 同时提供一个WebUI asynqmon[5],可以源码形式安装或使用Docker image, 还可以和Prometheus…...
保证率计算公式 正态分布
在正态分布中,如果我们要计算一个给定区间内的保证率,可以使用下面的计算公式: 找到给定保证率对应的标准正态分布的z值。可以使用标准正态分布表或计算器进行查询。例如,对于95%的保证率,对应的z值为1.96。 使用z值和…...

docker容器监控:Cadvisor+InfluxDB+Grafana的安装部署
目录 CadvisorInfluxDBGrafan安装部署 1、安装docker-ce 2、阿里云镜像加速器 3、下载组件镜像 4、创建自定义网络 5、创建influxdb容器 6、创建Cadvisor 容器 7、查看Cadvisor 容器: (1)准备测试镜像 (2)通…...

论文讲解——TPU-MLIR: A Compiler For TPU Using MLIR
论文讲解——TPU-MLIR: A Compiler For TPU Using MLIR https://arxiv.org/pdf/2210.15016.pdf概览模型转换TranslationCanonicalizeLoweringLayerGroup BufferizationCalibration QuantizationCorrectness Check相关资料 https://arxiv.org/pdf/2210.15016.pdf 本文将对TPU…...

基于最新导则下生态环评报告编制技术暨报告篇、制图篇、指数篇、综合应用篇系统性实践技能提升
查看原文>>>基于最新导则下生态环评报告编制技术暨报告篇、制图篇、指数篇、综合应用篇系统性实践技能提升 目录 专题一、生态环评报告编制规范 专题二、土地利用图 专题三、植被类型及植被覆盖度图 专题四、物种适宜生境分布图 专题五、生物多样性测定 专题六…...

NGZORRO:动态表单/模型驱动 的相关问题
官网的demo的[nzFor]"control.controlInstance",似乎是靠[formControlName]"control.controlInstance"来关联的。 <form nz-form [formGroup]"validateForm" (ngSubmit)"submitForm()"><nz-form-item *ngFor&quo…...
第十七次CCF计算机软件能力认证
第一题:小明种苹果 n , m map(int , input().split()) t , k , p 0 , 0 , -1 for _ in range(n):l list(map(int , input().split()))t sum(l)x -sum(l[i] for i in range(1 , len(l)))if x > p:p xk _ 1 print(t , k , p) 第二题:小明种苹…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...