Nacos配置中心源码解析

Nacos配置中心源码解析

1、nacos是如何把值赋值@Value注解的字段的?

在这里插入图片描述

  1. spring中配置是由org.springframework.core.env.Environment进行管理的,并赋值到@Value注解的字段上去的。具体可参考https://blog.csdn.net/f641385712/article/details/91043955

  2. Nacos是怎么从远程把属性放在Environment里的呢?
    在这里插入图片描述
    .png)]

从上面的调用图可以看到spring启动的时候会调用com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#locate方法

@Override
public PropertySource<?> locate(Environment env) {
	//通过配置信息构建ConfigService
    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);
    //组装dataId前缀开始,如果spring.cloud.nacos.config.name为空,则获取spring.application.name做前缀
    String name = nacosConfigProperties.getName();

    String dataIdPrefix = nacosConfigProperties.getPrefix();
    if (StringUtils.isEmpty(dataIdPrefix)) {
        dataIdPrefix = name;
    }

    if (StringUtils.isEmpty(dataIdPrefix)) {
        dataIdPrefix = env.getProperty("spring.application.name");
    }
	//组装dataId前缀结束
    CompositePropertySource composite = new CompositePropertySource(
        NACOS_PROPERTY_SOURCE_NAME);
	//获取共享的dataId的配置,即spring.cloud.nacos.config.shared-dataids的配置
    loadSharedConfiguration(composite);
    //获取额外的dataId的配置,spring.cloud.nacos.config.ext-config集合下的配置
    loadExtConfiguration(composite);
    //获取当前程序的配置
    loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);

    return composite;
}

上述代码中的loadSharedConfiguration、loadExtConfiguration、loadApplicationConfiguration都会调用com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosDataIfPresent方法

private void loadNacosDataIfPresent(final CompositePropertySource composite,
                                    final String dataId, final String group, String fileExtension,
                                    boolean isRefreshable) {
    //如果配置已经刷新过
    if (NacosContextRefresher.getRefreshCount() != 0) {
        NacosPropertySource ps;
        //如果不刷新配置,则从内存中取配置
        if (!isRefreshable) {
            ps = NacosPropertySourceRepository.getNacosPropertySource(dataId);
        }
        //从本地硬盘中获取数据,或者远程从nacos配置中心获得数据
        else {
            ps = nacosPropertySourceBuilder.build(dataId, group, fileExtension, true);
        }
        composite.addFirstPropertySource(ps);
    }
    //从本地硬盘中获取数据,或者远程从nacos配置中心获得数据
    else {
        NacosPropertySource ps = nacosPropertySourceBuilder.build(dataId, group,
                                                                  fileExtension, isRefreshable);
        composite.addFirstPropertySource(ps);
    }
}

上述代码的核心是调用com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#build

NacosPropertySource build(String dataId, String group, String fileExtension,
                          boolean isRefreshable) {
    //从本地硬盘中获取数据,或者远程从nacos配置中心获得数据
    Properties p = loadNacosData(dataId, group, fileExtension);
    //构建NacosPropertySource
    NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId,
                                                                      propertiesToMap(p), new Date(), isRefreshable);
    //把配置放在内存
    NacosPropertySourceRepository.collectNacosPropertySources(nacosPropertySource);
    return nacosPropertySource;
}

着重看下上述代码调用的com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData

private Properties loadNacosData(String dataId, String group, String fileExtension) {
    String data = null;
    try {
        //从本地硬盘中获取数据,或者远程从nacos配置中心获得数据
        data = configService.getConfig(dataId, group, timeout);
        //如果获取到的数据不为空,则把它解析成properties或者是yaml方便使用
        if (!StringUtils.isEmpty(data)) {
            log.info(String.format("Loading nacos data, dataId: '%s', group: '%s'",
                                   dataId, group));

            if (fileExtension.equalsIgnoreCase("properties")) {
                Properties properties = new Properties();

                properties.load(new StringReader(data));
                return properties;
            }
            else if (fileExtension.equalsIgnoreCase("yaml")
                     || fileExtension.equalsIgnoreCase("yml")) {
                YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
                yamlFactory.setResources(new ByteArrayResource(data.getBytes()));
                return yamlFactory.getObject();
            }

        }
    }
    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 会调用到com.alibaba.nacos.client.config.NacosConfigService#getConfigInner

private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
    //如果group为空,则变成直接赋值为DEFAULT_GROUP
    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;
    }

