国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Nacos 的長輪詢定時機制,太好用了!

開發 前端
ConfigService 是 Nacos 客戶端提供的用于訪問實現配置中心基本操作的類,我們將從 ConfigService 的實例化開始長輪詢定時機制的源碼之旅。

今天這篇文章來介紹一下Nacos配置中心的原理之一:長輪詢機制的應用

為方便理解與表達,這里把 Nacos 控制臺和 Nacos 注冊中心稱為 Nacos 服務器(就是 web 界面那個),我們編寫的業務服務稱為 Nacso 客戶端;

Nacos 動態監聽的長輪詢機制原理圖,本篇將圍繞這張圖剖析長輪詢定時機制的原理:

圖片圖片

ConfigService 是 Nacos 客戶端提供的用于訪問實現配置中心基本操作的類,我們將從 ConfigService 的實例化開始長輪詢定時機制的源碼之旅;

1. 客戶端的長輪詢定時機制

我們從NacosPropertySourceLocator.locate()開始【斷點步入】:

圖片圖片

1.1 利用反射機制實例化 NacosConfigService 對象

客戶端的長輪詢定時任務是在 NacosFactory.createConfigService() 方法中,構建 ConfigService 對象實例時啟動的,我們接著 1.1 處的源碼;

進入 NacosFactory.createConfigService()

public static ConfigService createConfigService(Properties properties) throws NacosException {
    //【斷點步入】創建 ConfigService
    return ConfigFactory.createConfigService(properties);
}

進入 ConfigFactory.createConfigService(),發現其使用反射機制實例化 NacosConfigService 對象;

圖片圖片

1.2 NacosConfigService 的構造方法里啟動長輪詢定時任務

進入 NacosConfigService.NacosConfigService() 構造方法,里面設置了一些更遠程任務相關的屬性;

圖片圖片

1.2.1 初始化 HttpAgent

MetricsHttpAgent 類的設計如下:

圖片圖片

ServerHttpAgent 類的設計如下:

圖片圖片

1.2.2 初始化 ClientWorker

進入 ClientWorker.ClientWorker() 構造方法,主要是創建了兩個定時調度的線程池,并啟動一個定時任務;

圖片圖片

進入 ClientWorker.checkConfigInfo(),每隔 10s 檢查一次配置是否發生變化;

  • cacheMap:是一個 AtomicReference<Map<String, CacheData>> 對象,用來存儲監聽變更的緩存集合,key 是根據 datalD/group/tenant(租戶)拼接的值。Value 是對應的存儲在 Nacos 服務器上的配置文件的內容;
  • 長輪詢任務拆分:默認情況下,每個長輪詢 LongPollingRunnable 任務處理3000個監聽配置集。如果超過3000個,則需要啟動多個 LongPollingRunnable 去執行;

圖片圖片

1.3 檢查配置變更,讀取變更配置 LongPollingRunnable.run()

因為我們沒有這么多配置項,debug 不進去,所以直接找到 LongPollingRunnable.run() 方法,該方法的主要邏輯是:

  • 根據 taskld 對 cacheMap 進行數據分割;
  • 再通過 checkLocalConfig() 方法比較本地配置文件(在 ${user}\nacos\config\ 里)的數據是否存在變更,如果有變更則直接觸發通知;
