写点什么

【Nacos 源码之配置管理 九】客户端获取配置数据的流程

  • 2022 年 10 月 09 日
    江西
  • 本文字数:5176 字

    阅读完需:约 17 分钟

【Nacos源码之配置管理 九】客户端获取配置数据的流程

作者石臻臻,CSDN 博客之星 Top5Kafka Contributornacos Contributor华为云 MVP,腾讯云 TVP,滴滴 Kafka 技术专家 KnowStreaming


KnowStreaming 是滴滴开源的Kafka运维管控平台, 有兴趣一起参与参与开发的同学,但是怕自己能力不够的同学,可以联系我,当你导师带你参与开源!

Part1 前言


上一篇文章讲了 【Nacos源码之配置管理 八】客户端怎么获取服务端集群列表 ,客户端获取到集群列表缓存在内存中,是在获取配置的时候需要使用的; 因为要去服务端发起 http 请求获取数据; 那么我们今天来分析一下,客户端是如何获取服务端数据的阅读完本文,您将会了解以下问题:

  • [x] 客户端如何获取配置数据

  • [x] 客户端如何配置本地配置数据(开发的时候,开发者可以配置自己的配置数据)

  • [x] 如果服务端全部宕机,客户端将如何获取数据

Part2 启动服务端


客户端的数据是去服务端获取的,所以我们如果不启动服务端,那么客户端也就获取不到数据;所以要先启动服务端;如何启动 参考 【Nacos源码之配置管理 一】阅读源码第一步,本地启动Nacos

Part3 启动客户端


我们创建一个新 SpringBoot 项目; 用 Nacos 的 sdk 获取配置数据;


然后启动项目;打上断点开始调试;

1NacosFatory.createConfigService 创建配置服务类


这个方法会获取一个 ConfigService 的实例 NacosConfigService;是通过返回创建的实例

  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);            return vendorImpl;        } catch (Throwable e) {            throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);        }    }
复制代码

NacosConfigService 是客户端的一个配置服务类; 所有对配置数据的操作都是这个实例来完成的;它里面持有一个 ServerHttpAgent 实例;ServerHttpAgent 是一个 Http 代理类,用来发起 Http 请求的;它做了一些数据采集的功能 ;ServerHttpAgent 中又持有一个 ServerListManager 实例,它负责所有的集群列表信息;就是上一篇文章解析过的; 【Nacos源码之配置管理 八】客户端怎么获取服务端集群列表

2 获取指定配置数据



            Properties properties = new Properties();            properties.put("serverAddr", serverAddr);            properties.put("namespace","dev");            ConfigService configService = NacosFactory.createConfigService(properties);            String content = configService.getConfig(dataId, group, 5000);            System.out.println(content);
复制代码

配置中设置了 serverAddr 和命名空间 namespace 这个命名空间可以在管理后台自己新建的


获取配置的核心代码

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;    }
复制代码

优先使用本地配置

从代码中可以看到调用了LocalConfigInfoProcessor.getFailover方法


        String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);        
复制代码

这个方法主要作用就是查询客户端本地的配置; 这个适和什么使用场景呢?例如我们本地开发调试的时候,为了不影响其他开发者,所以需要每个开发者使用自己单独的配置;这个时候我们就可以在本地配置一份属于自己的配置数据; 客户端会优先读取;

本地配置数据的路径是什么

这个路径真的是有一点恶心;挺长的,不太容易配置;①.如果配置了 tenant(就是命名空间 namespace)

/{LOCAL_SNAPSHOT_PATH}/{serverName}_nacos/data/config-data-tenant/{tenant}/{group}/{dataId}
复制代码

②.没有配置 tenant

/{LOCAL_SNAPSHOT_PATH}/{serverName}_nacos/data/config-data/{group}/{dataId}
复制代码