远程获取配置文件是由com.alibaba.nacos.client.config.impl.ClientWorker#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<String> params = null;
         if (StringUtils.isBlank(tenant)) {
             params = Arrays.asList("dataId", dataId, "group", group);
         } else {
             params = Arrays.asList("dataId", dataId, "group", group, "tenant", tenant);
         }
         //最终是get请求http://<ip>/nacos/v1/cs/configs?dataId=<dataId>&group=<group>获取
         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);
         }
     }
 }

2、nacos 配置获取服务端代码解析

这里的服务端的代码是使用1.3.1版本的

  1. 处理nacos/v1/cs/configs请求的是com.alibaba.nacos.config.server.controller.ConfigController#getConfig方法
@GetMapping
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
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 = processTenant(tenant);
    // check params
    ParamUtils.checkParam(dataId, group, "datumId", "content");
    ParamUtils.checkParam(tag);

    final String clientIp = RequestUtil.getRemoteIp(request);
    inner.doGetConfig(request, response, dataId, group, tenant, tag, clientIp);
}

前面是检查配置,重点是com.alibaba.nacos.config.server.controller.ConfigServletInner#doGetConfig

public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group,
                          String tenant, String tag, String clientIp) throws IOException, ServletException {
	//组装dataId,组,和租户的key
    final String groupKey = GroupKey2.getKey(dataId, group, tenant);
    String autoTag = request.getHeader("Vipserver-Tag");
    String requestIpApp = RequestUtil.getAppName(request);
    //尝试获取配置的读锁,返回大于0表示获取成功
    int lockResult = tryConfigReadLock(groupKey);

    final String requestIp = RequestUtil.getRemoteIp(request);
    boolean isBeta = false;
    if (lockResult > 0) {
        FileInputStream fis = null;
        try {
            String md5 = Constants.NULL;
            long lastModified = 0L;
           	//从内存中获取
            CacheItem cacheItem = ConfigCacheService.getContentCache(groupKey);
            if (cacheItem != null) {
                if (cacheItem.isBeta()) {
                    if (cacheItem.getIps4Beta().contains(clientIp)) {
                        isBeta = true;
                    }
                }
                String configType = cacheItem.getType();
                response.setHeader("Config-Type", (null != configType) ? configType : "text");
            }
            File file = null;
            ConfigInfoBase configInfoBase = null;
            PrintWriter out = null;
            //是否为beta版本的配置,如果是,根据ip判断去取配置
            if (isBeta) {
                md5 = cacheItem.getMd54Beta();
                lastModified = cacheItem.getLastModifiedTs4Beta();
                if (PropertyUtil.isDirectRead()) {
                    configInfoBase = persistService.findConfigInfo4Beta(dataId, group, tenant);
                } else {
                    file = DiskUtil.targetBetaFile(dataId, group, tenant);
                }
                response.setHeader("isBeta", "true");
            } else {
                //如果tag为空
                if (StringUtils.isBlank(tag)) {
                    //如果使用是用tag获取,则使用request head的Vipserver-Tag的值作为tag获取
                    if (isUseTag(cacheItem, autoTag)) {
                        if (cacheItem != null) {
                            if (cacheItem.tagMd5 != null) {
                                md5 = cacheItem.tagMd5.get(autoTag);
                            }
                            if (cacheItem.tagLastModifiedTs != null) {
                                lastModified = cacheItem.tagLastModifiedTs.get(autoTag);
                            }
                        }
                        if (PropertyUtil.isDirectRead()) {
                            configInfoBase = persistService.findConfigInfo4Tag(dataId, group, tenant, autoTag);
                        } else {
                            file = DiskUtil.targetTagFile(dataId, group, tenant, autoTag);
                        }

                        response.setHeader("Vipserver-Tag",
                                           URLEncoder.encode(autoTag, StandardCharsets.UTF_8.displayName()));
                    } 
                    //如果不是用tag获取
                    else {
                        //得到缓存中的md5值
                        md5 = cacheItem.getMd5();
                        //得到最后更新时间
                        lastModified = cacheItem.getLastModifiedTs();
                        //如果nacos是standalone模式启动且使用内置的数据库,则使用jdbc查询配置信息,否则使用获取文件
                        if (PropertyUtil.isDirectRead()) {
                            configInfoBase = persistService.findConfigInfo(dataId, group, tenant);
                        } else {
                            file = DiskUtil.targetFile(dataId, group, tenant);
                        }
                        if (configInfoBase == null && fileNotExist(file)) {
                            // FIXME CacheItem
                            // No longer exists. It is impossible to simply calculate the push delayed. Here, simply record it as - 1.
                            ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1,
                                                            ConfigTraceService.PULL_EVENT_NOTFOUND, -1, requestIp);

                            // pullLog.info("[client-get] clientIp={}, {},
                            // no data",
                            // new Object[]{clientIp, groupKey});

                            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                            response.getWriter().println("config data not exist");
                            return HttpServletResponse.SC_NOT_FOUND + "";
                        }
                    }
                } else {
                    if (cacheItem != null) {
                        if (cacheItem.tagMd5 != null) {
                            md5 = cacheItem.tagMd5.get(tag);
                        }
                        if (cacheItem.tagLastModifiedTs != null) {
                            Long lm = cacheItem.tagLastModifiedTs.get(tag);
                            if (lm != null) {
                                lastModified = lm;
                            }
                        }
                    }
                    if (PropertyUtil.isDirectRead()) {
                        configInfoBase = persistService.findConfigInfo4Tag(dataId, group, tenant, tag);
                    } else {
                        file = DiskUtil.targetTagFile(dataId, group, tenant, tag);
                    }
                    if (configInfoBase == null && fileNotExist(file)) {
                        // FIXME CacheItem
                        // No longer exists. It is impossible to simply calculate the push delayed. Here, simply record it as - 1.
                        ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1,
                                                        ConfigTraceService.PULL_EVENT_NOTFOUND, -1, requestIp);

                        // pullLog.info("[client-get] clientIp={}, {},
                        // no data",
                        // new Object[]{clientIp, groupKey});

                        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                        response.getWriter().println("config data not exist");
                        return HttpServletResponse.SC_NOT_FOUND + "";
                    }
                }
            }

            response.setHeader(Constants.CONTENT_MD5, md5);

            // Disable cache.
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setHeader("Cache-Control", "no-cache,no-store");
            if (PropertyUtil.isDirectRead()) {
                response.setDateHeader("Last-Modified", lastModified);
            } else {
                fis = new FileInputStream(file);
                response.setDateHeader("Last-Modified", file.lastModified());
            }

            if (PropertyUtil.isDirectRead()) {
                out = response.getWriter();
                out.print(configInfoBase.getContent());
                out.flush();
                out.close();
            } else {
                //把文件中的内容写入到response
                fis.getChannel()
                    .transferTo(0L, fis.getChannel().size(), Channels.newChannel(response.getOutputStream()));
            }

            LogUtil.PULL_CHECK_LOG.warn("{}|{}|{}|{}", groupKey, requestIp, md5, TimeUtils.getCurrentTimeStr());

            final long delayed = System.currentTimeMillis() - lastModified;

            // TODO distinguish pull-get && push-get
            /*
                 Otherwise, delayed cannot be used as the basis of push delay directly,
                 because the delayed value of active get requests is very large.
                 */
            ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, lastModified,
                                            ConfigTraceService.PULL_EVENT_OK, delayed, requestIp);

        } finally {
            releaseConfigReadLock(groupKey);
            if (null != fis) {
                fis.close();
            }
        }
    } else if (lockResult == 0) {

        // FIXME CacheItem No longer exists. It is impossible to simply calculate the push delayed. Here, simply record it as - 1.
        ConfigTraceService
            .logPullEvent(dataId, group, tenant, requestIpApp, -1, ConfigTraceService.PULL_EVENT_NOTFOUND, -1,
                          requestIp);

        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        response.getWriter().println("config data not exist");
        return HttpServletResponse.SC_NOT_FOUND + "";

    } else {

        PULL_LOG.info("[client-get] clientIp={}, {}, get data during dump", clientIp, groupKey);

        response.setStatus(HttpServletResponse.SC_CONFLICT);
        response.getWriter().println("requested file is being modified, please try later.");
        return HttpServletResponse.SC_CONFLICT + "";

    }

    return HttpServletResponse.SC_OK + "";
}