public void run() {
    List<CacheData> cacheDatas = new ArrayList();
    ArrayList inInitializingCacheList = new ArrayList();
    try {
        //遍歷 CacheData,檢查本地配置
        Iterator var3 = ((Map)ClientWorker.this.cacheMap.get()).values().iterator();
        while(var3.hasNext()) {
            CacheData cacheData = (CacheData)var3.next();
            if (cacheData.getTaskId() == this.taskId) {
                cacheDatas.add(cacheData);
                try {
                    //檢查本地配置
                    ClientWorker.this.checkLocalConfig(cacheData);
                    if (cacheData.isUseLocalConfigInfo()) {
                        cacheData.checkListenerMd5();
                    }
                } catch (Exception var13) {
                    ClientWorker.LOGGER.error("get local config info error", var13);
                }
            }
        }
        //【斷點步入 1.3.1】通過長輪詢請求檢查服務端對應的配置是否發生變更
        List<String> changedGroupKeys = ClientWorker.this.checkUpdateDataIds(cacheDatas, inInitializingCacheList);
        //遍歷存在變更的 groupKey,重新加載最新數據
        Iterator var16 = changedGroupKeys.iterator();
        while(var16.hasNext()) {
            String groupKey = (String)var16.next();
            String[] key = GroupKey.parseKey(groupKey);
            String dataId = key[0];
            String group = key[1];
            String tenant = null;
            if (key.length == 3) {
                tenant = key[2];
            }
            try {
                //【斷點步入 1.3.2】讀取變更配置,這里的 dataId、group 和 tenant 是【1.3.1】里獲取的
                String content = ClientWorker.this.getServerConfig(dataId, group, tenant, 3000L);
                CacheData cache = (CacheData)((Map)ClientWorker.this.cacheMap.get()).get(GroupKey.getKeyTenant(dataId, group, tenant));
                cache.setContent(content);
                ClientWorker.LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, cnotallow={}", new Object[]{ClientWorker.this.agent.getName(), dataId, group, tenant, cache.getMd5(), ContentUtils.truncateContent(content)});
            } catch (NacosException var12) {
                String message = String.format("[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s", ClientWorker.this.agent.getName(), dataId, group, tenant);
                ClientWorker.LOGGER.error(message, var12);
            }
        }
        //觸發事件通知
        var16 = cacheDatas.iterator();
        while(true) {
            CacheData cacheDatax;
            do {
                if (!var16.hasNext()) {
                    inInitializingCacheList.clear();
                    //繼續定時執行當前線程
                    ClientWorker.this.executorService.execute(this);
                    return;
                }
                cacheDatax = (CacheData)var16.next();
            } while(cacheDatax.isInitializing() && !inInitializingCacheList.contains(GroupKey.getKeyTenant(cacheDatax.dataId, cacheDatax.group, cacheDatax.tenant)));
            cacheDatax.checkListenerMd5();
            cacheDatax.setInitializing(false);
        }
    } catch (Throwable var14) {
        ClientWorker.LOGGER.error("longPolling error : ", var14);
        ClientWorker.this.executorService.schedule(this, (long)ClientWorker.this.taskPenaltyTime, TimeUnit.MILLISECONDS);
    }
}

注意:這里的斷點需要在 Nacos 服務器上修改配置(間隔大于 30s),進入后才好理解;

1.3.1 檢查配置變更 ClientWorker.checkUpdateDataIds()

我們點進 ClientWorker.checkUpdateDataIds() 方法,發現其最終調用的是 ClientWorker.checkUpdateConfigStr() 方法,其實現邏輯與源碼如下:

  • 通過 MetricsHttpAgent.httpPost() 方法(上面 1.2.1 有提到)調用 /v1/cs/configs/listener 接口實現長輪詢請求;
  • 長輪詢請求在實現層面只是設置了一個比較長的超時時間,默認是 30s;
  • 如果服務端的數據發生了變更,客戶端會收到一個 HttpResult ,服務端返回的是存在數據變更的 Data ID、Group、Tenant;
  • 獲得這些信息之后,在 LongPollingRunnable.run() 方法中調用 getServerConfig() 去 Nacos 服務器上讀取具體的配置內容;
List<String> checkUpdateConfigStr(String probeUpdateString, boolean isInitializingCacheList) throws IOException {
    List<String> params = Arrays.asList("Listening-Configs", probeUpdateString);
    List<String> headers = new ArrayList(2);
    headers.add("Long-Pulling-Timeout");
    headers.add("" + this.timeout);
    if (isInitializingCacheList) {
        headers.add("Long-Pulling-Timeout-No-Hangup");
        headers.add("true");
    }
    if (StringUtils.isBlank(probeUpdateString)) {
        return Collections.emptyList();
    } else {
        try {
            //調用 /v1/cs/configs/listener 接口實現長輪詢請求,返回的 HttpResult 里包含存在數據變更的 Data ID、Group、Tenant
            HttpResult result = this.agent.httpPost("/v1/cs/configs/listener", headers, params, this.agent.getEncode(), this.timeout);
            if (200 == result.code) {
                this.setHealthServer(true);
                //
                returnthis.parseUpdateDataIdResponse(result.content);
            }
            this.setHealthServer(false);
            LOGGER.error("[{}] [check-update] get changed dataId error, code: {}", this.agent.getName(), result.code);
        } catch (IOException var6) {
            this.setHealthServer(false);
            LOGGER.error("[" + this.agent.getName() + "] [check-update] get changed dataId exception", var6);
            throw var6;
        }
        return Collections.emptyList();
    }
}

1.3.2 讀取變更配置 ClientWorker.getServerConfig()

