nacos-config工作原理解析

本文解析了nacos配置中心的工作原理,从java SDK入手,详细介绍了通过getConfig()方法直接获取配置信息以及通过receiveConfigInfo()监听回调获取配置的流程。在服务端,nacos处理长连接请求,判断数据变化并及时响应客户端更新。
摘要由CSDN通过智能技术生成

nacos-config工作原理解析

说明:本文是基于nacos-1.1.0版本进行解读,请注意区分。

1. 什么是nacos?

nacos官方手册上对nacos的描述比较权威,这里不再赘述,手册地址https://nacos.io/zh-cn/docs/what-is-nacos.html

2. java的sdk

对工作原理的分析是从sdk入手的,如果你没有直接使用过sdk进行编程,建议先熟悉一下,具体可参考官方手册https://nacos.io/zh-cn/docs/sdk.html

3. nacos的工作流程

3.1 getConfig()方法直接获取配置信息

在这里插入图片描述
通过这种方式获取配置信息,工作流程比较简单,客户端每次发送http GET请求到server端获取最新的配置信息。

3.2 通过receiveConfigInfo()从监听中回调获取

在这里插入图片描述

3.2.1 源码解读
// sdk编程时的主入口
public static ConfigService createConfigService(Properties properties) throws NacosException {
   
    return ConfigFactory.createConfigService(properties);
}
public static ConfigService createConfigService(Properties properties) throws NacosException {
   
    try {
   
    	// 这里是通过反射创建一个NacosConfigService对象并返回
    	// 因此,我们sdk编程的时候其实最终使用的是这个类的实例
        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);
    }
}
public NacosConfigService(Properties properties) throws NacosException {
   
    String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE);
    if (StringUtils.isBlank(encodeTmp)) {
   
        encode = Constants.ENCODE;
    } else {
   
        encode = encodeTmp.trim();
    }
    // 这里是初始化命名空间
    initNamespace(properties);
    // 初始化http agent,后续与server端通信会用到
    agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
    agent.start();
    // 这里是客户端工作的核心,接下来我们要重点关注
    worker = new ClientWorker(agent, configFilterChainManager, properties);
}
// ClientWorker里面主要是初始化两个线程池
// executor线程池中延迟10ms启动了一个线程去执行checkConfigInfo()方法
public ClientWorker(final HttpAgent agent, final ConfigFilterChainManager configFilterChainManager, final Properties properties) {
   
    this.agent = agent;
    this.configFilterChainManager = configFilterChainManager;

    // Initialize the timeout parameter

    init(properties);

    executor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
   
        @Override
        public Thread newThread(Runnable r) {
   
            Thread t = new Thread(r);
            t.setName("com.alibaba.nacos.client.Worker." + agent.getName());
            t.setDaemon(true);
            return t;
        }
    });

    executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
   
        @Override
        public Thread newThread(Runnable r) {
   
            Thread t = new Thread(r);
            t.setName("com.alibaba.nacos.client.Worker.longPolling." + agent.getName());
            t.setDaemon(true);
            return t;
        }
    });

    executor.scheduleWithFixedDelay(new Runnable() {
   
        @Override
        public void run() {
   
            try {
   
            	// 这个方法是客户端的处理核心
            	// 每隔10ms就会调度一次这个方法
                checkConfigInfo();
            } catch (Throwable e) {
   
                LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e);
            }
        }
    }, 1L, 10L, TimeUnit.MILLISECONDS);
}
// 这里是采用分批的方式防止一次请求数据过大导致失败
public void checkConfigInfo() {
   
    // 分任务
    int listenerSize = cacheMap.get().size();
    // 向上取整为批数
    int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize());
    if (longingTaskCount > currentLongingTaskCount) {
   
        for (int i = (int) currentLongingTaskCount; i < longingTaskCount; i++) {
   
            // 要判断任务是否在执行 这块需要好好想想。 任务列表现在是无序的。变化过程可能有问题
            executorService.execute(new LongPollingRunnable(i));
        }
        currentLongingTaskCount = longingTaskCount;
    }
}
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
                // cacheMap:增加监听时,将具体的监听对应的data封装成CacheData缓存进cacheMap
                // 在封装CacheData时,会计算出CacheData对应的taskId
                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
                // 这里就是大名鼎鼎的长轮询
                // 通过长轮询的方式,从服务端获取data变化的dataId
                // 默认是30s超时
                List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList);

				// 获得变化的dataId清单之后,循环从server端查询最新的配置信息,并更新到本地的缓存文件中
                for (String groupKey : changedGroupKeys) {
   
                    String[] key = GroupKey.parseKey(groupKey);
                    String dataId = key[0];
                    String group = key[1]
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值