以上用括号{} 起来的参数都是入参;现在一一分析这些入参

  1. LOCAL_SNAPSHOT_PATH: 本地快照路径;可以设置 Jvm 属性-DJM.SNAPSHOT.PATH=/Users/shirenchuang/nacos;来指定;如果没有的话会默认获取 Jvm 属性user.home ; 这个属性是我们电脑的 home 路径;不需要主动设置;例如我的 mac 电脑就是 /Users/shirenchuang ;获取到上面的属性之后还要加上 nacos/config ;例如如果我什么都没有设置的话就是: /Users/shirenchuang/nacos/config

  2. serverName: 服务端名字; 这个名字就有点坑爹了,他是 ServerListManager 中的 name 属性;name 设置在构造方法ServerListManager(Properties properties)中;

  1. 如果是读取配置文件中的固定集群列表的方式:①.如果配置了 namespacename= fixed-{ip1_port1-ip2_port2-ip3_port3}-namespace②.如果没有配置 namespacename= fixed-{ip1_port1-ip2_port2-ip3_port3}例如我配置了固定的集群列表是 serverAddr = 127.0.0.1:8848,http://test.shiyi.com,127.0.0.1:8849;并且 namespace 设置为 dev;那么最终 name= fixed-127.0.0.1_8848-test.shiyi.com-127.0.0.1_8849-dev;注意 http:// https:// 会被删掉

    如果是通过访问 endpoint 的方式获取集群列表:①.如果配置了 namespacename={endpoint}-{namespace}②.如果没有配置 namespacename={endpoint}

本地配置例子

  1. 有配置 tenant(就是 namespace)=dev,方式一配置集群;集群列表配置 127.0.0.1:8848,http://test.shiyi.com,127.0.0.1:8849 ;;然后想获取 dataId=com.shirc.test.dataId ;group=DEFAULT_GROUP 的配置文件

name= /Users/shirenchuang/fixed-127.0.0.1_8848-test.shiyi.com-127.0.0.1_8849-dev_nacos/data/config-data/DEFAULT_GROUP/com.shirc.test.dataId

2. 使用方式二;配置了 namespace=dev;endpoint=shiyi.com

name= /Users/shirenchuang/shiyi.com-dev/data/config-data/DEFAULT_GROUP/com.shirc.test.dataId

所以,如果使用方式一的话,是不是觉得配置一下本地数据真的挺坑爹的;

所以觉得自己一个个拼接地址容易出错,那么你可以启动的时候在LocalConfigInfoProcessor.getFailoverFile 打个断点直接拿一下它要读取的地址,如下

那么最终的文件地址就是/Users/shirenchuang/nacos/config/fixed-172.16.10.61_8848_nacos/data/config-data/DEFAULT_GROUP/dataId文件名;

路径找到了,然后在这个路径下面创建你的 dataId 文件,因为在启动的时候 nacos 会自动的把数据 dump 到本地存放了一份快照文件,我们可以直接把这个快照 dataId 文件 copy 到刚刚的路径,再修改本地的一些配置;


其次使用服务端配置数据


如果本地没有配置配置文件的话,那么客户端就会想服务端发起 Http 请求去获取配置数据了;

public String getServerConfig(String dataId, String group, String tenant, long readTimeout)        throws NacosException {        /**以下省略了部分代码**/        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);            }            result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);        } catch (IOException 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: {                            }            case HttpURLConnection.HTTP_FORBIDDEN: {                          }            default: {                          }        }    }


复制代码

以上获取服务端数据的流程是:

  1. 服务端地址/v1/cs/configs 的 Get 请求;获取到数据


  1. 如果请求成功,Code 码=200; 则在本地保存一份数据快照 Snapshot;跟上面的本地配置不一样;

  2. 如果返回 404,则删除本地快照 Snapshot

最后使用快照 Snapshot 数据

上面在从服务端获取到数据之后,会保存一份快照数据到本地中;保存这个本地快照 Snapshot 有什么用呢?这个就是为了预防服务端全部不可访问、宕机之后还能从本地快照中获取上一次获取到的数据;

获取配置数据流程图

在这里插入图片描述

Part4 总结


客户端发起请求获取配置数据的时候并不会立马请求服务端中的数据

而是先查找自己本地有没有配置文件;如果有 直接返回本地配置如果没有才去查服务端中的配置数据; 查询到了之后还会在本地创建一个快照文件 Snapshot;这个快照文件就是防止服务端宕机获取不到数据的时候,可以去获取本地快照 Snapshot 返回;

发布于: 刚刚阅读数: 5
用户头像

关注公众号: 石臻臻的杂货铺 获取最新文章 2019.09.06 加入

进高质量滴滴技术交流群,只交流技术不闲聊 加 szzdzhp001 进群 20w字《Kafka运维与实战宝典》PDF下载请关注公众号:石臻臻的杂货铺

评论

发布
暂无评论
【Nacos源码之配置管理 九】客户端获取配置数据的流程_nacos_石臻臻的杂货铺_InfoQ写作社区