進入 ClientWorker.getServerConfig() 方法;讀取服務器上的變更配置;最終調用的是 MetricsHttpAgent.httpGet() 方法(上面 1.2.1 有提到),調用 /v1/cs/configs 接口獲取配置;然后通過調用 LocalConfigInfoProcessor.saveSnapshot() 將變更的配置保存到本地;

圖片圖片

圖片圖片

2. 服務端的長輪詢定時機制

2.1 服務器接收請求 ConfigController.listener()

Nacos客戶端 通過 HTTP 協議與服務器通信,那么在服務器源碼里必然有對應接口的實現;

在 nacos-config 模塊下的 controller 包,提供了個 ConfigController 類來處理請求,其中有個 /listener 接口,是客戶端發起數據監聽的接口,其主要邏輯和源碼如下:

  • 獲取客戶端需要監聽的可能發生變化的配置,并計算 MD5 值;
  • ConfigServletInner.doPollingConfig() 開始執行長輪詢請求;

2.2 執行長輪詢請求 ConfigSer

圖片圖片

vletInner.doPollingConfig()

進入 ConfigServletInner.doPollingConfig() 方法,該方法封裝了長輪詢的實現邏輯,同時兼容短輪詢邏輯;

圖片圖片

進入 LongPollingService.addLongPollingClient() 方法,里面是長輪詢的核心處理邏輯,主要作用是把客戶端的長輪詢請求封裝成 ClientPolling 交給 scheduler 執行;

圖片圖片

2.3 創建線程執行定時任務 ClientLongPolling.run()

我們找到 ClientLongPolling.run() 方法,這里可以體現長輪詢定時機制的核心原理,通俗來說,就是:

  • 服務端收到請求之后,不立即返回,沒有變更則在延后 (30-0.5)s 把請求結果返回給客戶端;
  • 這就使得客戶端和服務端之間在 30s 之內數據沒有發生變化的情況下一直處于連接狀態;

圖片圖片

2.4 監聽配置變更事件

2.4.1 監聽 LocalDataChangeEvent 事件的實現

當我們在 Nacos 服務器或通過 API 方式變更配置后,會發布一個 LocalDataChangeEvent 事件,該事件會被 LongPollingService 監聽;

這里 LongPollingService 為什么具有監聽功能在 1.3.1 版本后有些變化:

  • 1.3.1 前LongPollingService.onEvent()
  • 1.3.1 后Subscriber.onEvent()

在 Nacos 1.3.1 版本之前,通過 LongPollingService 繼承 AbstractEventListener 實現監聽,覆蓋 onEvent() 方法;

圖片圖片

而在 1.3.2 版本之后,通過構造訂閱者實現

圖片圖片

效果是一樣的,實現了對 LocalDataChangeEvent 事件的監聽,并通過通過線程池執行 DataChangeTask 任務;

2.4.2 監聽事件后的處理邏輯 DataChangeTask.run()

我們找到 DataChangeTask.run() 方法,這個線程任務實現了

圖片圖片

3. 源碼結構圖小結

3.1 客戶端的長輪詢定時機制

NacosPropertySourceLocator.locate() :初始化 ConfigService 對象,定位配置;

  • NacosConfigService.NacosConfigService() :NacosConfigService 的構造方法;
  • Executors.newScheduledThreadPool() :創建 executor 線程池;
  • Executors.newScheduledThreadPool() :創建 executorService 線程池;
  • ClientWorker.checkConfigInfo() :使用 executor 線程池檢查配置是否發生變化;
  • ClientWorker.checkLocalConfig() :檢查本地配置;
  • ClientWorker.checkUpdateDataIds() :檢查服務端對應的配置是否發生變更;
  • ClientWorker.getServerConfig() :讀取變更配置
  • MetricsHttpAgent.httpPost() :調用 /v1/cs/configs/listener 接口實現長輪詢請求;
  • ClientWorker.checkUpdateConfigStr() :檢查服務端對應的配置是否發生變更;
  • MetricsHttpAgent.httpGet() :調用 /v1/cs/configs 接口獲取配置;
  • LongPollingRunnable.run() :運行長輪詢定時線程;
  • MetricsHttpAgent.MetricsHttpAgent() :初始化 HttpAgent;
  • ClientWorker.ClientWorker() :初始化 ClientWorker;
  • NacosFactory.createConfigService() :創建配置服務器;
  • ConfigFactory.createConfigService() :利用反射機制創建配置服務器;

3.2 服務端的長輪詢定時機制

