「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)中; 如果是读取配置文件中的固定集群列表的方式: ①.如果配置了namespace name= fixed-{ip1_port1-ip2_port2-ip3_port3}-namespace ②.如果没有配置namespace name= 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的方式获取集群列表: ①.如果配置了namespace name={endpoint}-{namespace} ②.如果没有配置namespace name={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 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请求;获取到数据
  2. 如果请求成功,Code码=200; 则在本地保存一份数据快照Snapshot;跟上面的本地配置不一样;
  3. 如果返回404,则删除本地快照Snapshot

最后使用快照Snapshot数据

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

获取配置数据流程图

在这里插入图片描述

Part4总结


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

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

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章