了解了上述Environment的基本原理后,如何从近程服务器上加载配置到Spring的Environment中。

NacosPropertySourceLocator

顺着后面的剖析思路,咱们很天然的去找PropertySourceLocator的实现类,发现除了咱们自定义的GpJsonPropertySourceLocator以外,还有另外一个实现类NacosPropertySourceLocator.

于是,间接来看NacosPropertySourceLocator中的locate办法,代码如下。

public PropertySource<?> locate(Environment env) {    this.nacosConfigProperties.setEnvironment(env);    ConfigService configService = this.nacosConfigManager.getConfigService();    if (null == configService) {        log.warn("no instance of config service found, can't load config from nacos");        return null;    } else {        //获取客户端配置的超时工夫        long timeout = (long)this.nacosConfigProperties.getTimeout();        this.nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout);        //获取name属性,        String name = this.nacosConfigProperties.getName();        //在Spring Cloud中,默认的name=spring.application.name。        String dataIdPrefix = this.nacosConfigProperties.getPrefix();        if (StringUtils.isEmpty(dataIdPrefix)) {            dataIdPrefix = name;        }        if (StringUtils.isEmpty(dataIdPrefix)) {            dataIdPrefix = env.getProperty("spring.application.name"); //获取spring.application.name,赋值给dataIdPrefix        }       //创立一个Composite属性源,能够蕴含多个PropertySource        CompositePropertySource composite = new CompositePropertySource("NACOS");        this.loadSharedConfiguration(composite);   //加载共享配置          //加载扩大配置        loadExtConfiguration(composite);        //加载本身配置        loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);        return composite;    }}

上述代码的实现不难理解

  1. 获取nacos客户端的配置属性,并生成dataId(这个很重要,要定位nacos的配置)
  2. 别离调用三个办法从加载配置属性源,保留到composite组合属性源中

loadApplicationConfiguration

咱们能够先不论加载共享配置、扩大配置的办法,最终实质上都是去近程服务上读取配置,只是传入的参数不一样。

  • fileExtension,示意配置文件的扩展名
  • nacosGroup示意分组
  • 加载dataid=项目名称的配置
  • 加载dataid=项目名称+扩展名的配置
  • 遍历以后配置的激活点(profile),别离循环加载带有profile的dataid配置
private void loadApplicationConfiguration(    CompositePropertySource compositePropertySource, String dataIdPrefix,    NacosConfigProperties properties, Environment environment) {    String fileExtension = properties.getFileExtension();  //默认的扩大名为: properties    String nacosGroup = properties.getGroup(); //获取group    //加载`dataid=项目名称`的配置    loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,                           fileExtension, true);    //加载`dataid=项目名称+扩展名`的配置    loadNacosDataIfPresent(compositePropertySource,                           dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);    // 遍历profile(能够有多个),依据profile加载配置    for (String profile : environment.getActiveProfiles()) {        //此时的dataId=${spring.application.name}.${profile}.${fileExtension}        String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;        loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,                               fileExtension, true);    }}

loadNacosDataIfPresent

调用loadNacosPropertySource加载存在的配置信息。

把加载之后的配置属性保留到CompositePropertySource中。

private void loadNacosDataIfPresent(final CompositePropertySource composite,      final String dataId, final String group, String fileExtension,      boolean isRefreshable) {    //如果dataId为空,或者group为空,则间接跳过   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);    //把属性源保留到compositePropertySource中   this.addFirstPropertySource(composite, propertySource, false);}

loadNacosPropertySource

private NacosPropertySource loadNacosPropertySource(final String dataId,      final String group, String fileExtension, boolean isRefreshable) {   if (NacosContextRefresher.getRefreshCount() != 0) {      if (!isRefreshable) { //是否反对主动刷新,// 如果不反对主动刷新配置则主动从缓存获取返回(不从近程服务器加载)         return NacosPropertySourceRepository.getNacosPropertySource(dataId,               group);      }   }    //结构器从配置核心获取数据   return nacosPropertySourceBuilder.build(dataId, group, fileExtension,         isRefreshable);}

NacosPropertySourceBuilder.build

NacosPropertySource build(String dataId, String group, String fileExtension,      boolean isRefreshable) {        //调用loadNacosData加载近程数据   List<PropertySource<?>> propertySources = loadNacosData(dataId, group,         fileExtension);    //结构NacosPropertySource(这个是Nacos自定义扩大的PropertySource,和咱们后面演示的自定义PropertySource相似)。//    相当于把从近程服务器获取的数据保留到NacosPropertySource中。   NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources,         group, dataId, new Date(), isRefreshable);    //把属性缓存到本地缓存   NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);   return nacosPropertySource;}

NacosPropertySourceBuilder.loadNacosData

这个办法,就是连贯近程服务器去获取配置数据的实现,要害代码是configService.getConfig

private List<PropertySource<?>> loadNacosData(String dataId, String group,      String fileExtension) {   String data = null;   try {      data = configService.getConfig(dataId, group, timeout); //加载Nacos配置数据      if (StringUtils.isEmpty(data)) {         log.warn(               "Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]",               dataId, group);         return Collections.emptyList();      }      if (log.isDebugEnabled()) {         log.debug(String.format(               "Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,               group, data));      }       //对加载的数据进行解析,保留到List<PropertySource>汇合。      return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,            fileExtension);   }   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 Collections.emptyList();}

阶段性总结

通过上述剖析,咱们晓得了Spring Cloud集成Nacos时的要害门路,并且晓得在启动时,Spring Cloud会从Nacos Server中加载动态数据保留到Environment汇合。

从而实现动静配置的主动注入。

Nacos客户端的数据的加载流程

配置数据的最终加载,是基于 configService.getConfig,Nacos提供的SDK来实现的。

public String getConfig(String dataId, String group, long timeoutMs) throws NacosException
对于Nacos SDK的应用教程: https://nacos.io/zh-cn/docs/s...

也就是说,接下来咱们的源码剖析,间接进入到Nacos这个领域。

NacosConfigService.getConfig

@Overridepublic 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 = blank2defaultGroup(group); //获取group,如果为空,则为default-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); //把内容设置到cr中。        //获取容灾配置的encryptedDataKey        String encryptedDataKey = LocalEncryptedDataKeyProcessor                .getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);        cr.setEncryptedDataKey(encryptedDataKey); //保留到cr        configFilterChainManager.doFilter(null, cr); //执行过滤(目前如同没有实现)        content = cr.getContent(); //返回文件content        return content;    }    //如果本地文件中不存在相干内容,则发动近程调用    try {        ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs);        //把响应内容返回        cr.setContent(response.getContent());        cr.setEncryptedDataKey(response.getEncryptedDataKey());                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());    }    //如果呈现NacosException,且不是403异样,则尝试通过本地的快照文件去获取配置进行返回。    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);    String encryptedDataKey = LocalEncryptedDataKeyProcessor            .getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);    cr.setEncryptedDataKey(encryptedDataKey);    configFilterChainManager.doFilter(null, cr);    content = cr.getContent();    return content;}

从本地缓存读取配置

默认状况下,nacos先从本地缓存的配置中读取文件:C:\Users\mayn\nacos\config\fixed-192.168.8.133_8848-6a382560-ed4c-414c-a5e2-9d72c48f1a0e_nacos

如果本地缓存内容存在,则返回内容数据,否则返回空值。

public static String getFailover(String serverName, String dataId, String group, String tenant) {    File localPath = getFailoverFile(serverName, dataId, group, tenant);    if (!localPath.exists() || !localPath.isFile()) {        return null;    }    try {        return readFile(localPath);    } catch (IOException ioe) {        LOGGER.error("[" + serverName + "] get failover error, " + localPath, ioe);        return null;    }}

从指定文件目录下读取文件内容。

static File getFailoverFile(String serverName, String dataId, String group, String tenant) {    File tmp = new File(LOCAL_SNAPSHOT_PATH, serverName + "_nacos");    tmp = new File(tmp, "data");    if (StringUtils.isBlank(tenant)) {        tmp = new File(tmp, "config-data");    } else {        tmp = new File(tmp, "config-data-tenant");        tmp = new File(tmp, tenant);    }    return new File(new File(tmp, group), dataId);}

ClientWorker.getServerConfig

ClientWorker,示意客户端的一个工作类,它负责和服务端交互。

public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout)        throws NacosException {    ConfigResponse configResponse = new ConfigResponse();    if (StringUtils.isBlank(group)) { //如果group为空,则返回默认group        group = Constants.DEFAULT_GROUP;    }        HttpRestResult<String> result = null;    try {        Map<String, String> params = new HashMap<String, String>(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) {        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, ex);        throw new NacosException(NacosException.SERVER_ERROR, ex);    }    //依据响应后果实现不同的解决    switch (result.getCode()) {         case HttpURLConnection.HTTP_OK: //如果响应胜利,保留快照到本地,并返回响应内容            LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.getData());            configResponse.setContent(result.getData());            String configType;  //配置文件的类型,如text、json、yaml等            if (result.getHeader().getValue(CONFIG_TYPE) != null) {                configType = result.getHeader().getValue(CONFIG_TYPE);            } else {                configType = ConfigType.TEXT.getType();            }            configResponse.setConfigType(configType); //设置到configResponse中,后续要依据文件类型实现不同解析策略            //获取加密数据的key            String encryptedDataKey = result.getHeader().getValue(ENCRYPTED_DATA_KEY);            //保留            LocalEncryptedDataKeyProcessor                    .saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, encryptedDataKey);            configResponse.setEncryptedDataKey(encryptedDataKey);            return configResponse;        case HttpURLConnection.HTTP_NOT_FOUND: //如果返回404, 清空本地快照            LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null);            LocalEncryptedDataKeyProcessor.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, null);            return configResponse;        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.getCode(), result.getMessage());        }        default: {            LOGGER.error("[{}] [sub-server-error]  dataId={}, group={}, tenant={}, code={}", agent.getName(),                    dataId, group, tenant, result.getCode());            throw new NacosException(result.getCode(),                    "http error, code=" + result.getCode() + ",dataId=" + dataId + ",group=" + group + ",tenant="                            + tenant);        }    }}

ServerHttpAgent.httpGet

发动近程申请的实现。

@Overridepublic HttpRestResult<String> httpGet(String path, Map<String, String> headers, Map<String, String> paramValues,        String encode, long readTimeoutMs) throws Exception {    final long endTime = System.currentTimeMillis() + readTimeoutMs;    injectSecurityInfo(paramValues);  //注入平安信息    String currentServerAddr = serverListMgr.getCurrentServerAddr();//获取以后服务器地址    int maxRetry = this.maxRetry; //获取最大重试次数,默认3次    //配置HttpClient的属性,默认的readTimeOut超时工夫是3s    HttpClientConfig httpConfig = HttpClientConfig.builder()            .setReadTimeOutMillis(Long.valueOf(readTimeoutMs).intValue())            .setConTimeOutMillis(ConfigHttpClientManager.getInstance().getConnectTimeoutOrDefault(100)).build();    do {               try {            //设置header            Header newHeaders = getSpasHeaders(paramValues, encode);            if (headers != null) {                newHeaders.addAll(headers);            }            //构建query查问条件            Query query = Query.newInstance().initParams(paramValues);            //发动http申请,http://192.168.8.133:8848/nacos/v1/cs/configs            HttpRestResult<String> result = NACOS_RESTTEMPLATE                    .get(getUrl(currentServerAddr, path), httpConfig, newHeaders, query, String.class);            if (isFail(result)) { //如果申请失败,                LOGGER.error("[NACOS ConnectException] currentServerAddr: {}, httpCode: {}",                        serverListMgr.getCurrentServerAddr(), result.getCode());            } else {                // Update the currently available server addr                serverListMgr.updateCurrentServerAddr(currentServerAddr);                return result;            }        } catch (ConnectException connectException) {            LOGGER.error("[NACOS ConnectException httpGet] currentServerAddr:{}, err : {}",                    serverListMgr.getCurrentServerAddr(), connectException.getMessage());        } catch (SocketTimeoutException socketTimeoutException) {            LOGGER.error("[NACOS SocketTimeoutException httpGet] currentServerAddr:{}, err : {}",                    serverListMgr.getCurrentServerAddr(), socketTimeoutException.getMessage());        } catch (Exception ex) {            LOGGER.error("[NACOS Exception httpGet] currentServerAddr: " + serverListMgr.getCurrentServerAddr(),                    ex);            throw ex;        }        //如果服务端列表有多个,并且以后申请失败,则尝试用下一个地址进行重试        if (serverListMgr.getIterator().hasNext()) {            currentServerAddr = serverListMgr.getIterator().next();        } else {            maxRetry--; //重试次数递加            if (maxRetry < 0) {                throw new ConnectException(                        "[NACOS HTTP-GET] The maximum number of tolerable server reconnection errors has been reached");            }            serverListMgr.refreshCurrentServerAddr();        }            } while (System.currentTimeMillis() <= endTime);        LOGGER.error("no available server");    throw new ConnectException("no available server");}

Nacos Server端的配置获取

客户端向服务端加载配置,调用的接口是:/nacos/v1/cs/configs , 于是,在Nacos的源码中找到该接口

定位到Nacos源码中的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 = NamespaceUtil.processNamespaceParameter(tenant); //租户,也就是namespaceid    // check params    ParamUtils.checkParam(dataId, group, "datumId", "content"); //查看申请参数是否为空    ParamUtils.checkParam(tag);        final String clientIp = RequestUtil.getRemoteIp(request); //获取申请的ip    inner.doGetConfig(request, response, dataId, group, tenant, tag, clientIp); //加载配置}

inner.doGetConfig

public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group,        String tenant, String tag, String clientIp) throws IOException, ServletException {    final String groupKey = GroupKey2.getKey(dataId, group, tenant);    String autoTag = request.getHeader("Vipserver-Tag");    String requestIpApp = RequestUtil.getAppName(request); //申请端的利用名称       int lockResult = tryConfigReadLock(groupKey);  //尝试获取以后申请配置的读锁(防止读写抵触)        final String requestIp = RequestUtil.getRemoteIp(request); //申请端的ip        boolean isBeta = false;    //lockResult>0 ,示意CacheItem(也就是缓存的配置项)不为空,并且曾经加了读锁,意味着这个缓存数据不能被删除。    //lockResult=0 ,示意cacheItem为空,不须要加读锁    //lockResult=01 , 示意加锁失败,存在抵触。    //上面这个if,就是针对这三种状况进行解决。    if (lockResult > 0) {        // LockResult > 0 means cacheItem is not null and other thread can`t delete this cacheItem        FileInputStream fis = null;        try {            String md5 = Constants.NULL;            long lastModified = 0L;            //从本地缓存中,依据groupKey获取CacheItem            CacheItem cacheItem = ConfigCacheService.getContentCache(groupKey);            //判断是否是beta公布,也就是测试版本            if (cacheItem.isBeta() && cacheItem.getIps4Beta().contains(clientIp)) {                isBeta = true;            }            //获取配置文件的类型            final String configType =                    (null != cacheItem.getType()) ? cacheItem.getType() : FileTypeEnum.TEXT.getFileType();            response.setHeader("Config-Type", configType);            //返回文件类型的枚举对象            FileTypeEnum fileTypeEnum = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(configType);            String contentTypeHeader = fileTypeEnum.getContentType();            response.setHeader(HttpHeaderConsts.CONTENT_TYPE, contentTypeHeader);                        File file = null;            ConfigInfoBase configInfoBase = null;            PrintWriter out = null;            if (isBeta) { //如果是测试配置                md5 = cacheItem.getMd54Beta();                lastModified = cacheItem.getLastModifiedTs4Beta();                if (PropertyUtil.isDirectRead()) {                    configInfoBase = persistService.findConfigInfo4Beta(dataId, group, tenant);                } else {                    file = DiskUtil.targetBetaFile(dataId, group, tenant); //从磁盘中获取文件,失去的是一个残缺的File                }                response.setHeader("isBeta", "true");            } else {                if (StringUtils.isBlank(tag)) { //判断tag标签是否为空,tag对应的是nacos配置核心的标签选项                    if (isUseTag(cacheItem, autoTag)) {                        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()));                    } else {//间接走这个逻辑(默认不会配置tag属性)                        md5 = cacheItem.getMd5(); //获取缓存的md5                        lastModified = cacheItem.getLastModifiedTs(); //获取最初更新工夫                        if (PropertyUtil.isDirectRead()) {  //判断是否是stamdalone模式且应用的是derby数据库,如果是,则从derby数据库加载数据                            configInfoBase = persistService.findConfigInfo(dataId, group, tenant);                        } else {                            //否则,如果是数据库或者集群模式,先从本地磁盘失去文件                            file = DiskUtil.targetFile(dataId, group, tenant);                        }                        //如果本地磁盘文件为空,并且configInfoBase为空,则示意配置数据不存在,间接返回null                        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 {//如果tag不为空,阐明配置文件设置了tag标签                    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中返回                        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 {//否则,通过trasferTo                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); //开释锁            IoUtils.closeQuietly(fis);        }    } 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 + "";}

persistService.findConfigInfo

从derby数据库中获取数据内容,这个就是一个根本的数据查问操作。

@Overridepublic ConfigInfo findConfigInfo(final String dataId, final String group, final String tenant) {    final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;    final String sql = "SELECT ID,data_id,group_id,tenant_id,app_name,content,md5,type FROM config_info "            + " WHERE data_id=? AND group_id=? AND tenant_id=?";    final Object[] args = new Object[] {dataId, group, tenantTmp};    return databaseOperate.queryOne(sql, args, CONFIG_INFO_ROW_MAPPER);    }

DiskUtil.targetFile

从磁盘目录中获取指标文件,间接依据dataId/group/tenant ,查找指定目录下的文件即可

public static File targetFile(String dataId, String group, String tenant) {    File file = null;    if (StringUtils.isBlank(tenant)) {        file = new File(EnvUtil.getNacosHome(), BASE_DIR);    } else {        file = new File(EnvUtil.getNacosHome(), TENANT_BASE_DIR);        file = new File(file, tenant);    }    file = new File(file, group);    file = new File(file, dataId);    return file;}

至此,NacosPropertySourceLocator 实现了从Nacos Server上动静获取配置并缓存到本地,从而实现Nacos动静配置获取的能力!

版权申明:本博客所有文章除特地申明外,均采纳 CC BY-NC-SA 4.0 许可协定。转载请注明来自 Mic带你学架构
如果本篇文章对您有帮忙,还请帮忙点个关注和赞,您的保持是我一直创作的能源。欢送关注同名微信公众号获取更多技术干货!