ConfigController.listener() :服務器接收請求;

  • LongPollingService.addLongPollingClient() :長輪詢的核心處理邏輯,提前 500ms 返回響應;
  • ClientLongPolling.run() :長輪詢定時機制的實現邏輯;
  • Map.put() :將 ClientLongPolling 實例本身添加到 allSubs 隊列中;
  • Queue.remove() :把 ClientLongPolling 實例本身從 allSubs 隊列中移除;
  • MD5Util.compareMd5() :比較數據的 MD5 值;
  • LongPollingService.sendResponse() :將變更的結果通過 response 返回給客戶端;
  • ConfigExecutor.scheduleLongPolling() :啟動定時任務,延時時間為 29.5s;
  • HttpServletRequest.getHeader() :獲取客戶端設置的請求超時時間;
  • MD5Util.compareMd5() :和服務端的數據進行 MD5 對比;
  • ConfigExecutor.executeLongPolling() :創建 ClientLongPolling 線程執行定時任務;
  • MD5Util.getClientMd5Map() :計算 MD5 值;
  • ConfigServletInner.doPollingConfig() :執行長輪詢請求;

3.3 Nacos 服務器配置變更的事件監聽

Nacos 服務器上的配置發生變更后,發布一個 LocalDataChangeEvent 事件;

Subscriber.onEvent() :監聽 LocalDataChangeEvent 事件(1.3.2 版本后);

DataChangeTask.run() :根據 groupKey 返回配置;

ConfigExecutor.executeLongPolling() :通過線程池執行 DataChangeTask 任務。

責任編輯:武曉燕 來源: 碼猿技術專欄
相關推薦

2022-07-14 08:36:28

NacosApollo長輪詢

2025-09-29 09:32:32

2024-12-13 16:01:35

2025-07-29 09:36:51

2024-05-11 09:38:05

React編譯器React 19

2022-05-31 09:42:49

工具編輯器

2021-04-22 09:56:32

MYSQL開發數據庫

2022-08-01 07:02:06

SpringEasyExcel場景

2020-12-29 10:45:55

開發設計代碼

2020-06-23 15:58:42

心電圖

2022-09-06 10:52:04

正則庫HumrePython

2021-08-11 09:33:15

Vue 技巧 開發工具

2021-03-18 10:12:54

JavaCompletable字符串

2021-09-10 10:15:24

Python人臉識別AI

2022-05-11 14:43:37

WindowsPython服務器

2021-03-19 09:48:10

Jupyter Not插件Python

2020-11-10 06:11:59

工具軟件代碼

2022-07-25 06:42:24

分布式鎖Redis

2010-07-14 09:55:27

JavaScript

2021-03-02 20:42:20

實戰策略
點贊
收藏

51CTO技術棧公眾號

