Spring Cloud 2.2.2 源碼之三十四nacos客戶端LongPollingRunnable配置更新二

Spring Cloud 2.2.2 源碼之三十四nacos客戶端LongPollingRunnable配置更新二

獲取更新的配置文件

前面的checkUpdateDataIds只是獲取有更新的配置文件名,不是配置文件內容,所以後面還要去判斷哪些有更新,再去獲取內容。這裏會立即返回內容的,不會掛去。


	//輪詢有配置改變的,然後去獲取內容
   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 {//有更新的就獲取一次配置內容,超時時間3秒
                        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]);//設置配置類型
                        }
                       ...
                    } catch (NacosException 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);

CacheData的checkListenerMd5

這裏就是遍歷所有的監聽器,然後看配置內容是否有變化,有的話就進行通知。

    void checkListenerMd5() {
        for (ManagerListenerWrap wrap : listeners) {
            if (!md5.equals(wrap.lastCallMd5)) {//有改變的話就通知
                safeNotifyListener(dataId, group, content, type, md5, wrap);
            }
        }
    }

safeNotifyListener通知

創建了一個任務,封裝好信息,調用監聽器的receiveConfigInfo方法接受數據處理。然後修改內容和MD5。這裏他設置了一下類加載器,包裝和監聽器的類加載器一樣,可能跟SPI反射調用相關。

 private void safeNotifyListener(final String dataId, final String group, final String content, final String type,
                                    final String md5, final ManagerListenerWrap listenerWrap) {
        final Listener listener = listenerWrap.listener;

        Runnable job = new Runnable() {
            @Override
            public void run() {
                ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader();
                ClassLoader appClassLoader = listener.getClass().getClassLoader();
                try {
                    if (listener instanceof AbstractSharedListener) {
                        AbstractSharedListener adapter = (AbstractSharedListener) listener;
                        adapter.fillContext(dataId, group);
                        LOGGER.info("[{}] [notify-context] dataId={}, group={}, md5={}", name, dataId, group, md5);
                    }
                    // 執行回調之前先將線程classloader設置爲具體webapp的classloader,以免回調方法中調用spi接口是出現異常或錯用(多應用部署纔會有該問題)。
                    Thread.currentThread().setContextClassLoader(appClassLoader);

                    ConfigResponse cr = new ConfigResponse();
                    cr.setDataId(dataId);
                    cr.setGroup(group);
                    cr.setContent(content);
                    configFilterChainManager.doFilter(null, cr);
                    String contentTmp = cr.getContent();
                    listener.receiveConfigInfo(contentTmp);

                    // compare lastContent and content
                    if (listener instanceof AbstractConfigChangeListener) {
                        Map data = ConfigChangeHandler.getInstance().parseChangeData(listenerWrap.lastContent, content, type);
                        ConfigChangeEvent event = new ConfigChangeEvent(data);
                        ((AbstractConfigChangeListener)listener).receiveConfigChange(event);
                        listenerWrap.lastContent = content;
                    }

                    listenerWrap.lastCallMd5 = md5;
                    LOGGER.info("[{}] [notify-ok] dataId={}, group={}, md5={}, listener={} ", name, dataId, group, md5,
                        listener);
                } catch (NacosException de) {
                    LOGGER.error("[{}] [notify-error] dataId={}, group={}, md5={}, listener={} errCode={} errMsg={}", name,
                        dataId, group, md5, listener, de.getErrCode(), de.getErrMsg());
                } catch (Throwable t) {
                    LOGGER.error("[{}] [notify-error] dataId={}, group={}, md5={}, listener={} tx={}", name, dataId, group,
                        md5, listener, t.getCause());
                } finally {
                    Thread.currentThread().setContextClassLoader(myClassLoader);
                }
            }
        };

        final long startNotify = System.currentTimeMillis();
        try {
            if (null != listener.getExecutor()) {
                listener.getExecutor().execute(job);
            } else {
                job.run();//一般是直接運行的
            }
        } catch (Throwable t) {
            LOGGER.error("[{}] [notify-error] dataId={}, group={}, md5={}, listener={} throwable={}", name, dataId, group,
                md5, listener, t.getCause());
        }
        final long finishNotify = System.currentTimeMillis();
        LOGGER.info("[{}] [notify-listener] time cost={}ms in ClientWorker, dataId={}, group={}, md5={}, listener={} ",
            name, (finishNotify - startNotify), dataId, group, md5, listener);
    }

監聽器處理

其實就是這塊,添加記錄,通知刷新事件,這裏RefreshEventListener會被通知,裏面會進行刷新。
在這裏插入圖片描述
在這裏插入圖片描述
具體怎麼刷下篇說吧。

好了,今天就到這裏了,希望對學習理解有幫助,大神看見勿噴,僅爲自己的學習理解,能力有限,請多包涵

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章