1.umi3源码解析之核心Service类初始化
2.图解+源码讲解 Nacos 客户端动态监听配置机制
umi3源码解析之核心Service类初始化
前言
umi是源码一个插件化的企业级前端应用框架,在开发中后台项目中应用颇广,源码确实带来了许多便利。源码借着这个契机,源码便有了我们接下来的源码“umi3源码解析”系列的分享,初衷很简单就是源码博彩源码搭建从源码层面上帮助大家深入认知umi这个框架,能够更得心应手的源码使用它,学习源码中的源码设计思想提升自身。该系列的源码大纲如下:
开辟鸿蒙,今天要解析的源码就是第一part,内容包括以下两个部分:
邂逅umi命令,源码看看umidev时都做了什么?
初遇插件化,源码了解源码中核心的源码Service类初始化的过程。
本次使用源码版本为?源码3.5.,地址放在这里了,源码接下来的每一块代码笔者都贴心的为大家注释了在源码中的位置,先clone再食用更香哟!
邂逅umi命令该部分在源码中的趋势打扮源码路径为:packages/umi
首先是第一部分umi命令,umi脚手架为我们提供了umi这个命令,当我们创建完一个umi项目并安装完相关依赖之后,通过yarnstart启动该项目时,执行的命令就是umidev
那么在umi命令运行期间都发生了什么呢,先让我们来看一下完整的流程,如下图:
接下来我们对其几个重点的步骤进行解析,首先就是对于我们在命令行输入的umi命令进行处理。
处理命令行参数//packages/umi/src/cli.tsconstargs=yParser(process.argv.slice(2),{ alias:{ version:['v'],help:['h'],},boolean:['version'],});if(args.version&&!args._[0]){ args._[0]='version';constlocal=existsSync(join(__dirname,'../.local'))?chalk.cyan('@local'):'';console.log(`umi@${ require('../package.json').version}${ local}`);}elseif(!args._[0]){ args._[0]='help';}解析命令行参数所使用的yParser方法是基于yargs-parser封装,该方法的两个入参分别是进程的可执行文件的绝对路径和正在执行的JS文件的路径。解析结果如下:
//输入umidev经yargs-parser解析后为://args={ //_:["dev"],//}在解析命令行参数后,对version和help参数进行了特殊处理:
如果args中有version字段,并且args._中没有值,将执行version命令,并从package.json中获得version的值并打印
如果没有version字段,args._中也没有值,将执行help命令
总的来说就是,如果只输入umi实际会执行umihelp展示umi命令的使用指南,如果输入umi--version会输出依赖的iapp电影源码版本,如果执行umidev那就是接下来的步骤了。
提问:您知道输入umi--versiondev会发什么吗?
运行umidev
//packages/umi/src/cli.tsconstchild=fork({ scriptPath:require.resolve('./forkedDev'),});process.on('SIGINT',()=>{ child.kill('SIGINT');process.exit(0);});//packages/umi/src/utils/fork.tsif(CURRENT_PORT){ process.env.PORT=CURRENT_PORT;}constchild=fork(scriptPath,process.argv.slice(2),{ execArgv});child.on('message',(data:any)=>{ consttype=(data&&data.type)||null;if(type==='RESTART'){ child.kill();start({ scriptPath});}elseif(type==='UPDATE_PORT'){ //setcurrentusedportCURRENT_PORT=data.portasnumber;}process.send?.(data);});本地开发时,大部分脚手架都会采用开启一个新的线程来启动项目,umi脚手架也是如此。这里的fork方法是基于node中child_process.fork()方法的封装,主要做了以下三件事:
确定端口号,使用命令行指定的端口号或默认的,如果该端口号已被占用则prot+=1
开启子进程,该子进程独立于父进程,两者之间建立IPC通信通道进行消息传递
处理通信,主要监听了RESTART重启和UPDATE_PORT更新端口号事件
接下来看一下在子进程中运行的forkedDev.ts都做了什么。
//packages/umi/src/forkedDev.ts(async()=>{ try{ //1、设置NODE_ENV为developmentprocess.env.NODE_ENV='development';//2、InitwebpackversiondeterminationandrequirehookinitWebpack();//3、实例化Service类,执行run方法constservice=newService({ cwd:getCwd(),//umi项目的根路径pkg:getPkg(process.cwd()),//项目的package.json文件的路径});awaitservice.run({ name:'dev',args,});//4、父子进程通信letclosed=false;process.once('SIGINT',应收应付源码()=>onSignal('SIGINT'));process.once('SIGQUIT',()=>onSignal('SIGQUIT'));process.once('SIGTERM',()=>onSignal('SIGTERM'));functiononSignal(signal:string){ if(closed)return;closed=true;//退出时触发插件中的onExit事件service.applyPlugins({ key:'onExit',type:service.ApplyPluginsType.event,args:{ signal,},});process.exit(0);}}catch(e:any){ process.exit(1);}})();设置process.env.NODE_ENV的值
initWebpack(接下来解析)
实例化Service并run(第二part的内容)
处理父子进程通信,当父进程监听到SIGINT、SIGTERM等终止进程的信号,也通知到子进程进行终止;子进程退出时触发插件中的onExit事件
initWebpack
//packages/umi/src/initWebpack.tsconsthaveWebpack5=(configContent.includes('webpack5:')&&!configContent.includes('//webpack5:')&&!configContent.includes('//webpack5:'))||(configContent.includes('mfsu:')&&!configContent.includes('//mfsu:')&&!configContent.includes('//mfsu:'));if(haveWebpack5||process.env.USE_WEBPACK_5){ process.env.USE_WEBPACK_5='1';init(true);}else{ init();}initRequreHook();这一步功能是检查用户配置确定初始化webpack的版本。读取默认配置文件.umirc和config/config中的配置,如果其中有webpack5或?mfsu等相关配置,umi就会使用webpack5进行初始化,否则就使用webpack4进行初始化。这里的mfsu是webpack5的模块联邦相关配置,umi在3.5版本时已经进行了支持。
初遇插件化该部分在源码中的路径为:packages/core/src/Service
说起umi框架,最先让人想到的就是插件化,这也是框架的核心,该部分实现的核心源码就是Service类,接下来我们就来看看Service类的实例化和init()的过程中发生了什么,可以称之为插件化实现的开端,该部分的大致流程如下
该流程图中前四步,都是网站源码洗钱在Service类实例化的过程中完成的,接下来让我们走进Service类。
Service类的实例化//packages/core/src/Service/Service.tsexportdefaultclassServiceextendsEventEmitter{ constructor(opts:IServiceOpts){ super();this.cwd=opts.cwd||process.cwd();//当前工作目录//repoDirshouldbetherootdirofrepothis.pkg=opts.pkg||this.resolvePackage();//package.jsonthis.env=opts.env||process.env.NODE_ENV;//环境变量//在解析config之前注册babelthis.babelRegister=newBabelRegister();//通过dotenv将环境变量中的变量从.env或.env.local文件加载到process.env中this.loadEnv();//1、getuserconfigconstconfigFiles=opts.configFiles;this.configInstance=newConfig({ cwd:this.cwd,service:this,localConfig:this.env==='development',configFiles});this.userConfig=this.configInstance.getUserConfig();//2、getpathsthis.paths=getPaths({ cwd:this.cwd,config:this.userConfig!,env:this.env,});//3、getpresetsandpluginsthis.initialPresets=resolvePresets({ ...baseOpts,presets:opts.presets||[],userConfigPresets:this.userConfig.presets||[],});this.initialPlugins=resolvePlugins({ ...baseOpts,plugins:opts.plugins||[],userConfigPlugins:this.userConfig.plugins||[],});}}Service类继承自EventEmitter用于实现自定义事件。在Service类实例化的过程中除了初始化成员变量外主要做了以下三件事:
1、解析配置文件
//packages/core/src/Config/Config.tsconstDEFAULT_CONFIG_FILES=[//默认配置文件'.umirc.ts','.umirc.js','config/config.ts','config/config.js',];//...if(Array.isArray(opts.configFiles)){ //配置的优先读取this.configFiles=lodash.uniq(opts.configFiles.concat(this.configFiles));}//...getUserConfig(){ //1、找到configFiles中的第一个文件constconfigFile=this.getConfigFile();this.configFile=configFile;//潜在问题:.local和.env的配置必须有configFile才有效if(configFile){ letenvConfigFile;if(process.env.UMI_ENV){ //1.根据UMI_ENV添加后缀eg:.umirc.ts-->.umirc.cloud.tsconstenvConfigFileName=this.addAffix(configFile,process.env.UMI_ENV,);//2.去掉后缀eg:.umirc.cloud.ts-->.umirc.cloudconstfileNameWithoutExt=envConfigFileName.replace(extname(envConfigFileName),'',);//3.找到该环境下对应的配置文件eg:.umirc.cloud.[ts|tsx|js|jsx]envConfigFile=getFile({ base:this.cwd,fileNameWithoutExt,type:'javascript',})?.filename;}constfiles=[configFile,//eg:.umirc.tsenvConfigFile,//eg:.umirc.cloud.tsthis.localConfig&&this.addAffix(configFile,'local'),//eg:.umirc.local.ts].filter((f):fisstring=>!!f).map((f)=>join(this.cwd,f))//转为绝对路径.filter((f)=>existsSync(f));//clearrequirecacheandsetbabelregisterconstrequireDeps=files.reduce((memo:string[],file)=>{ memo=memo.concat(parseRequireDeps(file));//递归解析依赖returnmemo;},[]);//删除对象中的键值require.cache[cachePath],下一次require将重新加载模块requireDeps.forEach(cleanRequireCache);this.service.babelRegister.setOnlyMap({ key:'config',value:requireDeps,});//requireconfigandmergereturnthis.mergeConfig(...this.requireConfigs(files));}else{ return{ };}}细品源码,可以看出umi读取配置文件的优先级:自定义配置文件?>.umirc>config/config,后续根据UMI_ENV尝试获取对应的配置文件,development模式下还会使用local配置,不同环境下的配置文件也是有优先级的
例如:.umirc.local.ts>.umirc.cloud.ts>.umirc.ts
由于配置文件中可能require其他配置,这里通过parseRequireDeps方法进行递归处理。在解析出所有的配置文件后,会通过cleanRequireCache方法清除requeire缓存,这样可以保证在接下来合并配置时的引入是实时的。
2、获取相关绝对路径
//packages/core/src/Service/getPaths.tsexportdefaultfunctiongetServicePaths({ cwd,config,env,}:{ cwd:string;config:any;env?:string;}):IServicePaths{ letabsSrcPath=cwd;if(isDirectoryAndExist(join(cwd,'src'))){ absSrcPath=join(cwd,'src');}constabsPagesPath=config.singular?join(absSrcPath,'page'):join(absSrcPath,'pages');consttmpDir=['.umi',env!=='development'&&env].filter(Boolean).join('-');returnnormalizeWithWinPath({ cwd,absNodeModulesPath:join(cwd,'node_modules'),absOutputPath:join(cwd,config.outputPath||'./dist'),absSrcPath,//srcabsPagesPath,//pagesabsTmpPath:join(absSrcPath,tmpDir),});}这一步主要获取项目目录结构中node_modules、dist、src、pages等文件夹的绝对路径。如果用户在配置文件中配置了singular为true,那么页面文件夹路径就是src/page,默认是src/pages
3、收集preset和plugin以对象形式描述
在umi中“万物皆插件”,preset是对于插件的描述,可以理解为“插件集”,是为了方便对插件的管理。例如:@umijs/preset-react就是一个针对react应用的插件集,其中包括了plugin-access权限管理、plugin-antdantdUI组件等。
//packages/core/src/Service/Service.tsthis.initialPresets=resolvePresets({ ...baseOpts,presets:opts.presets||[],userConfigPresets:this.userConfig.presets||[],});this.initialPlugins=resolvePlugins({ ...baseOpts,plugins:opts.plugins||[],userConfigPlugins:this.userConfig.plugins||[],});在收集preset和plugin时,首先调用了resolvePresets方法,其中做了以下处理:
3.1、调用getPluginsOrPresets方法,进一步收集preset和plugin并合并
//packages/core/src/Service/utils/pluginUtils.tsgetPluginsOrPresets(type:PluginType,opts:IOpts):string[]{ constupperCaseType=type.toUpperCase();return[//opts...((opts[type===PluginType.preset?'presets':'plugins']asany)||[]),//env...(process.env[`UMI_${ upperCaseType}S`]||'').split(',').filter(Boolean),//dependencies...Object.keys(opts.pkg.devDependencies||{ }).concat(Object.keys(opts.pkg.dependencies||{ })).filter(isPluginOrPreset.bind(null,type)),//userconfig...((opts[type===PluginType.preset?'userConfigPresets':'userConfigPlugins']asany)||[]),].map((path)=>{ returnresolve.sync(path,{ basedir:opts.cwd,extensions:['.js','.ts'],});});}这里可以看出收集preset和plugin的来源主要有四个:
实例化Service时的入参
process.env中指定的UMI_PRESETS或UMI_PLUGINS
package.json中dependencies和devDependencies配置的,需要命名规则符合?/^(@umijs\/|umi-)preset-/这个正则
解析配置文件中的,即入参中的userConfigPresets或userConfigPresets
3.2、调用pathToObj方法:将收集的plugin或preset以对象的形式输出
//输入umidev经yargs-parser解析后为://args={ //_:["dev"],//}0umi官网中提到过:每个插件都会对应一个id和一个key,id是路径的简写,key是进一步简化后用于配置的唯一值。便是在这一步进行的处理
形式如下:
//输入umidev经yargs-parser解析后为://args={ //_:["dev"],//}1思考:为什么要将插件以对象的形式进行描述?有什么好处?
执行run方法,初始化插件在Service类实例化完毕后,会立马调用run方法,run()执行的第一步就是执行init方法,init()方法的功能就是完成插件的初始化,主要操作如下:
遍历initialPresets并init
合并initpresets过程中得到的plugin和initialPlugins
遍历合并后的plugins并init
这里的initialPresets和initialPlugins就是上一步收集preset和plugin得到的结果,在这一步要对其逐一的init,接下来我们看一下init的过程中做了什么。
Initplugin
//输入umidev经yargs-parser解析后为://args={ //_:["dev"],//}2这段代码主要做了以下几件事情:
getPluginAPI方法:newPluginAPI时传入了Service实例,通过pluginAPI实例中的registerMethod方法将register方法添加到Service实例的pluginMethods中,后续返回pluginAPI的代理,以动态获取最新的register方法,以实现边注册边使用。
//输入umidev经yargs-parser解析后为:/图解+源码讲解 Nacos 客户端动态监听配置机制
图解+源码讲解 Nacos 客户端动态监听配置机制
在人生中第一要紧的是发现自己。为了这个目的,各位时常需要孤独和深思 —— 南森 Nacos 源码分析系列相关文章
从零开始看 Nacos 源码环境搭建
图解+源码讲解 Nacos 客户端发起注册流程
图解+源码讲解 Nacos 服务端处理注册请求逻辑
图解+源码讲解 Nacos 客户端下线流程
图解+源码讲解 Nacos 服务端处理下线请求
图解+源码讲解 Nacos 客户端发起心跳请求
图解+源码讲解 Nacos 服务端处理心跳请求
图解+源码讲解 Nacos 服务端处理配置获取请求
图解+源码讲解 Nacos 客户端动态监听配置机制
NacosConfigAutoConfiguration我们看到这里面其实注入了一个 Nacos 配置刷新的关键 NacosContextRefresherBean
@Configuration@ConditionalOnProperty(name?=?"spring.cloud.nacos.config.enabled",?matchIfMissing?=?true)public?class?NacosConfigAutoConfiguration?{ //?Nacos?配置属性@Beanpublic?NacosConfigProperties?nacosConfigProperties(ApplicationContext?context)?{ if?(context.getParent()?!=?null&&?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context.getParent(),?NacosConfigProperties.class).length?>?0)?{ return?BeanFactoryUtils.beanOfTypeIncludingAncestors(context.getParent(),NacosConfigProperties.class);}return?new?NacosConfigProperties();}//?Nacos?配置刷新属性@Beanpublic?NacosRefreshProperties?nacosRefreshProperties()?{ return?new?NacosRefreshProperties();}//?Nacos?刷新历史@Beanpublic?NacosRefreshHistory?nacosRefreshHistory()?{ return?new?NacosRefreshHistory();}//?Nacos?配置管理@Beanpublic?NacosConfigManager?nacosConfigManager(NacosConfigProperties?nacosConfigProperties)?{ return?new?NacosConfigManager(nacosConfigProperties);}//?Nacos?配置刷新@Beanpublic?NacosContextRefresher?nacosContextRefresher(NacosConfigManager?nacosConfigManager,NacosRefreshHistory?nacosRefreshHistory)?{ return?new?NacosContextRefresher(nacosConfigManager,?nacosRefreshHistory);}}NacosContextRefresher 配置中心刷新public?NacosContextRefresher(NacosConfigManager?nacosConfigManager,NacosRefreshHistory?refreshHistory)?{ //?获取配置属性信息this.nacosConfigProperties?=?nacosConfigManager.getNacosConfigProperties();//?刷新历史this.nacosRefreshHistory?=?refreshHistory;//?获取配置服务this.configService?=?nacosConfigManager.getConfigService();//?是否开启刷新,是truethis.isRefreshEnabled?=?this.nacosConfigProperties.isRefreshEnabled();}获取配置服务 getConfigServicenacosConfigManager.getConfigService(),这行代码其实就是为了创建 NcaosConfigService 对象,我们看看你是怎么创建的,其实核心代码就是通过 NacosFactory 反射创建的 NcaosConfigService 对象,这个对象是一个核心对象后续会讲到的
public?static?ConfigService?createConfigService(Properties?properties)?throws?NacosException?{ try?{ //?加载?NacosConfigService?类Class<?>?driverImplClass?=?Class.forName("com.alibaba.nacos.client.config.NacosConfigService");//?获取构造器Constructor?constructor?=?driverImplClass.getConstructor(Properties.class);//?创建实例ConfigService?vendorImpl?=?(ConfigService)?constructor.newInstance(properties);return?vendorImpl;}?catch?(Throwable?e)?{ throw?new?NacosException(NacosException.CLIENT_INVALID_PARAM,?e);}}监听器NacosContextRefresher 实现了 ApplicationListener ,一看这就是一个监听器了,我们看看这个在监听器里面做了什么操作
@Overridepublic?void?onApplicationEvent(ApplicationReadyEvent?event)?{ //?这是一个?CAS?操作,只设置一次if?(this.ready.compareAndSet(false,?true))?{ //?注册?Nacos?监听器对于应用this.registerNacosListenersForApplications();}}注册 Nacos 监听/**register Nacos Listeners. 注册Nacos监听器 */ private void registerNacosListenersForApplications() { // 默认是 true if (isRefreshEnabled()) { // 遍历Nacos属性资源中心 for (NacosPropertySource propertySource : NacosPropertySourceRepository .getAll()) { if (!propertySource.isRefreshable()) { continue; } // 获取资源ID ?String dataId = propertySource.getDataId(); // 通过组和 dataId 注册 Nacos 监听器 registerNacosListener(propertySource.getGroup(), dataId); } } }
private void registerNacosListener(final String groupKey, final String dataKey) { // 构建 Key 信息 String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey); // 在 listenerMap中放入了 key 对应 AbstractSharedListener 响应的方法 Listener listener = listenerMap.computeIfAbsent(key, lst -> new AbstractSharedListener() { @Override public void innerReceive(String dataId, String group, String configInfo) { // 刷新次数 refreshCountIncrement(); // 记录刷新历史,就是改变历史 nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo); // 发布刷新事件 applicationContext.publishEvent( new RefreshEvent(this, null, "Refresh Nacos config")); } }); // 向配置服务中添加监听器 configService.addListener(dataKey, groupKey, listener);
}
####?向配置服务中添加监听器 此时调用的是?NacosConfigService?中的?addListener?方法,但是最终执行的还是?ClientWorker?中的?addTenantListeners?方法,后面会进行分析?ClientWorker?这个类的```java@Overridepublic?void?addListener(String?dataId,?String?group,?Listener?listener)?throws?NacosException?{ //?这个?ClientWorker?worker?也是一个核心类worker.addTenantListeners(dataId,?group,?Arrays.asList(listener));}构建 CacheData 信息此时向 ClientWorker 中的 CacheData 中添加数据,之后遍历监听器添加到 CacheData 中
创建 CacheData 对象public?CacheData(ConfigFilterChainManager?configFilterChainManager,?String?name,?String?dataId,?String?group,String?tenant)?{ //?dataId?不能为空if?(null?==?dataId?||?null?==?group)?{ throw?new?IllegalArgumentException("dataId="?+?dataId?+?",?group="?+?group);}this.name?=?name;this.configFilterChainManager?=?configFilterChainManager;this.dataId?=?dataId;?//?设置dataIdthis.group?=?group;?//?设置组信息this.tenant?=?tenant;?//?设置租户listeners?=?new?CopyOnWriteArrayList<ManagerListenerWrap>();?//?装饰器集合this.isInitializing?=?true;//?加载缓存数据从本地磁盘this.content?=?loadCacheContentFromDiskLocal(name,?dataId,?group,?tenant);//?计算本地缓存信息的MD5this.md5?=?getMd5String(content);}向 CacheData 中添加数据public?void?addTenantListeners(String?dataId,?String?group,List<?extends?Listener>?listeners)throws?NacosException?{ //?DefaultGroupgroup?=?null2defaultGroup(group);String?tenant?=?agent.getTenant();?//?是?""//?向缓存数据中添加监听器CacheData?cache?=?addCacheDataIfAbsent(dataId,?group,?tenant);for?(Listener?listener?:?listeners)?{ cache.addListener(listener);}}public?CacheData?addCacheDataIfAbsent(String?dataId,?String?group,?String?tenant)throws?NacosException?{ //?获取Key信息String?key?=?GroupKey.getKeyTenant(dataId,?group,?tenant);//?在缓存?Map?中获取缓存数据CacheData?cacheData?=?cacheMap.get(key);//?如果不为空的情况下那么就返回,如果为空那么就创建一个?CacheDataif?(cacheData?!=?null)?{ return?cacheData;}//?创建一个?CacheData?cacheData?=?new?CacheData(configFilterChainManager,?agent.getName(),dataId,?group,?tenant);//?将创建好的?cacheData?放入缓存?Map?中CacheData?lastCacheData?=?cacheMap.putIfAbsent(key,?cacheData);//?如果缓存数据为空的话那么从配置中心拉取,不过此时不为空if?(lastCacheData?==?null)?{ //fix?issue?#?if?(enableRemoteSyncConfig)?{ String[]?ct?=?getServerConfig(dataId,?group,?tenant,?L);cacheData.setContent(ct[0]);}//?计算任务IDint?taskId?=?cacheMap.size()?/?(int)?ParamUtil.getPerTaskConfigSize();//?设置任务IDcacheData.setTaskId(taskId);lastCacheData?=?cacheData;}//?缓存数据初始化完成//?reset?so?that?server?not?hang?this?checklastCacheData.setInitializing(true);LOGGER.info("[{ }]?[subscribe]?{ }",?agent.getName(),?key);MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.size());//?返回最新的缓存数据return?lastCacheData;}到这里 CacheData 对象 和 cacheMap 集合已经构建完成了,后续会用到这个数据的
NacosConfigService 分析NacosConfigService这个类在创建的时候主要做了什么事情,这这里面创建了一个 ClientWorker对象,这个对象是一个核心的类,有关于配置的一些操作都是归功于 ClientWorker类
public?NacosConfigService(Properties?properties)?throws?NacosException?{ ......this.agent?=?new?MetricsHttpAgent(new?ServerHttpAgent(properties));this.agent.start();//?核心工作类this.worker?=?new?ClientWorker(this.agent,this.configFilterChainManager,?properties);}核心配置类 ClientWorker分析一下这个类都在做什么事情,都有哪些核心方法 其实能看到里面有一个构造函数、添加缓存数据、添加监听器、检查配置中心相关方法、获取服务配置、解析数据响应、移除缓存数据、删除监听器以及 shutdown方法
构造函数看到这里其实看到了定义了两个调度线程池,一个是用于配置检测的,一个是用于执行长轮询服务的
@SuppressWarnings("PMD.ThreadPoolCreationRule")public?ClientWorker(final?HttpAgent?agent,final?ConfigFilterChainManager?configFilterChainManager,?final?Properties?properties){ this.agent?=?agent;this.configFilterChainManager?=?configFilterChainManager;//?初始化操作init(properties);//?定义一个调度线程池,只有一个线程还是守护线程this.executor?=?Executors.newScheduledThreadPool(1,?new?ThreadFactory()?{ @Overridepublic?Thread?newThread(Runnable?r)?{ Thread?t?=?new?Thread(r);t.setName("com.alibaba.nacos.client.Worker."?+?agent.getName());t.setDaemon(true);return?t;}});//?定义一个多个线程的调度线程池,线程个数和CPU?核心数有关,也是守护线程,是一个长轮询this.executorService?=?Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(),?new?ThreadFactory()?{ @Overridepublic?Thread?newThread(Runnable?r)?{ Thread?t?=?new?Thread(r);t.setName("com.alibaba.nacos.client.Worker.longPolling."?+agent.getName());t.setDaemon(true);return?t;}});//?定义一个定时的调度任务,第一次执行的时候延时1毫秒,后续毫秒调度一次this.executor.scheduleWithFixedDelay(new?Runnable()?{ @Overridepublic?void?run()?{ try?{ //?检查配置信息方法checkConfigInfo();}?catch?(Throwable?e)?{ LOGGER.error("["?+?agent.getName()?+?"]?"+?"[sub-check]?rotate?check?error",?e);}}},?1L,?L,?TimeUnit.MILLISECONDS);}检查配置服务方法这个 cacheMap 包含了一些任务信息,这里面的任务是怎么来的呢,他是在添加监听器的时候添加的,上面已经分析过了
public?NacosContextRefresher(NacosConfigManager?nacosConfigManager,NacosRefreshHistory?refreshHistory)?{ //?获取配置属性信息this.nacosConfigProperties?=?nacosConfigManager.getNacosConfigProperties();//?刷新历史this.nacosRefreshHistory?=?refreshHistory;//?获取配置服务this.configService?=?nacosConfigManager.getConfigService();//?是否开启刷新,是truethis.isRefreshEnabled?=?this.nacosConfigProperties.isRefreshEnabled();}0长轮询任务 LongPollingRunnable2024-12-27 17:04
2024-12-27 17:04
2024-12-27 16:54
2024-12-27 16:41
2024-12-27 16:25
2024-12-27 15:28
2024-12-27 14:40
2024-12-27 14:40