前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Nacos源码分析】- 02 获取配置流程

【Nacos源码分析】- 02 获取配置流程

作者头像
Reactor2020
发布2023-03-22 19:05:03
5440
发布2023-03-22 19:05:03
举报

【Nacos源码分析】- 01 ConfigService创建流程

【Java并发编程】- 03 MESI、内存屏障

【Spring源码】- 11 Spring AOP之编程式事务

Gateway源码环境搭建

ConfigService是提供给开发者使用的,用来对配置文件进行相关操作的核心接口,比如获取/监听/发布/删除配置项等,这一节我们来分析下获取配置内容的流程,对应的是ConfigService#getConfig()方法。

String getConfig(String dataId, String group, long timeoutMs)方法有几个参数:

  • dataId:配置文件名;
  • group:配置文件所属group
  • timeoutMs:向nacos server发送请求获取配置内容时,可能会由于各种原因失败导致不停重试,timeoutMs指定超时时间可以避免由于过多的重试导致阻塞过久。

这里没有指定namespace,是因为namespace是和ConfigService绑定的,即每个ConfigService只能对应一个namespacenamespace设计就是用来进行隔离。

准备工作

代码语言:javascript
复制
//group空则赋值DEFAULT_GROUP
group = null2defaultGroup(group);
//校验dataId、group是否合法,不为空且必须是数字、字母或[- _ . :]四个字符,其它都是非法
ParamUtils.checkKeyParam(dataId, group);
//返回结果封装
ConfigResponse cr = new ConfigResponse();

cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);

主要是对参数进行校验,以及创建一个ConfigResponse用来封装返回结果信息。

从FailoverFile获取配置

在向Nacos Server发送http请求获取配置内容之前,会调用LocalConfigInfoProcessor.getFailover先尝试从本地配置文件FailoverFile获取,代码如下:

代码语言:javascript
复制
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);
 //调用ConfigFilter.doFilter()方法
 configFilterChainManager.doFilter(null, cr);
 content = cr.getContent();
 //如果本地配置存在,则直接返回本地配置,而不用去nacos server上去拉取
 return content;
}

FailoverFile在客户端不会自动生成,正常情况下Failover文件都是不存在的,设计这种机制可能主要是某种场景下扩展,比如用户系统自行维护配置变更,将最新的配置内容写入到本地Failover文件中,这样就不需要向Nacos Server发送http请求获取配置。

Failover文件路径:系统用户根目录+nacos/config+agentName+data+config-data+[group名称]+[dataId名称],比如:C:\Users\Administrator\nacos\config\fixed-127.0.0.1_8848-192.168.1.1_9999_nacos\data\config-data\DEFAULT_GROUP\other

从Nacos Server获取配置

如果Failover方式没有获取到配置信息,则向Nacos Server获取配置内容,核心代码如下:

代码语言:javascript
复制
try {
 //去nacos server上拉取
 String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
 cr.setContent(ct[0]);
 //调用ConfigFilter chain对获取的配置进行处理
 configFilterChainManager.doFilter(null, cr);
 content = cr.getContent();

 return content;
} catch (NacosException ioe) {
 if (NacosException.NO_RIGHT == ioe.getErrCode()) {//403没有权限,拒绝访问
  throw ioe;
 }
 LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", agent.getName(), dataId, group, tenant, ioe.toString());
}

关键代码是:worker.getServerConfig(dataId, group, tenant, timeoutMs),即委托给ClientWorker对象:

代码语言:javascript
复制
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;
  }

 HttpResult result = null;
 try {
  List<String> params = null;
        if (StringUtils.isBlank(tenant)) {
   params = new ArrayList<String>(Arrays.asList("dataId", dataId, "group", group));
  } else {
   params = new ArrayList<String>(Arrays.asList("dataId", dataId, "group", group, "tenant", tenant));
  }
  //利用ServerHttpAgent向nacos server发送get请求,path=/v1/cs/configs,params=dataId+group+tenant(namespace)
  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:
            //更新本地对应的snapshot配置文件
            LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content);
            ct[0] = result.content;
            //设置配置文件类型,如:properties、yaml等
            if (result.headers.containsKey(CONFIG_TYPE)) {
                ct[1] = result.headers.get(CONFIG_TYPE).get(0);
            } 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: {
            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);
        }
    }
}

代码看着比较多,但是逻辑还是比较简单,大致流程:

  • 通过ServerHttpAgentNacos发送http请求,path=/v1/cs/configs,params=dataId+group+tenant(namespace),之前分析过ServerHttpAgent专门用来向Nacos发送http请求,返回值就是params所指定的配置文件内容。
  • switch (result.code)对返回结果进行处理,如果返回http状态码200,则表示请求成功,首先调用saveSnapshot()方法对获取到最新的配置文件内存保存到本地SnapshotFile中,当Nacos Server不可用时容灾使用,然后返回配置文件内容和配置文件类型。
  • 然后就是请求异常处理,这里主要分为两类:
    • HTTP_NOT_FOUND即404异常,会将本地对应的SnapshotFile文件内容清空,然后返回null
    • 其它异常都会抛出异常;

从SnapshotFile获取配置

如果从Nacos Server获取配置出现异常,即ClientWorker.getServerConfig()方法抛出异常,则会从本地SnapshotFile中获取配置内容,核心代码如下:

代码语言:javascript
复制
LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", agent.getName(),
dataId, group, tenant, ContentUtils.truncateContent(content));
//如果从nacos server上获取配置失败,则从本地snapshot中获取配置内容
content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);
cr.setContent(content);
//调用ConfigFilter chain对获取的配置进行处理
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;

总结

调用ConfigService.getConfig()获取配置的流程分析完成,从上面分析流程来看,获取配置大致可以总结如下:

  1. 先会从本地FailoverFile配置文件中获取,如果获取到则直接返回,不用再去向Nacos Server发送http请求方式获取,不过一般情况下客户端不会写FailoverFile配置文件,即正常情况下FailoverFile都是不存在的,主要可能是某种场景下扩展使用;
  2. FailoverFile获取失败时,这时会利用ServerHttpAgentNacos发送http请求,path=/v1/cs/configs,params=dataId+group+tenant(namespace),返回值就是配置文件内容信息,成功则将最新配置内容写入到本地SnapshotFile中,然后返回;
  3. 上篇分析过ServerHttpAgent是带有异常重试机制的,重试后如果最终还是失败,则查看本地是否存在配置快照文件SnapshotFile,有则读取返回,否则返回nullSnapshotFile相当于Nacos Server不可用时一种容灾补救方式。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Reactor2020 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 准备工作
  • 从FailoverFile获取配置
  • 从Nacos Server获取配置
  • 从SnapshotFile获取配置
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档