nacos源码1-配置中心-客户端

nacos官方主页

一 结构

结构.png

二 ConfigFactory

  • 通过ConfigFactory方法,创建NacosConfigService,作为配置管理的入口对象。
  • 一种是通过自定义配置kv方式指定属性,另一种指定配置服务端地址方式。
public class ConfigFactory {
    public static ConfigService createConfigService(Properties properties) throws NacosException {
        ...
    }

    public static ConfigService createConfigService(String serverAddr) throws NacosException {
        ...
    }
}

三 NacosConfigService

3.1 配置管理

3.1.1 配置发布,删除

接口文档配置请求参数,调用ServerHttpAgent接口

3.1.2 配置获取

  • 优先级最高使用failvoer配置
String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
  • 次之从配置服务端获取配置
    clientWorker.getServerConfig(dataId, group, tenant, timeoutMs)
  • 最低优先级获取本地snapshot配置
content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant)

3.2 配置变更监听管理

调用ClientWork的配置变更监听添加删除接口

四 ClientWork

4.1 获取配置接口

调用ServerHttpAgent接口获取配置,获取成功则保存或删除本地snapshot配置。

HttpResult result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);
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;
...
}

4.2 配置变更监听存储

  • 存储监听变更的配置集
    AtomicReference<Map<String/*group key*/, CacheData>> cacheMap
  • group key生成函数
static public String getKeyTenant(String dataId, String group, String tenant) {
        StringBuilder sb = new StringBuilder();
        urlEncode(dataId, sb);
        sb.append('+');
        urlEncode(group, sb);
        if (StringUtils.isNotEmpty(tenant)) {
            sb.append('+');
            urlEncode(tenant, sb);
        }
        return sb.toString();
    }

4.3 配置变更监听处理

4.3.1 定时任务

  • 初始化时,创建10ms定时任务,启动LongPullingRunnable任务
  • 每个LongPullingRunnable任务默认处理3000个监听配置集
  • 根据总监听数量计算需要启动的LongPullingRunnable任务数
  • currentLongingTaskCount保存已启动的LongPullingRunnable任务数
  • 根据需要启动新的LongPullingRunnable任务数。
executor.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                try {
                    checkConfigInfo();
                } catch (Throwable e) {
                    log.error(agent.getName(), "NACOS-XXXX", "[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 LongPullingRunnable(i));
            }
            currentLongingTaskCount = longingTaskCount;
        }
    }

4.3.2 LongPullingRunnable

4.3.2.1 failover配置变更监听

// check failover config
                for (CacheData cacheData : cacheMap.get().values()) {
                    if (cacheData.getTaskId() == taskId) {
                        cacheDatas.add(cacheData);
                        try {
                            checkLocalConfig(cacheData);
                            if (cacheData.isUseLocalConfigInfo()) {
                                cacheData.checkListenerMd5();
                            }
                        } catch (Exception e) {
                            log.error("NACOS-CLIENT", "get local config info error", e);
                        }
                    }
                }
  • 没有->有,有->有且存在变更,更新cachData配置信息
cacheData.setUseLocalConfigInfo(true);
cacheData.setLocalConfigInfoVersion(path.lastModified());
cacheData.setContent(content);
  • 有->没有
    cacheData.setUseLocalConfigInfo(false); 从服务端获取配置。

4.3.2.2 服务端配置变更监听

  • checkUpdateDataIds() 遍历本线程处理的cachData,对其中无本地failover配置的项,组装配置集串,格式为dataId2Group2contentMD52tenant1或者dataId2Group2contentMD5^1。设置Listening-Configs请求参数。
  • checkUpdateConfigStr()执行longpulling
    Long-Pulling-Timeout头指定请求超时时间,默认30s
    Long-Pulling-Timeout-No-Hangup首次监听时配置,告知服务端无论是否有变更都立即返回
  • parseUpdateDataIdResponse()解析服务端返回有变更的配置集。格式与请求参数Listening-Configs格式相同

4.3.2.3 配置变更监听回调

  • 遍历便跟的配置集,从服务端获取对应配置。更新cachdata
String content = getServerConfig(dataId, group, tenant, 3000L);
                        CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
                        cache.setContent(content);
  • 遍历cachData列表,通过配置数据的md5检查是否发生变更,触发变更回调。
void checkListenerMd5() {
        for (ManagerListenerWrap wrap : listeners) {
            if (!md5.equals(wrap.lastCallMd5)) {
                safeNotifyListener(dataId, group, content, md5, wrap);
            }
        }
    }
  • 监听回调
    如果listener提供了线程执行器,则异步线程调用变更回调,否则在当前LongPullingRunnable线程调用。
Runnable job = new Runnable() {
            public void run() {
                ...
                listener.receiveConfigInfo(contentTmp);
                listenerWrap.lastCallMd5 = md5;
                ...
            }
        };

if (null != listener.getExecutor()) {
                listener.getExecutor().execute(job);
            } else {
                job.run();
            }

五 ServerHttpAgent

5.1 ServerListManager配置服务端管理

5.1.1 配置服务器地址

  • List<String> serverUrls保存服务端地址。
  • ServerAddressIterator对服务端地址随机排序。
  • currentServerAddr存储上次访问成功的服务端地址。尽量保证longpulling配置变更监听的地址和获取配置的是同一个服务器。
  • 访问失败则调用refreshCurrentServerAddr 更新下次使用的服务端地址。
public void refreshCurrentServerAddr() {
        currentServerAddr = iterator().next();
    }

    public String getCurrentServerAddr() {
        if (StringUtils.isBlank(currentServerAddr)) {
            currentServerAddr = iterator().next();
        }
        return currentServerAddr;
    }

5.1.2 配置地址服务器

使用定时线程,从地址服务器获取最新配置服务器地址列表,更新本地缓存List<String> serverUrls

5.2 Limiter调用限速

  • 初始化最多对1000个url请求限速。
  • 每次限速区间1分钟
private static int CAPACITY_SIZE = 1000;
    private static Cache<String, RateLimiter> cache = CacheBuilder.newBuilder()
        .initialCapacity(CAPACITY_SIZE).expireAfterAccess(1, TimeUnit.MINUTES)
        .build();
  • 每个限速区间限制5次调用
rateLimiter = cache.get(accessKeyID, new Callable<RateLimiter>() {
                @Override
                public RateLimiter call() throws Exception {
                    return RateLimiter.create(5);
                }
            });
  • 每次请求获取令牌时间最多等待1s
    rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容

  • 简介摘要SOFARPC服务调用创建服务引用配置ConsumerConfig,自定义设置接口名称、调用协议、直连调用...
    鋒Nic阅读 9,696评论 1 7
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,096评论 1 32
  • Java继承关系初始化顺序 父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父...
    第六象限阅读 2,152评论 0 9
  • 一、简历准备 1、个人技能 (1)自定义控件、UI设计、常用动画特效 自定义控件 ①为什么要自定义控件? Andr...
    lucas777阅读 5,200评论 2 54
  • 祖宗19代 光听名字就觉得很好玩,是不是?不错,它的确是一部喜剧片,由红遍宇宙的小岳岳--岳云鹏主演。名字虽很搞笑...
    大雁非阅读 137评论 0 0