久久国产免费| 视频一区二区三区在线观看| 久久久国产精品x99av| 久久久亚洲综合网站| 国产精品精品久久久久久| 精品肉辣文txt下载| 亚洲在线观看免费视频| 国产精品igao| www欧美成人18+| 日本女人高潮视频| 久久大逼视频| 精品午夜一区二区| 国产欧美日韩一区二区三区在线| 97在线观看视频| 亚洲啊v在线免费视频| 亚洲视频欧洲视频| 丁香婷婷久久| 北条麻妃一区二区三区中文字幕 | 欧美日本一区| 亚洲综合一区二区不卡| 国内久久视频| 视频一区二区三区免费观看| 久久se精品一区精品二区| 正在播放一区二区三区| 国产不卡高清在线观看视频| 高清在线观看免费| 久久久www成人免费毛片麻豆 | 久久影院理伦片| 在线免费观看色| 国产日韩三级| 精品久久中文字幕久久av| 手机成人在线| 中文无字幕一区二区三区| 久久久久资源| 石原莉奈一区二区三区在线观看| 欧美日韩综合久久| 国产乱码一区二区三区| 国产一区二区网| 国产精品久久久久影视| 麻豆电影在线观看| 欧美日韩国产三级| 中文av在线全新| 欧美理论片在线观看| 国产一区99| 蜜桃久久精品乱码一区二区 | www亚洲天堂| 狠狠做深爱婷婷久久综合一区 | 亚洲欧美视频在线观看视频| 在线视频中文字幕久| 在线观看视频一区| sm捆绑调教国产免费网站在线观看| 亚洲人成电影网站| 国产精品久久久久久久久久久久久久久| 国内自拍欧美激情| 国色天香一区二区| 免费观看国产视频在线| 亚洲欧美中日韩| jizz亚洲| 深夜福利国产精品| 99久久亚洲精品| 强伦女教师2:伦理在线观看| 中文一区二区在线观看| lutube成人福利在线观看| 亚洲免费视频在线观看| 麻豆国产欧美一区二区三区r| 国产精品18毛片一区二区| 成人av资源在线观看| 污视频在线观看免费| 在线不卡国产精品| 欧美日韩免费| 一区二区在线播放视频| 欧美日本一区二区三区| 日韩欧美另类中文字幕| 精品国产乱码久久久久久蜜柚| 99re热这里只有精品视频| 久久视频www| 欧美国产日本在线| 美女视频一区在线观看| 蜜臀在线观看| 欧美成人久久久| 日韩精品福利网| 在线国产中文字幕| 麻豆乱码国产一区二区三区| 国产精品嫩草99av在线| 又黄又爽毛片免费观看| 亚洲男人第一网站| 婷婷综合激情| 成人18网站| 国产一区二区动漫| 一区二区91| 在线观看高清av| 欧美肥臀大乳一区二区免费视频| 免费在线成人| 日韩精品欧美激情| 外国成人在线视频| 自拍偷拍电影| 女女同性女同一区二区三区91| 日韩欧美亚洲综合| 日本黄色免费在线| 精品99又大又爽又硬少妇毛片 | 国产成人精品影视| 玛雅亚洲电影| 456亚洲精品成人影院| 久久国产成人精品国产成人亚洲| 欧美乱大交xxxxx另类电影| 欧美日韩精品在线视频| 超碰成人免费| 瑟瑟在线观看| 97超级碰碰人国产在线观看| 欧美影院三区| 污污视频网站免费观看| 天天影视色香欲综合网老头| 91麻豆精品一二三区在线| 裸体大乳女做爰69| 在线免费不卡电影| 久久精品国产第一区二区三区| 男人插女人欧美| 伊人久久精品视频| 红桃成人av在线播放| 可以免费看的黄色网址| 在线一区二区视频| 国内精品久久久久国产盗摄免费观看完整版| 日韩精品久久久免费观看| 中文字幕字幕中文在线中不卡视频| 欧美影院精品| 99在线观看视频免费| 欧美日韩一区不卡| 欧美激情三级| 天天做天天爱天天高潮| 成人黄色在线网站| 国产二区三区在线| 午夜精品亚洲一区二区三区嫩草 | 午夜精品免费在线观看| 日本高清免费电影一区| 色喇叭免费久久综合网| 特黄特色欧美大片| 丁香花在线高清完整版视频| 成人午夜精品| 欧美a一欧美| 国产黄色影视| av在线下载| 欧美性色视频在线| avtt久久| 久久午夜夜伦鲁鲁一区二区| 精品伊人久久97| 国产91一区二区三区| 欧美日韩日本视频| 蜜臀在线免费观看| 粉嫩精品一区二区三区在线观看| 精品伊人久久久久7777人| 精品黄色免费中文电影在线播放| 伊人久久在线观看| 亚洲精品视频免费在线观看| 久久青草国产手机看片福利盒子| 成人激情在线| 一区二区三区免费在线看| 伦一区二区三区中文字幕v亚洲| 男人的天堂在线| 国产成人精品自拍| 久久这里只有精品99| 欧美日韩国产精选| 欧美成va人片在线观看| 欧美经典影片视频网站| 三级a在线观看| 国产精品99久久久久久人| 亚洲制服丝袜av| 亚洲区综合中文字幕日日| 成人77777| 亚洲午夜精品久久久中文影院av| 日韩av网站导航| 国产亚洲精品bt天堂精选| 日韩精品水蜜桃| 超级碰碰不卡在线视频| 不卡影院一区二区| 成人在线激情视频| 精品国产1区二区| 国产亚洲视频系列| 欧美啪啪一区| 第四色日韩影片| 玩弄japan白嫩少妇hd| 欧美亚州一区二区三区| 欧美亚洲自拍偷拍| 高清视频一区二区| 日韩电影免费在线观看| zzzwww在线看片免费| wwwav91| 亚洲一区二区三区久久 | 精品亚洲永久免费精品| 成人精品视频.| 天天做天天爱天天综合网2021 | 91欧美日韩一区| 日韩高清a**址| 一区二区在线观看免费| 日韩成人午夜电影| 国产福利一区二区精品秒拍| 日本亚洲精品| 99riav视频| 中文字幕在线亚洲三区| 国产精品美女免费| 国产亚洲福利一区|