首页>>后端>>java->实战 Cloud : Nacos 配置加载流程和优先级

实战 Cloud : Nacos 配置加载流程和优先级

时间:2023-12-01 本站 点击:0

一 . 前言

这一篇来看一下 Nacos Client 对配置的请求流程以及相关的配置

二 . 配置类及用法

2.1 基本使用案例

#这也是最常见的案例模板spring:application:name:nacos-multi-configcloud:nacos:config:extension-configs:#对应NacosDataId-data-id:nacos-multi-config-A.yamlgroup:DEFAULT_RSM-data-id:nacos-multi-config-B.yamlgroup:DEFAULT_RSM-data-id:nacos-multi-config-C.yamlgroup:DEFAULT_RSM#文件后缀file-extension:yamlserver-addr:127.0.0.1:8848discovery:server-addr:127.0.0.1:8848

2.2 配置类解析

Nacos 对应的配置类为 NacosConfigProperties , 这里来看一下所有的参数:

//Nacos地址privateStringserverAddr;//用户名及密码privateStringusername;privateStringpassword;//内容编码privateStringencode;//所属组privateStringgroup="DEFAULT_GROUP";//ConfigDataId前缀privateStringprefix;//Config后缀文件名privateStringfileExtension="properties";//获取配置超时时间privateinttimeout=3000;//最大重连次数privateStringmaxRetry;//获取配置长轮询超时时间privateStringconfigLongPollTimeout;//配置获取错误的重试次数privateStringconfigRetryTime;//自动获取及注册Listener监听,会有网络开销privatebooleanenableRemoteSyncConfig=false;//服务域名,可动态获取服务器地址privateStringendpoint;//命名空间,不同环境的分离配置privateStringnamespace;//访问键和访问密钥privateStringaccessKey;privateStringsecretKey;//容器地址privateStringcontextPath;//分片名privateStringclusterName;//naco配置dataId名称privateStringname;/***共享配置集*spring.cloud.nacos.config.shared-configs[0]=xxx.*/privateList<Config>sharedConfigs;/***扩展配置集*spring.cloud.nacos.config.extension-configs[0]=xxx.*/privateList<Config>extensionConfigs;//是否刷新配置privatebooleanrefreshEnabled=true;

三 . 配置流程

下面看一下配置的加载和获取流程

3.1 开启配置的入口

配置得开启是基于 PropertySourceBootstrapConfiguration 开启的 ,

