nacos源码2-配置中心-服务端

一 配置获取

  • 获取目标配置集读锁,避免和并发写冲突
    int lockResult = tryConfigReadLock(request, response, groupKey);
    tryConfigReadLock调用 tryReadLock尝试获取读锁,最多尝试10次,每次获取失败sleep 1ms。
lockResult值 含义
0 表示不存在目标配置集
大于0 获取锁成功
小雨0 获取锁失败,已经有写锁。
 static public int tryReadLock(String groupKey) {
        CacheItem groupItem = CACHE.get(groupKey);
        int result = (null == groupItem) ? 0 : (groupItem.rwLock.tryReadLock() ? 1 : -1);
        if (result < 0) {
            defaultLog.warn("[read-lock] failed, {}, {}", result, groupKey);
        }
        return result;
    }
  • 获取配置
    根据key获取配置的相关信息CacheItem
    非集群模式从数据库获取配置,集群模式从本地缓存文件获取配置
CacheItem cacheItem = ConfigService.getContentCache(groupKey);
md5 = cacheItem.getMd5();
                            lastModified = cacheItem.getLastModifiedTs();
                            if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) {
                                configInfoBase = persistService.findConfigInfo(dataId, group, tenant);
                            } else {
                                file = DiskUtil.targetFile(dataId, group, tenant);
                            }

二 配置变更

配置和监听流程图.png

2.1异步通知

  • 配置变更时,先更改数据库信息,同时更改配置表和添加一条配置历史记录。然后走异步通知所有服务端更新本地缓存文件配置。
addConfiTagsRelationAtomic(configId, configTags, configInfo.getDataId(), configInfo.getGroup(),
                        configInfo.getTenant());
                    insertConfigHistoryAtomic(0, configInfo, srcIp, srcUser, time, "I");
  • 一次变更触发一个AsyncTask线程任务
    遍历配置服务端列表,依次发送变更通知。
    发送失败或当前服务端连接不稳定,则延迟重发。
  • 延迟时间获取函数
private static int getDelayTime(NotifySingleTask task) {
        int failCount = task.getFailCount();
        int delay = MINRETRYINTERVAL + failCount * failCount * INCREASESTEPS;
        if (failCount <= MAXCOUNT) {
            task.setFailCount(failCount + 1);
        }
        return delay;
    }

    private static int MINRETRYINTERVAL = 500;
    private static int INCREASESTEPS = 1000;
    private static int MAXCOUNT = 6;

2.2 文件缓存

  • 创建缓存任务
public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp,
                     boolean isBeta) {
        String groupKey = GroupKey2.getKey(dataId, group, tenant);
        dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));
    }
  • DumpProcessor执行缓存任务
    获取写锁
    更新缓存配置文件
    更新内存缓存cachItem的md5
ConfigService.dump(dataId, group, tenant, cf.getContent(), lastModified)

dump(String dataId, String group, String tenant, String content, long lastModifiedTs) {
final int lockResult = tryWriteLock(groupKey);
DiskUtil.saveToDisk(dataId, group, tenant, content);
updateMd5(groupKey, md5, lastModifiedTs);
}

2.3 变更监听响应

  • 创建线程执行DataChangeTask
  • final Queue<ClientLongPulling> allSubs,存储所有监听配置变更的客户端长轮询信息ClientLongPulling。
    final Map<String, String> clientMd5Map; 客户端监听的配置集集合
  • 遍历监听列表,监听当前变更的配置集,则发送响应
LongPollingService.DataChangeTask.run() {
for (Iterator<ClientLongPulling> iter = allSubs.iterator(); iter.hasNext(); ) {
                    ClientLongPulling clientSub = iter.next();
                    if (clientSub.clientMd5Map.containsKey(groupKey)) {
                        iter.remove(); // 删除订阅关系
                        clientSub.sendResponse(Arrays.asList(groupKey));
                    }
                }
}
  • 组装变更的配置集信息,asyncContext.complete(); 发送响应

三 配置变更监听