3、spring cloud客户端是如何监听配置中心配置变更的

在这里插入图片描述

  1. spring cloud nacos监听配置变更并刷新是由com.alibaba.cloud.nacos.refresh.NacosContextRefresher#registerNacosListenersForApplications完成
private void registerNacosListenersForApplications() {
    //如果配置了监听刷新,即spring.cloud.nacos.config.refresh.enabled配置为true
    if (refreshProperties.isEnabled()) {
        for (NacosPropertySource nacosPropertySource : NacosPropertySourceRepository
             .getAll()) {

            if (!nacosPropertySource.isRefreshable()) {
                continue;
            }

            String dataId = nacosPropertySource.getDataId();
            registerNacosListener(nacosPropertySource.getGroup(), dataId);
        }
    }
}
  1. 上述代码重点调用com.alibaba.cloud.nacos.refresh.NacosContextRefresher#registerNacosListener
private void registerNacosListener(final String group, final String dataId) {
    //构建监听器
    Listener listener = listenerMap.computeIfAbsent(dataId, i -> new Listener() {
        @Override
        public void receiveConfigInfo(String configInfo) {
            //收到了配置请求
            refreshCountIncrement();
            String md5 = "";

            if (!StringUtils.isEmpty(configInfo)) {
                try {
                    MessageDigest md = MessageDigest.getInstance("MD5");
                    md5 = new BigInteger(1, md.digest(configInfo.getBytes("UTF-8")))
                        .toString(16);
                }
                catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
                    log.warn("[Nacos] unable to get md5 for dataId: " + dataId, e);
                }
            }
            refreshHistory.add(dataId, md5);
           //发布更新事件
            applicationContext.publishEvent(
                new RefreshEvent(this, null, "Refresh Nacos config"));
            if (log.isDebugEnabled()) {
                log.debug("Refresh Nacos config group " + group + ",dataId" + dataId);
            }
        }

        @Override
        public Executor getExecutor() {
            return null;
        }
    });

    try {
        configService.addListener(dataId, group, listener);
    }
    catch (NacosException e) {
        e.printStackTrace();
    }
}
  1. 上述代码说收到新的配置之后会发送更新事件,我们那怎么样才能算是收到了配置更新了呢。我们来看com.alibaba.nacos.api.config.ConfigService#addListener
 public void addListeners(String dataId, String group, List<? extends Listener> listeners) {
     group = null2defaultGroup(group);
     //得到缓存的数据
     CacheData cache = addCacheDataIfAbsent(dataId, group);
     //给该缓存添加监听器
     for (Listener listener : listeners) {
         cache.addListener(listener);
     }
 }
  1. 上述代码会调用com.alibaba.nacos.client.config.impl.ClientWorker#addCacheDataIfAbsent(java.lang.String, java.lang.String, java.lang.String)