publicvoidinitialize(ConfigurableApplicationContextapplicationContext){List<PropertySource<?>>composite=newArrayList<>();AnnotationAwareOrderComparator.sort(this.propertySourceLocators);booleanempty=true;ConfigurableEnvironmentenvironment=applicationContext.getEnvironment();//从不同的locators获取资源for(PropertySourceLocatorlocator:this.propertySourceLocators){//对于Nacos,此处会调用NacosPropertySourceLocatorCollection<PropertySource<?>>source=locator.locateCollection(environment);if(source==null||source.size()==0){continue;}List<PropertySource<?>>sourceList=newArrayList<>();for(PropertySource<?>p:source){//分别生成了BootstrapPropertySource和SimpleBootstrapPropertySourceif(pinstanceofEnumerablePropertySource){EnumerablePropertySource<?>enumerable=(EnumerablePropertySource<?>)p;sourceList.add(newBootstrapPropertySource<>(enumerable));}else{sourceList.add(newSimpleBootstrapPropertySource(p));}}composite.addAll(sourceList);empty=false;}//省略资源的处理,后文再说}

3.2 Nacos 加载入口

期间会经过接口类的 PropertySourceLocator 来发起对应

//C-NacosPropertySourceLocatorpublicPropertySource<?>locate(Environmentenv){nacosConfigProperties.setEnvironment(env);//ConfigService是主要的查询ConfigServiceconfigService=nacosConfigManager.getConfigService();if(null==configService){log.warn("noinstanceofconfigservicefound,can'tloadconfigfromnacos");returnnull;}//超时时间可以通过配置文件配置longtimeout=nacosConfigProperties.getTimeout();//为该类的局部变量,意味着通用该变量nacosPropertySourceBuilder=newNacosPropertySourceBuilder(configService,timeout);//准备前缀和nameStringname=nacosConfigProperties.getName();StringdataIdPrefix=nacosConfigProperties.getPrefix();if(StringUtils.isEmpty(dataIdPrefix)){dataIdPrefix=name;}//注意,默认前缀if(StringUtils.isEmpty(dataIdPrefix)){dataIdPrefix=env.getProperty("spring.application.name");}CompositePropertySourcecomposite=newCompositePropertySource(NACOS_PROPERTY_SOURCE_NAME);//privateList<Config>sharedConfigs的处理loadSharedConfiguration(composite);//privateList<Config>extensionConfigs的处理loadExtConfiguration(composite);//主配置文件获取loadApplicationConfiguration(composite,dataIdPrefix,nacosConfigProperties,env);returncomposite;}

补充一 : loadSharedConfiguration 进行共享数据的处理

持多个共享 Data Id 的配置,优先级小于extension-configs , 适合于共享配置文件与项目默认配置文件处于相同Group时 (PS:只能在一个 Group 中)

主要流程为获取配置 , 校验准确性 , 调用 loadNacosConfiguration 发起配置的调用

PS: 主要流程看补充二 , 这也是为什么优先级没有 loadExtConfiguration 高的原因

补充二 : loadExtConfiguration 处理配置

主要流程为 nacosConfigProperties.getExtensionConfigs() , 判断是否存在 , 存在会先 checkConfiguration , 再调用 loadNacosConfiguration 进行主流程处理

privatevoidloadNacosConfiguration(finalCompositePropertySourcecomposite,List<NacosConfigProperties.Config>configs){//此处会循环调用,所以后面的实际上会覆盖前面的for(NacosConfigProperties.Configconfig:configs){//getFileExtension为文件后缀loadNacosDataIfPresent(composite,config.getDataId(),config.getGroup(),NacosDataParserHandler.getInstance().getFileExtension(config.getDataId()),config.isRefresh());}}//后续逻辑就是调用NacosPropertySourceBuilder进行正在的发起查询和解析NacosPropertySourcebuild(StringdataId,Stringgroup,StringfileExtension,booleanisRefreshable){//Step1:此处查询到对象并且完成解析->3.3List<PropertySource<?>>propertySources=loadNacosData(dataId,group,fileExtension);//Step2:将解析的对象封装为NacosPropertySourceNacosPropertySourcenacosPropertySource=newNacosPropertySource(propertySources,group,dataId,newDate(),isRefreshable);//Step3:NacosPropertySourceRepository中有个ConcurrentHashMap<String,NacosPropertySource>NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);returnnacosPropertySource;}

补充三 : 主配置文件获取

该流程的优先级最高 , 是通过常规方式获取配置的流程

privatevoidloadApplicationConfiguration(CompositePropertySourcecompositePropertySource,StringdataIdPrefix,NacosConfigPropertiesproperties,Environmentenvironment){//获取文件后缀和组StringfileExtension=properties.getFileExtension();StringnacosGroup=properties.getGroup();//第一次加载:默认情况下直接加载一次loadNacosDataIfPresent(compositePropertySource,dataIdPrefix,nacosGroup,fileExtension,true);//第二次加载:加载带有后缀,具有比默认值更高的优先级loadNacosDataIfPresent(compositePropertySource,dataIdPrefix+DOT+fileExtension,nacosGroup,fileExtension,true);//第三次加载:用配置文件Profile加载,它比后缀具有更高的优先级//此处循环所有的Profiles,分别进行处理for(Stringprofile:environment.getActiveProfiles()){StringdataId=dataIdPrefix+SEP1+profile+DOT+fileExtension;loadNacosDataIfPresent(compositePropertySource,dataId,nacosGroup,fileExtension,true);}}

可以看到 , 这里加载了多次 , 同时通过这种方式确定优先级

3.3 Nacos 获取配置主流程

privateList<PropertySource<?>>loadNacosData(StringdataId,Stringgroup,StringfileExtension){Stringdata=null;//Step1:获取远程配置信息data=configService.getConfig(dataId,group,timeout);if(StringUtils.isEmpty(data)){returnCollections.emptyList();}//Step2:解析远程配置信息returnNacosDataParserHandler.getInstance().parseNacosData(dataId,data,fileExtension);//此处省略了异常处理逻辑,出现异常不会抛出,而是返回空集合//PS:这里也导致部分错误不好从日志判断原因//returnCollections.emptyList();}

PS : 这里不止是个字符串 , 复制到文本里面可以看出就是一个 yaml 格式的数据

3.3.1 远程配置的获取

privateStringgetConfigInner(Stringtenant,StringdataId,Stringgroup,longtimeoutMs)throwsNacosException{group=null2defaultGroup(group);ParamUtils.checkKeyParam(dataId,group);ConfigResponsecr=newConfigResponse();//准备查询对象//{dataId=nacos-multi-config-B.yaml,tenant=,group=DEFAULT_RSM}cr.setDataId(dataId);cr.setTenant(tenant);cr.setGroup(group);//优先使用本地配置Stringcontent=LocalConfigInfoProcessor.getFailover(agent.getName(),dataId,group,tenant);//如果此处有本地配置,则直接返回,此处省略try{//发起远程调用//HttpAgent.httpGet(Constants.CONFIG_CONTROLLER_PATH,null,params,agent.getEncode(),readTimeout)String[]ct=worker.getServerConfig(dataId,group,tenant,timeoutMs);cr.setContent(ct[0]);configFilterChainManager.doFilter(null,cr);content=cr.getContent();returncontent;}catch(NacosExceptionioe){if(NacosException.NO_RIGHT==ioe.getErrCode()){throwioe;}}//如果不是403,则会通过快照获取content=LocalConfigInfoProcessor.getSnapshot(agent.getName(),dataId,group,tenant);cr.setContent(content);configFilterChainManager.doFilter(null,cr);content=cr.getContent();returncontent;}

3.3.2 远程配置的解析

//C-NacosDataParserHandlerpublicList<PropertySource<?>>parseNacosData(StringconfigName,StringconfigValue,Stringextension)throwsIOException{if(StringUtils.isEmpty(configValue)){returnCollections.emptyList();}//文件后缀if(StringUtils.isEmpty(extension)){extension=this.getFileExtension(configName);}//此处的PropertySourceLoader主要有四种://-NacosXmlPropertySourceLoader:XML文件格式//-PropertiesPropertySourceLoader:Properties文件格式//-YamlPropertySourceLoader:YAML文件格式//-NacosJsonPropertySourceLoader:JSON文件格式for(PropertySourceLoaderpropertySourceLoader:propertySourceLoaders){if(!canLoadFileExtension(propertySourceLoader,extension)){//如果不能加载,直接退出continue;}NacosByteArrayResourcenacosByteArrayResource;//省略转换为Byte逻辑:newNacosByteArrayResourcenacosByteArrayResource.setFilename(getFileName(configName,extension));//核心逻辑,此处将yaml格式文本转换为了OriginTrackedMapPropertySourceList<PropertySource<?>>propertySourceList=propertySourceLoader.load(configName,nacosByteArrayResource);if(CollectionUtils.isEmpty(propertySourceList)){returnCollections.emptyList();}returnpropertySourceList.stream().filter(Objects::nonNull).map(propertySource->{if(propertySourceinstanceofEnumerablePropertySource){//获得Name名称String[]propertyNames=((EnumerablePropertySource)propertySource).getPropertyNames();if(propertyNames!=null&&propertyNames.length>0){Map<String,Object>map=newLinkedHashMap<>();//此处是将OriginTrackedValue转换为对应类型的值Arrays.stream(propertyNames).forEach(name->{map.put(name,propertySource.getProperty(name));});//最终构建一个PropertySource的List集合returnnewOriginTrackedMapPropertySource(propertySource.getName(),map,true);}}returnpropertySource;}).collect(Collectors.toList());}returnCollections.emptyList();}

补充 : YamlPropertySourceLoader 流程

publicList<PropertySource<?>>load(Stringname,Resourceresource)throwsIOException{//核心:通过OriginTrackedYamlLoader进行Loader加载,此处就不深入了,属于工具类的功能List<Map<String,Object>>loaded=newOriginTrackedYamlLoader(resource).load();//省略判空和添加逻辑->propertySources.add(newOriginTrackedMapPropertySourcereturnpropertySources;}

四 . 总结

篇幅有限 , 所以 本地配置的覆盖与集成 以及 配置的顺序加载及优先级 准备放在下一篇文档里面梳理 , 下面分享一张流程图

补充 : 配置的优先级

application 主配置 > extensionConfigs > sharedConfigs

extensionConfigs/sharedConfigs 排在后面的数组比前面的优先级高

application 主逻辑 profile > 带后缀


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/5491.html