3.1 短轮询

  • 解析监听的配置集集合
  • 检查客户端md5和当前缓存的cachItem的md5,放回变更配置集集合

3.2 长轮询

  • 获取长轮询超时时间,和立即返回参数
  • 非固定周期轮询,则先判断是否有配置变更或有LONG_PULLING_NO_HANG_UP_HEADER,有则立即返回变更的配置集集合
  • http请求异步化
    final AsyncContext asyncContext = req.startAsync()
  • 启动异步线程ClientLongPulling
    延迟线程,对超时无变更的客户端返回响应,避免请求超时。
    延迟线程中,对isFixedPolling()固定轮询的情况会先检查是否有配置变更再返回结果。
    在allSubs中保存监听变更的客户端信息。 在2.3节配置变更时进行响应。
public void addLongPullingClient(HttpServletRequest req, HttpServletResponse rsp, Map<String, String> clientMd5Map,
                                     int probeRequestSize) {
String str = req.getHeader(LongPollingService.LONG_PULLING_HEADER);
        String noHangUpFlag = req.getHeader(LongPollingService.LONG_PULLING_NO_HANG_UP_HEADER);
        String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER);
        String tag = req.getHeader("Vipserver-Tag");
        int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500);
        /**
         * 提前500ms返回响应,为避免客户端超时 @qiaoyi.dingqy 2013.10.22改动  add delay time for LoadBalance
         */
        long timeout = Math.max(10000, Long.parseLong(str) - delayTime);
        if (isFixedPolling()) {
            timeout = Math.max(10000, getFixedPollingInterval());
            // do nothing but set fix polling timeout
        } else {
            long start = System.currentTimeMillis();
            List<String> changedGroups = MD5Util.compareMd5(req, rsp, clientMd5Map);
            if (changedGroups.size() > 0) {
                generateResponse(req, rsp, changedGroups);
                LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}",
                    System.currentTimeMillis() - start, "instant", RequestUtil.getRemoteIp(req), "polling",
                    clientMd5Map.size(), probeRequestSize, changedGroups.size());
                return;
            } else if (noHangUpFlag != null && noHangUpFlag.equalsIgnoreCase(TRUE_STR)) {
                LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}", System.currentTimeMillis() - start, "nohangup",
                    RequestUtil.getRemoteIp(req), "polling", clientMd5Map.size(), probeRequestSize,
                    changedGroups.size());
                return;
            }
        }
        String ip = RequestUtil.getRemoteIp(req);
        // 一定要由HTTP线程调用,否则离开后容器会立即发送响应
        final AsyncContext asyncContext = req.startAsync();
        // AsyncContext.setTimeout()的超时时间不准,所以只能自己控制
        asyncContext.setTimeout(0L);

        scheduler.execute(
            new ClientLongPulling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag));
}

四 监听查询

4.1 获取监听指定配置集的客户端信息

  • 入口GET /v1/cs/configs/listener 获取监听配置集变更的客户端信息
  • 遍历配置服务端列表, GET /v1/cs/communication/configWatchers 从服务端的final Queue<ClientLongPulling> allSubs中获取配置集的监听客户端信息
  • 合并所有服务端数据。

4.2 获取指定客户端监听的所有配置集信息

  • 入口GET /v1/cs/listener
  • 遍历服务端列表,GET /v1/cs/communication/watcherConfigs,从allSubs中获取客户端监听的所有配置集
  • 合并所有服务端数据。

五 容量限制

  • 使用aop方式对controller接口层的配置发布和删除进行控制。
  • tenant_capacity 存储租户的配置容量
  • group_capacity 存储分组的配置容量,若group_id=""则表示集群的配置容量
    code | 说明
    ---- | ----
    OVER_CLUSTER_QUOTA|超过集群配置个数上限
    OVER_GROUP_QUOTA|超过该Group配置个数上限
    OVER_TENANT_QUOTA|超过该租户配置个数上限
    OVER_MAX_SIZE|超过配置的内容大小上限
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容