Spring Cloud Alibaba-nacos是怎么实现动态配置的

echo编辑整理,欢迎转载,转载请声明文章来源。欢迎添加echo微信(微信号:t2421499075) 交流学习。


首先来思考一个问题:nacos的动态配置实现,选用服务端推送完成还是选用客户端不断拉取完成?那种方式更合适?本节主要讲解明白这两个问题。可以直接看结尾的结论

动态配置描述

使用动态配置之前我们有讲过,忘记了或者不知道先回估计一下前面的实现动态配置的基本流程,文章名称《Spring Cloud Alibaba-使用nacos做配置中心》。我们知道,当我们修改注册中心的配置时,我们本地基本上马上就能获取到对应的修改后的配置。这就是我们今天要讲的动态配置。

到底是拉还是推

思考一卡,我们知道了配置中心修改了配置之后,客户端就能获取到最新配置,这到底是配置中心给客户端推送了信息还是客户端拉取到了配置信息?要搞清楚这个问题我们需要知道拉去看看源码

  • 准备一份源码
源码下载地址
https://github.com/alibaba/nacos/

找到NacosConfigService

在这里插入图片描述

我们看到配置类的实现里面维护了一个长轮训和一个代理连接,我们继续跟踪长轮训的代码如下:

在这里插入图片描述

里面总共做了三件事

  • 创建一个定时任务的线程池
  • 创建一个保持长连接的线程池
  • 创建一个延迟任务,每个10ms检查配置信息

这三件事情里面我们可以直接来看看检查配置信息的实现

/**
 * Check config info.
 */
public void checkConfigInfo() {
    // Dispatch taskes.
    int listenerSize = cacheMap.get().size();
    // Round up the longingTaskCount.
    int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize());
    if (longingTaskCount > currentLongingTaskCount) {
        for (int i = (int) currentLongingTaskCount; i < longingTaskCount; i++) {
            // The task list is no order.So it maybe has issues when changing.
            executorService.execute(new LongPollingRunnable(i));
        }
        currentLongingTaskCount = longingTaskCount;
    }
}

在代码中我们可以看到他执行了一个LongPollingRunnable的事件,那他做了什么呢?我们看他对应的run方法就知道了。

@Override
public void run() {
    
    List<CacheData> cacheDatas = new ArrayList<CacheData>();
    List<String> inInitializingCacheList = new ArrayList<String>();
    try {
        // 容错配置检查
        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);
                }
            }
        }
        
        // 检查配置中心的配置
        List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList);
        if (!CollectionUtils.isEmpty(changedGroupKeys)) {
            LOGGER.info("get changedGroupKeys:" + changedGroupKeys);
        }
        
        for (String groupKey : changedGroupKeys) {
            String[] key = GroupKey.parseKey(groupKey);
            String dataId = key[0];
            String group = key[1];
            String tenant = null;
            if (key.length == 3) {
                tenant = key[2];
            }
            try {
                String[] ct = getServerConfig(dataId, group, tenant, 3000L);
                CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
                cache.setContent(ct[0]);
                if (null != ct[1]) {
                    cache.setType(ct[1]);
                }
                LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}, type={}",
                        agent.getName(), dataId, group, tenant, cache.getMd5(),
                        ContentUtils.truncateContent(ct[0]), ct[1]);
            } catch (NacosException ioe) {
                String message = String
                        .format("[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s",
                                agent.getName(), dataId, group, tenant);
                LOGGER.error(message, ioe);
            }
        }
        
        // 比对是否有变化,有变化通知监听进行处理
        for (CacheData cacheData : cacheDatas) {
            if (!cacheData.isInitializing() || inInitializingCacheList
                    .contains(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant))) {
                cacheData.checkListenerMd5();
                cacheData.setInitializing(false);
            }
        }
        inInitializingCacheList.clear();
        
        executorService.execute(this);
        
    } catch (Throwable e) {
        
        // If the rotation training task is abnormal, the next execution time of the task will be punished
        LOGGER.error("longPolling error : ", e);
        executorService.schedule(this, taskPenaltyTime, TimeUnit.MILLISECONDS);
    }
}

看到这里我们对于动态配置的实现是不是慢慢的明了了呢?整个流程里面我们看到,都是客户端维护了一个长轮训,然后配置中心发生了变化,本地比对之后进行更新。

到底是拉去好还是推送好

我们已经确定了动态配置的实现就是客户端拉取的,那为什么会选用客户端拉取的方式?而不是服务端推送?

仔细思考一下,假如我们远程配置中心通过推送来解决这个问题,会有哪些有缺点?

推送解决的优缺点

优点:简单不需要客户端做什么操作
缺点:需要推送多客户端,服务段压力大

拉取解决的优缺点

优点:客户端分散了服务端的业务
缺点:长轮训

总结

  • 动态配置是客户端通过拉取的方式来实现的
  • 拉取的方式相比推送的方式,更可靠
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xlecho

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值