9Nacos配置中心之加載配置客戶端和服務端的處理

      網友投稿 820 2025-04-01

      9Nacos配置中心之加載配置客戶端和服務端的處理


      一 客戶端配置中心之加載配置

      prepareContext()方法

      applyInitializers()方法

      PropertySourceBootstrapConfiguration的initialize()方法

      NacosPropertySourceLocator的locate()方法

      loadNacosData()方法

      ClientWork的getServerConfig()方法

      9Nacos配置中心之加載配置客戶端和服務端的處理

      一 客戶端配置中心之加載配置

      prepareContext()方法

      applyInitializers()方法

      PropertySourceBootstrapConfiguration的initialize()方法

      NacosPropertySourceLocator的locate()方法

      loadNacosData()方法

      ClientWork的getServerConfig()方法

      二 服務端/v1/cs/configs接口的處理

      總結

      9Nacos配置中心之加載配置客戶端和服務端的處理

      一 客戶端配置中心之加載配置

      我們接著上個文章說的,Springboot啟動的時候調用prep在reEnvironment()進行環境準備,

      9Nacos配置中心之加載配置客戶端和服務端的處理

      prepareEnvironment()進行環境準備,在啟動類執行完prepareEnvironment后,執行prepareContext進行刷新應用上下文件的準備

      代碼如下:

      public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //環境準備 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }

      我們看一下prepareContext()準備上下文方法做了什么

      prepareContext()方法

      private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources Set sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }

      調用applyInitializers()方法

      注冊打印banner圖的單例bean

      加載資源

      我們再看一下applyInitializers()方法是做什么的

      applyInitializers()方法

      @SuppressWarnings({ "rawtypes", "unchecked" }) 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); } }

      PropertySourceBootstrapConfiguration實現了ApplicationContextInitializer接口,所以會調用PropertySourceBootstrapConfiguration的initialize()方法

      PropertySourceBootstrapConfiguration的initialize()方法

      @Override public void initialize(ConfigurableApplicationContext applicationContext) { CompositePropertySource composite = new CompositePropertySource( BOOTSTRAP_PROPERTY_SOURCE_NAME); AnnotationAwareOrderComparator.sort(this.propertySourceLocators); boolean empty = true; ConfigurableEnvironment environment = applicationContext.getEnvironment(); for (PropertySourceLocator locator : this.propertySourceLocators) { PropertySource source = null; //加載 source = locator.locate(environment); if (source == null) { continue; } logger.info("Located property source: " + source); composite.addPropertySource(source); empty = false; } if (!empty) { MutablePropertySources propertySources = environment.getPropertySources(); String logConfig = environment.resolvePlaceholders("${logging.config:}"); LogFile logFile = LogFile.get(environment); if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); } insertPropertySources(propertySources, composite); reinitializeLoggingSystem(environment, logConfig, logFile); setLogLevels(applicationContext, environment); handleIncludedProfiles(environment); } }

      locator.locate(environment)方法會調用NacosPropertySourceLocator的locate方法,這就是加載配置的關鍵代碼了

      NacosPropertySourceLocator的locate()方法

      @Override public PropertySource locate(Environment env) { ConfigService configService = nacosConfigProperties.configServiceInstance(); 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; } if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = env.getProperty("spring.application.name"); } CompositePropertySource composite = new CompositePropertySource( NACOS_PROPERTY_SOURCE_NAME); loadSharedConfiguration(composite); loadExtConfiguration(composite); loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env); return composite; }

      初始化ConfigService對象,ConfigService是Nacos客戶端提供的用于訪問實現配置中心基本操作的類

      如果為空打印支持沒有ConfigService實例,不能加載配置,返回空

      如果不為空,按照順序分別加載共享配置、擴展配置、應用名稱對應的配置。

      進入loadApplicationConfiguration-》loadNacosDataIfPresent-》loadNacosPropertySource-》build-》loadNacosData

      loadNacosData()方法

      private Properties loadNacosData(String dataId, String group, String fileExtension) { String data = null; try { data = configService.getConfig(dataId, group, timeout); if (StringUtils.isEmpty(data)) { log.warn( "Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]", dataId, group); return EMPTY_PROPERTIES; } log.info(String.format( "Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId, group, data)); Properties properties = NacosDataParserHandler.getInstance() .parseNacosData(data, fileExtension); return properties == null ? EMPTY_PROPERTIES : properties; } catch (NacosException e) { log.error("get data from Nacos error,dataId:{}, ", dataId, e); } catch (Exception e) { log.error("parse data from Nacos error,dataId:{},data:{},", dataId, data, e); } return EMPTY_PROPERTIES; }

      configService.getConfig方法從Nacos配置中心上加載配置進行填充

      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); // 優先使用本地配置 String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant); if (content != null) { LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", agent.getName(), dataId, group, tenant, ContentUtils.truncateContent(content)); cr.setContent(content); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content; } try { content = worker.getServerConfig(dataId, group, tenant, timeoutMs); cr.setContent(content); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content; } catch (NacosException ioe) { if (NacosException.NO_RIGHT == ioe.getErrCode()) { throw ioe; } LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", agent.getName(), dataId, group, tenant, ioe.toString()); } LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", agent.getName(), dataId, group, tenant, ContentUtils.truncateContent(content)); content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant); cr.setContent(content); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content; }

      方法中優先加載本地的配置,如果不為空就返回結果,否則這里又會調用ClientWork的getServerConfig()方法獲取內容然后返回結果

      ClientWork的getServerConfig()方法

      public String getServerConfig(String dataId, String group, String tenant, long readTimeout) throws NacosException { if (StringUtils.isBlank(group)) { group = Constants.DEFAULT_GROUP; } HttpResult result = null; try { List params = null; if (StringUtils.isBlank(tenant)) { params = Arrays.asList("dataId", dataId, "group", group); } else { params = Arrays.asList("dataId", dataId, "group", group, "tenant", tenant); } //發起請求 result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout); } catch (IOException e) { String message = String.format( "[%s] [sub-server] get server config exception, dataId=%s, group=%s, tenant=%s", agent.getName(), dataId, group, tenant); LOGGER.error(message, e); throw new NacosException(NacosException.SERVER_ERROR, e); } switch (result.code) { case HttpURLConnection.HTTP_OK: LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content); return result.content; case HttpURLConnection.HTTP_NOT_FOUND: LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null); return null; case HttpURLConnection.HTTP_CONFLICT: { LOGGER.error( "[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, " + "tenant={}", agent.getName(), dataId, group, tenant); throw new NacosException(NacosException.CONFLICT, "data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } case HttpURLConnection.HTTP_FORBIDDEN: { LOGGER.error("[{}] [sub-server-error] no right, dataId={}, group={}, tenant={}", agent.getName(), dataId, group, tenant); throw new NacosException(result.code, result.content); } default: { LOGGER.error("[{}] [sub-server-error] dataId={}, group={}, tenant={}, code={}", agent.getName(), dataId, group, tenant, result.code); throw new NacosException(result.code, "http error, code=" + result.code + ",dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } } }

      現在水落石出了,這里調用客戶端向服務端發送請求,agent.httpGet()發起請求,請求路徑:/v1/cs/configs

      二 服務端/v1/cs/configs接口的處理

      服務端在nacos.config包下,ConfigController類的getConfig()方法

      @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) public void getConfig(HttpServletRequest request, HttpServletResponse response, @RequestParam("dataId") String dataId, @RequestParam("group") String group, @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, @RequestParam(value = "tag", required = false) String tag) throws IOException, ServletException, NacosException { // check tenant ParamUtils.checkTenant(tenant); tenant = NamespaceUtil.processNamespaceParameter(tenant); // check params ParamUtils.checkParam(dataId, group, "datumId", "content"); ParamUtils.checkParam(tag); final String clientIp = RequestUtil.getRemoteIp(request); String isNotify = request.getHeader("notify"); inner.doGetConfig(request, response, dataId, group, tenant, tag, isNotify, clientIp); }

      調用了ConfigServletInner的doGetConfig()方法,調用tryConfigReadLock()方法加入讀鎖,查看緩存中有沒有nacos中配置的key 有的話就加鎖成功了,加鎖成功后調用ConfigCacheService.getContentCache()獲取CacheItem實例,然后判斷,然后根據配置信息等選擇從數據庫取數據還是獲取本地文件,然后返回文件內容返回給客戶端

      總結

      啟動類執行完prepareEnvironment后,執行prepareContext進行刷新應用上下文件的準備,調用applyInitializers,調用NacosPropertySourceLocator的locate方法,初始化ConfigService對象,按照順序分別加載共享配置、擴展配置、應用名稱對應的配置,進入loadNacosData方法,然后configService.getConfig方法從Nacos配置中心上加載配置進行填充。

      這就是nacos初始化的大體流程,如果我們在工作中遇到獲取nacos數據獲取不到的時候,我們可以試著跟蹤一下nacos加載數據的流程,分析問題,定位問題,及時解決。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:2020年您需要了解的5種CRM統計數據
      下一篇:WPS表格如何快速隱藏不使用的區域(wps表格窗口怎么會隱藏)
      相關文章
      www亚洲精品久久久乳| 亚洲中文字幕一二三四区苍井空| 亚洲字幕AV一区二区三区四区 | 国产成人精品久久亚洲高清不卡 | 亚洲最大福利视频| 2020年亚洲天天爽天天噜| 亚洲欧洲高清有无| 久久精品国产亚洲AV无码偷窥| 久久精品亚洲中文字幕无码网站| 国产AV无码专区亚洲AV毛网站 | 亚洲永久在线观看| 中文字幕在线日亚洲9| 亚洲色无码专区一区| 亚洲中文字幕乱码AV波多JI| 亚洲中文字幕久久精品无码VA| 亚洲日韩一区二区一无码| 亚洲国产乱码最新视频| 亚洲色欲啪啪久久WWW综合网| 99999久久久久久亚洲| 国产精品高清视亚洲一区二区 | 亚洲日本中文字幕天天更新| 亚洲三级高清免费| 亚洲精品乱码久久久久久蜜桃图片| 亚洲精品中文字幕| 国产亚洲欧美日韩亚洲中文色| 国产亚洲精品免费| 亚洲一级黄色视频| 亚洲精品白浆高清久久久久久| 亚洲精品乱码久久久久久| 久久精品国产精品亚洲蜜月| 噜噜噜亚洲色成人网站∨| 亚洲免费电影网站| 成人亚洲国产va天堂| 亚洲国产成人AV网站| 亚洲午夜av影院| 亚洲AV无码一区东京热久久| 亚洲精品国产成人中文| 亚洲偷自精品三十六区| 18禁亚洲深夜福利人口| 久久乐国产精品亚洲综合| 亚洲成AV人在线观看天堂无码|