2023-09-15  阅读(2)
原文作者:王伟王胖胖 原文地址: https://blog.csdn.net/wangwei19871103/article/details/105763551

获取更新的配置文件

前面的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会被通知,里面会进行刷新。

202309152316042151.png

202309152316048992.png
具体怎么刷下篇说吧。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文