public CacheData addCacheDataIfAbsent(String dataId, String group, String tenant) throws NacosException {
       //如果通过dataId, group, tenant获取到CacheData
        CacheData cache = getCache(dataId, group, tenant);
        if (null != cache) {
            return cache;
        }
    	//如果由dataId, group, tenant组成的key
        String key = GroupKey.getKeyTenant(dataId, group, tenant);
        synchronized (cacheMap) {
            CacheData cacheFromMap = getCache(dataId, group, tenant);
            // multiple listeners on the same dataid+group and race condition,so
            // double check again
            // other listener thread beat me to set to cacheMap
            if (null != cacheFromMap) {
                cache = cacheFromMap;
                // reset so that server not hang this check
                cache.setInitializing(true);
            } else {
                cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant);
                // fix issue # 1317
                if (enableRemoteSyncConfig) {
                    String content = getServerConfig(dataId, group, tenant, 3000L);
                    cache.setContent(content);
                }
            }
			//组装CacheData,加到cacheMap中
            Map<String, CacheData> copy = new HashMap<String, CacheData>(cacheMap.get());
            copy.put(key, cache);
            cacheMap.set(copy);
        }
        LOGGER.info("[{}] [subscribe] {}", agent.getName(), key);

        MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size());

        return cache;
    }
  1. 现在是CacheData组装了加到了cacheMap中,这样就完了,不会吧?应该肯定有个定时线程读取cacheMap并进行监听配置的变化

我们找到了这个方法com.alibaba.nacos.client.config.impl.ClientWorker.LongPollingRunnable

class LongPollingRunnable implements Runnable {
        private int taskId;

