Spring Cloud Nacos配置中心实现原理

环境:Springboot2.3.12.RELEASE + Spring Cloud Alibaba2.2.5.RELEASE + Spring Cloud Hoxton.SR12

应用的核心技术是:自定义PropertySourceLocator,然后配置spring.factories

在如下包中配置:

spring-cloud-context-xxx.jar中

org.springframework.cloud.bootstrap.BootstrapConfiguration=org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration

Nacos加载配置的时候默认会通过一下三个DataId加载数据:

核心类:NacosPropertySourceLocator

  • dataId=nacos-config, tenant(namespace)=7205e694-ac51-4ac1-bbe9-87c28689b88a, group=HisGroup
  • loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup, fileExtension, true);
  • dataId=nacos-config.properties, tenant=7205e694-ac51-4ac1-bbe9-87c28689b88a, group=HisGroup
  • loadNacosDataIfPresent(compositePropertySource, dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
  • 通过定义的profile
  • private static final String NACOS_PROPERTY_SOURCE_NAME = “NACOS”;private static final String SEP1 = “-“;private static final String DOT = “.”;for (String profile : environment.getActiveProfiles()) { String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension; loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup, fileExtension, true);}
  • 自动配置核心

    @Configuration(proxyBeanMethods = false)@ConditionalOnProperty(name = “spring.cloud.nacos.config.enabled”, matchIfMissing = true)public class NacosConfigBootstrapConfiguration { // Nacos Config相关的配置属性 @Bean @ConditionalOnMissingBean public NacosConfigProperties nacosConfigProperties() { return new NacosConfigProperties(); } // 管理Nacos Config相关的服务 @Bean @ConditionalOnMissingBean public NacosConfigManager nacosConfigManager( NacosConfigProperties nacosConfigProperties) { return new NacosConfigManager(nacosConfigProperties); } // 核心类自定义启动加载配置文件(Bootstraps) @Bean public NacosPropertySourceLocator nacosPropertySourceLocator( NacosConfigManager nacosConfigManager) { return new NacosPropertySourceLocator(nacosConfigManager); }}org.springframework.cloud.bootstrap.BootstrapConfiguration=com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration

    自定义属性源

    public class NacosPropertySourceLocator implements PropertySourceLocator { private NacosPropertySourceBuilder nacosPropertySourceBuilder; private NacosConfigProperties nacosConfigProperties; private NacosConfigManager nacosConfigManager; public NacosPropertySourceLocator(NacosConfigManager nacosConfigManager) { this.nacosConfigManager = nacosConfigManager; this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties(); } public PropertySource locate(Environment env) { nacosConfigProperties.setEnvironment(env); // 关键通过NacosConfigManager获取ConfigService服务 ConfigService configService = nacosConfigManager.getConfigService(); // … long timeout = nacosConfigProperties.getTimeout(); nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout); // 下面这些设置就是对应从配置文件中获取 String name = nacosConfigProperties.getName(); String dataIdPrefix = nacosConfigProperties.getPrefix(); if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = name; } if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = env.getProperty(“spring.application.name”); } CompositePropertySource composite = new CompositePropertySource( NACOS_PROPERTY_SOURCE_NAME);// NACOS_PROPERTY_SOURCE_NAME = NACOS // 加载共享配置 loadSharedConfiguration(composite); // 加载扩展配置 loadExtConfiguration(composite); // 加载应用程序配置(这里就以应用程序配置,深入查看) loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env); return composite; }}

    获取配置服务

    ConfigService配置服务是获取配置信息的核心方法:

    public interface ConfigService { // 获取配置 String getConfig(String dataId, String group, long timeoutMs) throws NacosException; // 获取配置并设置监听 String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException; // 给配置添加监听 void addListener(String dataId, String group, Listener listener) throws NacosException; // 发布配置 boolean publishConfig(String dataId, String group, String content) throws NacosException; // 删除配置 boolean removeConfig(String dataId, String group) throws NacosException; // 删除监听 void removeListener(String dataId, String group, Listener listener); // 获取服务状态 String getServerStatus(); // 关闭资源服务 void shutDown() throws NacosException;}

    NacosConfigManager

    public class NacosConfigManager { private static ConfigService service = null; private NacosConfigProperties nacosConfigProperties; public NacosConfigManager(NacosConfigProperties nacosConfigProperties) { this.nacosConfigProperties = nacosConfigProperties; // 创建配置服务 createConfigService(nacosConfigProperties); } static ConfigService createConfigService( NacosConfigProperties nacosConfigProperties) { if (Objects.isNull(service)) { synchronized (NacosConfigManager.class) { try { if (Objects.isNull(service)) { // 通过工厂创建服务 // assembleConfigServiceProperties方法 // 就是收集关于Nacos所有配置信息,如:地址,端口,用户名,密码等 // 详细查看NacosConfigProperties#assembleConfigServiceProperties service = NacosFactory.createConfigService( nacosConfigProperties.assembleConfigServiceProperties()); } } // … } } return service; }}// NacosFactorypublic class NacosFactory { public static ConfigService createConfigService(Properties properties) throws NacosException { return ConfigFactory.createConfigService(properties); }}public class ConfigFactory { public static ConfigService createConfigService(Properties properties) throws NacosException { try { Class driverImplClass = Class.forName(“com.alibaba.nacos.client.config.NacosConfigService”); Constructor constructor = driverImplClass.getConstructor(Properties.class); ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties); // 通过反射构造了NacosConfigservice,同时设置属性信息 // 这些属性信息就是一些Naocs服务的地址,端口,用户名,密码等信息 return vendorImpl; } catch (Throwable e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); } }}

    到此就得到了一个ConfigService服务类NacosConfigService。

    获取应用程序配置

    接着95.2获取到了ConfigService以后继续执行

    public class NacosPropertySourceLocator implements PropertySourceLocator { private NacosPropertySourceBuilder nacosPropertySourceBuilder; public PropertySource locate(Environment env) { nacosConfigProperties.setEnvironment(env); // 关键通过NacosConfigManager获取ConfigService服务 ConfigService configService = nacosConfigManager.getConfigService(); // … nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout); CompositePropertySource composite = new CompositePropertySource( NACOS_PROPERTY_SOURCE_NAME); loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env); return composite; } private void loadApplicationConfiguration( CompositePropertySource compositePropertySource, String dataIdPrefix, NacosConfigProperties properties, Environment environment) { // 获取文件扩展 String fileExtension = properties.getFileExtension(); // 获取分组 String nacosGroup = properties.getGroup(); // … // load with suffix, which have a higher priority than the default // 这里就以这里为例 loadNacosDataIfPresent(compositePropertySource, dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true); // Loaded with profile, which have a higher priority than the suffix // 这里会根据不同配置的profiles再次加载不同环境的配置 for (String profile : environment.getActiveProfiles()) { String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension; loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup, fileExtension, true); } } private void loadNacosDataIfPresent(final CompositePropertySource composite, final String dataId, final String group, String fileExtension, boolean isRefreshable) { if (null == dataId || dataId.trim().length() < 1) { return; } if (null == group || group.trim().length() < 1) { return; } // 加载Nacos属性源 NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group, fileExtension, isRefreshable); this.addFirstPropertySource(composite, propertySource, false); } private NacosPropertySource loadNacosPropertySource(final String dataId, final String group, String fileExtension, boolean isRefreshable) { // … return nacosPropertySourceBuilder.build(dataId, group, fileExtension, isRefreshable); } private void addFirstPropertySource(final CompositePropertySource composite, NacosPropertySource nacosPropertySource, boolean ignoreEmpty) { if (null == nacosPropertySource || null == composite) { return; } if (ignoreEmpty && nacosPropertySource.getSource().isEmpty()) { return; } composite.addFirstPropertySource(nacosPropertySource); }}// 这里面开始加载数据public class NacosPropertySourceBuilder { NacosPropertySource build(String dataId, String group, String fileExtension, boolean isRefreshable) { Map p = loadNacosData(dataId, group, fileExtension); NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId, p, new Date(), isRefreshable); NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource); return nacosPropertySource; } // 加载数据 private Map loadNacosData(String dataId, String group, String fileExtension) { String data = null; try { // 通过ConfigService加载配置内容(从远程服务获取) data = configService.getConfig(dataId, group, timeout); if (StringUtils.isEmpty(data)) { return EMPTY_MAP; } Map dataMap = NacosDataParserHandler.getInstance() .parseNacosData(data, fileExtension); return dataMap == null ? EMPTY_MAP : dataMap; } // … return EMPTY_MAP; }}public class NacosConfigService implements ConfigService { public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { return getConfigInner(namespace, dataId, group, timeoutMs); } private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException { group = null2defaultGroup(group); ParamUtils.checkKeyParam(dataId, group); ConfigResponse cr = new ConfigResponse(); cr.setDataId(dataId); cr.setTenant(tenant); cr.setGroup(group); // 这里会从缓存文件中获取,如果能获取就不会再从远程加载了 // 会从如下缓存目录下加载配置: // System.getProperty("JM.SNAPSHOT.PATH", // System.getProperty("user.home")) + File.separator + "nacos" // + File.separator + "config" String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant); if (content != null) { cr.setContent(content); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content; } try { // 缓存总无法获取则从远程服务上拉取数据 String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs); cr.setContent(ct[0]); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content; } catch (NacosException ioe) { if (NacosException.NO_RIGHT == ioe.getErrCode()) { throw ioe; } } content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant); cr.setContent(content); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content; }}public class ClientWorker implements Closeable { // 这里就是从远程服务拉取配置 public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout) throws NacosException { String[] ct = new String[2]; if (StringUtils.isBlank(group)) { group = Constants.DEFAULT_GROUP; } HttpRestResult result = null; try { Map params = new HashMap(3); if (StringUtils.isBlank(tenant)) { params.put("dataId", dataId); params.put("group", group); } else { params.put("dataId", dataId); params.put("group", group); params.put("tenant", tenant); } result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout); } catch (Exception ex) { throw new NacosException(NacosException.SERVER_ERROR, ex); } switch (result.getCode()) { case HttpURLConnection.HTTP_OK: // 获取成功后会将数据保存到缓存目录下 LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.getData()); ct[0] = result.getData(); if (result.getHeader().getValue(CONFIG_TYPE) != null) { ct[1] = result.getHeader().getValue(CONFIG_TYPE); } else { ct[1] = ConfigType.TEXT.getType(); } return ct; case HttpURLConnection.HTTP_NOT_FOUND: LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null); return ct; case HttpURLConnection.HTTP_CONFLICT: { throw new NacosException(NacosException.CONFLICT, "data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } throw new NacosException(result.getCode(), result.getMessage()); } default: { throw new NacosException(result.getCode(), "http error, code=" + result.getCode() + ",dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } } }}

    到此就完成了远程配置数据的加载

    总结:Nacos Config先从本地缓存中获取数据,如果不能获取才从远程拉取(如果远程服务无法连接,那么会不断重试,前提是本地能够加载到数据,否则服务将无法正常启动)。

    完毕!!!

    Spring MVC 异步请求方式 Spring中的@Configuration注解你真的了解吗? Spring Retry重试框架的应用 Spring容器这些扩展点你都清楚了吗? Spring MVC 异常处理方式 SpringBoot WebFlux整合Spring Security进行权限认证 Spring 自定义Advisor以编程的方式实现AOP

    郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
    用户投稿
    上一篇 2022年6月13日 21:14
    下一篇 2022年6月13日 21:14

    相关推荐

    • 计算机网络技术论文(计算机网络技术论文七千字)

      今天小编给各位分享计算机网络技术论文的知识,其中也会对计算机网络技术论文七千字进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧! 计算机网络方面的论文3000字…

      2022年11月26日
    • 《宝可梦朱紫》獒教父属性是什么?獒教父属性一览

      宝可梦朱紫里獒教父是一只很强的宝可梦,很多玩家不清楚獒教父的属性是什么样的,下面就给大家带来宝可梦朱紫獒教父属性一览,感兴趣的小伙伴一起来看看吧,希望能帮助到大家。 獒教父属性一览…

      2022年11月25日
    • 《宝可梦朱紫》太晶化强力宝可梦推荐 太晶化哪些宝可梦最强?

      宝可梦朱紫游戏中宝可梦种类繁多,不过有的宝可梦比较强,有的稍弱一些,那么太晶化化哪些宝可梦最强呢,为了便于大家更好的体验游戏,这里给大家带来了宝可梦朱紫太晶化强力宝可梦推荐,一起来…

      2022年11月25日
    • 宝可梦朱紫四大天王属性怎么样 四大天王属性数值介绍

      宝可梦朱紫四大天王属性如何?四大天王的属性数值玩家们还是挺好奇的,想要了解四大天王属性的可以看看下面小编的介绍,小编会把四大天王的属性数值全都分享在下面,各位赶紧来小编这里多了解一…

      2022年11月25日
    • 宝可梦朱紫皮卡丘多少级进化 皮卡丘进化条件攻略

      宝可梦朱紫皮卡丘进化条件是什么?皮卡丘怎么进化?作为最受欢迎的电属性宝可梦,大家都很想知道皮卡丘进化的方法,今天小编这就在下面的攻略中分享皮卡丘进化条件,各位可以赶紧来小编这里了解…

      2022年11月25日
    • 什么是推广cpa一篇文章带你看懂CPA推广渠道

      CPA渠道 CPA指的是按照指定的行为结算,可以是搜索,可以是注册,可以是激活,可以是搜索下载激活,可以是绑卡,实名认证,可以是付费,可以是浏览等等。甲乙双方可以根据自己的情况来定…

      2022年11月25日
    • 沈阳茂业中心

      由深圳赢商网茂业集团与上海建工集团联袂打造的沈阳金廊第一高楼沈阳茂业中心于2011年11月8日顺利实现主塔楼结构封顶至此,历经四年,沈阳茂业中心以31095m的高度雄踞东北超高层建…

      2022年11月25日
    • 抖音直播带货有哪些方法技巧(抖音直播带货有哪些痛点)

      如今抖音这个短视频的变现能力越来越突显了,尤其是在平台上开通直播,更具有超强的带货属性,已经有越来越多的普通人加入到其中了。不过直播带货虽然很火,但是也不是每个人都能做好的,那么在…

      2022年11月24日
    • 《原神》3.2无相交响诗第一天无相之冰怎么打?无相交响诗攻略

      原神3.2无相交响诗第一天无相之冰怎么打?最近新版本3.2版本的无相交响诗活动又开启了,不少玩家还不清楚具体的玩法,下面一起来看一下原神被隐去的原神3.2无相交响诗第一天无相之冰打…

      2022年11月24日
    • 《宝可梦朱紫》四天王属性是什么?四天王属性阵容一览

      宝可梦朱紫中玩家可以挑战四天王,很多玩家想知道宝可梦朱紫四天王属性是什么,有什么阵容?下面就带来宝可梦朱紫四天王属性阵容一览,感兴趣的小伙伴不要错过,希望能帮助到大家。 四天王属性…

      2022年11月24日

    联系我们

    联系邮箱:admin#wlmqw.com
    工作时间:周一至周五,10:30-18:30,节假日休息