        public LongPollingRunnable(int taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {

            List<CacheData> cacheDatas = new ArrayList<CacheData>();
            List<String> inInitializingCacheList = new ArrayList<String>();
            try {
                // check failover config
                for (CacheData cacheData : cacheMap.get().values()) {
                    if (cacheData.getTaskId() == taskId) {
                        cacheDatas.add(cacheData);
                        try {
                            checkLocalConfig(cacheData);
                            if (cacheData.isUseLocalConfigInfo()) {
                                cacheData.checkListenerMd5();
                            }
                        } catch (Exception e) {
                            LOGGER.error("get local config info error", e);
                        }
                    }
                }

                // check server config
                //这里是组装好所有的配置key,返回已变化了的key,这里是个长轮询,超时时间为30秒,超时后返回或者是服务配置有变更时返回
                List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList);
				//根据已经变化了的key,从服务端重新去拿配置,拿到配置后更新本地文件和内存缓存,并刷新@Value等
                for (String groupKey : changedGroupKeys) {
                    String[] key = GroupKey.parseKey(groupKey);
                    String dataId = key[0];
                    String group = key[1];
                    String tenant = null;
                    if (key.length == 3) {
                        tenant = key[2];
                    }
                    try {
                        String content = getServerConfig(dataId, group, tenant, 3000L);
                        CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
                        cache.setContent(content);
                        LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}",
                            agent.getName(), dataId, group, tenant, cache.getMd5(),
                            ContentUtils.truncateContent(content));
                    } catch (NacosException ioe) {
                        String message = String.format(
                            "[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s",
                            agent.getName(), dataId, group, tenant);
                        LOGGER.error(message, ioe);
                    }
                }
                for (CacheData cacheData : cacheDatas) {
                    if (!cacheData.isInitializing() || inInitializingCacheList
                        .contains(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant))) {
                        //通知监听器配置变更
                        cacheData.checkListenerMd5();
                        cacheData.setInitializing(false);
                    }
                }
                inInitializingCacheList.clear();

                executorService.execute(this);

            } catch (Throwable e) {

                // If the rotation training task is abnormal, the next execution time of the task will be punished
                LOGGER.error("longPolling error : ", e);
                executorService.schedule(this, taskPenaltyTime, TimeUnit.MILLISECONDS);
            }
        }
    }
  1. 获取到变化的配置后,对每个缓存项CacheData进行发送配置变化,

    com.alibaba.nacos.client.config.impl.CacheData#checkListenerMd5 ->

    com.alibaba.nacos.client.config.impl.CacheData#safeNotifyListener->

    com.alibaba.nacos.api.config.listener.Listener#receiveConfigInfo

    也就是回到了第2点的发布RefreshEvent。org.springframework.cloud.endpoint.event.RefreshEventListener#onApplicationEvent监听到事件,最终调用`org.springframework.cloud.context.refresh.ContextRefresher#refresh,这个方法把所有标记了@RefreshScope的bean重新加载,这一部分的原理参考https://www.cnblogs.com/niechen/p/8979578.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nacos配置中心控制台的源码分析可以帮助我们深入理解其实现细节和工作原理。以下是一个大致的源码分析过程: 1. 入口类分析:首先,我们需要找到Nacos配置中心控制台的入口类。该类通常是一个Spring Boot应用的启动类,负责初始化和启动整个应用。我们可以查找包含main方法的类,或者在启动脚本中找到应用的入口点。 2. 依赖分析:接下来,我们需要分析应用所依赖的第三方库和框架。查看应用的pom.xml文件或者build.gradle文件,可以获取到所依赖的各个库和对应版本。这些依赖通常包括Spring框架、Nacos客户端等。 3. 配置加载与解析Nacos配置中心控制台需要加载和解析配置,包括数据库配置、Nacos服务地址配置等。我们可以查找相关的配置文件或者代码片段,了解配置的加载和解析过程。 4. 控制器与路由:控制台通常提供了一些Web接口供前端调用。我们可以查找控制器类,分析其中的方法和注解,了解各个接口的功能和路由规则。 5. 页面模板与前端交互:配置中心控制台通常包含一些页面模板和与前端的交互逻辑。我们可以查找相关的HTML、CSS和JavaScript文件,分析页面的结构和交互逻辑。 6. 调用Nacos API:控制台需要与Nacos服务器进行通信,调用Nacos的API获取和修改配置信息。我们可以查找相关的API调用,了解控制台是如何与Nacos服务器进行通信的。 通过以上分析,我们可以逐步了解Nacos配置中心控制台的实现细节和工作原理。需要注意的是,具体的源码分析过程会因项目结构和代码风格而有所不同。以上只是一个大致的指导,具体分析还需根据实际情况来进行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值