dubbo解析-详解元数据中心MetadataReport

2023-11-10

本文基于dubbo 2.7.5版本代码

下面这篇文章介绍了dubbo为什么要采用元数据中心,对元数据中心和注册中心存储的数据做了对比,最后介绍了如何配置使用,我将在本文从代码上分析元数据中心。
岛风:一文聊透 Dubbo 元数据中心

元数据中心是dubbo2.7版本之后新增的功能,主要是为了减轻注册中心的压力,将部分存储在注册中心的内容放到元数据中心。元数据中心的数据只是给自己使用的,改动不需要告知对端,比如服务端修改了元数据,不需要通知消费端。这样注册中心存储的数据减少,同时大大降低了因为配置修改导致注册中心频繁通知监听者,从而大大减轻注册中心的压力。

一、配置

要使用元数据中心的功能,需要增加三项配置:

  1. 注册中心配置增加simplified=“true”,默认simplified=false;
  2. 增加metadata-report配置。
  3. 增加@Servcie(parameters={“metadata”,“remote”})或者@Reference(parameters={“metadata”,“remote”}),否则无法使用远程元数据中心,下一篇文章介绍这个参数的用途

例如:

dubbo.registry.address=zookeeper://localhost:2181
dubbo.registry.simplified=true
dubbo.metadata-report.address=zookeeper://127.0.0.1:2182(不配置address,启动报错)

这个例子注册中心和元数据中心都使用了zookeeper。
元数据中心具体的配置项可以参见类MetadataReportConfig。

二、注册中心参数simplified原理

如果要减少注册中心的配置信息,需要增加配置simplified=true。下面介绍一下该参数如何发挥作用的。
下图是客户端启动时创建服务提供者代理对象的流程。RegistryProtocol的getRegisteredConsumerUrl里面处理simplified参数。
在这里插入图片描述
getRegisteredConsumerUrl的代码如下。

//simplified参数在入参registryUrl里面
//可以简单认为入参registryUrl包含了几乎所有在客户端配置的参数
//入参consumerUrl只包含将要注册到注册中心的数据
public URL getRegisteredConsumerUrl(final URL consumerUrl, URL registryUrl) {
		//如果simplified=false,则在consumerUrl基础上添加另外两个配置信息
        if (!registryUrl.getParameter(SIMPLIFIED_KEY, false)) {
            return consumerUrl.addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY,
                    CHECK_KEY, String.valueOf(false));
        } else {
        	如果simplified=true,则从consumerUrl中筛选出5个值,常量定义见下方
            return URL.valueOf(consumerUrl, DEFAULT_REGISTER_CONSUMER_KEYS, null).addParameters(
                    CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, String.valueOf(false));
        }
    }
//DEFAULT_REGISTER_CONSUMER_KEYS定义了简化后注册中心必须有的数据,包括:
//1、应用名,key为application
//2、应用版本,key为version
//3、分组信息,key为group
//4、dubbo rpc协议版本,key为dubbo
//5、dubbo版本,key为release
public static final String[] DEFAULT_REGISTER_CONSUMER_KEYS = {
	APPLICATION_KEY, VERSION_KEY, GROUP_KEY, DUBBO_VERSION_KEY, RELEASE_KEY
};

上面方法的返回值就是要注册到注册中心的URL。例如,当simplified=true时,方法返回值如下:

consumer://192.168.56.1/com.dubbo.WebsiteProcessor?
application=consumer&category=consumers&check=false&dubbo=2.0.2&release=2.7.5

服务端的原理类似,服务端在服务暴露时,调用RegistryProtocol的getUrlToRegistry方法处理simplified。

	if (!registryUrl.getParameter(SIMPLIFIED_KEY, false)) {
            return providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameters(
                    MONITOR_KEY, BIND_IP_KEY, BIND_PORT_KEY, QOS_ENABLE, QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY,
                    INTERFACES);
        } else {
        	//下面处理simplified=false
        	//extraKeys为配置dubbo.registry.extra-keys的值
            String extraKeys = registryUrl.getParameter(EXTRA_KEYS_KEY, "");
            if (!providerUrl.getPath().equals(providerUrl.getParameter(INTERFACE_KEY))) {
                if (StringUtils.isNotEmpty(extraKeys)) {
                    extraKeys += ",";
                }
                extraKeys += INTERFACE_KEY;
            }
            //DEFAULT_REGISTER_PROVIDER_KEYS为要注册到注册中心的数据的key,值见下方
            String[] paramsToRegistry = getParamsToRegistry(DEFAULT_REGISTER_PROVIDER_KEYS
                    , COMMA_SPLIT_PATTERN.split(extraKeys));
            //组装URL
            return URL.valueOf(providerUrl, paramsToRegistry, providerUrl.getParameter(METHODS_KEY, (String[]) null));
        }
	public static final String[] DEFAULT_REGISTER_PROVIDER_KEYS = {
            APPLICATION_KEY, CODEC_KEY, EXCHANGER_KEY, SERIALIZATION_KEY, CLUSTER_KEY, CONNECTIONS_KEY, DEPRECATED_KEY,
            GROUP_KEY, LOADBALANCE_KEY, MOCK_KEY, PATH_KEY, TIMEOUT_KEY, TOKEN_KEY, VERSION_KEY, WARMUP_KEY,
            WEIGHT_KEY, TIMESTAMP_KEY, DUBBO_VERSION_KEY, RELEASE_KEY
    };

对服务端来说,注册中心需要注册的数据比较多,不再一一介绍,大家看DEFAULT_REGISTER_PROVIDER_KEYS即可。

三、元数据中心

元数据中心的配置类是MetadataReportConfig,可以在配置文件中使用前缀“dubbo.metadata-report”设置MetadataReportConfig的属性。也可以配置多个元数据中心,但是在dubbo 2.7.5版本里面只能其中一个生效。
元数据中心的启动流程图,元数据中心以zookeeper为例:
在这里插入图片描述
MetadataReportInstance的metadataReport属性是一个静态属性。MetadataService下一篇文章介绍。
客户端和服务端都是在DubboBootstrap中启动的元数据中心,元数据中心使用接口MetadataReport表示,实现类有:
在这里插入图片描述
本文将以zookeeper为例介绍元数据中心。如果使用redis或者其他作为元数据中心,在dubbo.metadata-report.address中修改协议即可。

1、AbstractMetadataReport

该类是所有元数据中心的顶层抽象类。先看一下其构造方法,构造方法主要做了两件事:

  1. 在本地创建元数据存储文件;
  2. 创建元数据更新的定时器,定时器在每天2:00至6:00之间的随机时间运行一次。
public AbstractMetadataReport(URL reportServerURL) {
        setUrl(reportServerURL);
        //构建本地文件名,APPLICATION_KEY默认不存在,需要在
        //MetadataReportConfig.parameters中配置
        String defaultFilename = System.getProperty("user.home") + "/.dubbo/dubbo-metadata-" + reportServerURL.getParameter(APPLICATION_KEY) + "-" + reportServerURL.getAddress().replaceAll(":", "-") + ".cache";
        //如果在MetadataReportConfig.parameters中配置了file参数,则使用它作为文件名
        String filename = reportServerURL.getParameter(FILE_KEY, defaultFilename);
        File file = null;
        if (ConfigUtils.isNotEmpty(filename)) {
            file = new File(filename);
            if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) {
                if (!file.getParentFile().mkdirs()) {
                    throw new IllegalArgumentException("Invalid service store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
                }
            }
            if (!initialized.getAndSet(true) && file.exists()) {
                file.delete();//第一次启动的时候将文件删除
            }
        }
        this.file = file;
        loadProperties();//这里调用该方法没有用,因为文件不存在
        //设置是否异步将数据传输到元数据中心
        syncReport = reportServerURL.getParameter(SYNC_REPORT_KEY, false);
        //当访问元数据中心失败时,MetadataReportRetry设置了两个与重试相关的参数:
        //1、重试次数;2、多长时间重试一次
        metadataReportRetry = new MetadataReportRetry(reportServerURL.getParameter(RETRY_TIMES_KEY, DEFAULT_METADATA_REPORT_RETRY_TIMES),
                reportServerURL.getParameter(RETRY_PERIOD_KEY, DEFAULT_METADATA_REPORT_RETRY_PERIOD));
        //参数cycle.report表示是否按照一定的频率将元数据更新到元数据中心,默认是true
        if (reportServerURL.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) {
        	//使用定时器按照一定的频率将元数据更新到元数据中心
            ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("DubboMetadataReportTimer", true));
            //定时任务是每天运行一次,在每天的2:00至6:00一个随机时间
            scheduler.scheduleAtFixedRate(this::publishAll, calculateStartTime(), ONE_DAY_IN_MIll, TimeUnit.MILLISECONDS);
        }
    }

下面介绍该类的主要方法。

(1)publishAll

先看一下构造方法中定时任务调用的publishAll方法。

	void publishAll() {
        logger.info("start to publish all metadata.");
        //allMetadataReports是一个Map<MetadataIdentifier, Object>类型,
        //存储了客户端和服务端的所有与服务相关的元数据
        this.doHandleMetadataCollection(allMetadataReports);
    }
    //遍历allMetadataReports中的值,
    //如果是服务端的元数据,调用storeProviderMetadata;
    //如果是客户端的元数据,调用storeConsumerMetadata
    private boolean doHandleMetadataCollection(Map<MetadataIdentifier, Object> metadataMap) {
        if (metadataMap.isEmpty()) {
            return true;
        }
        Iterator<Map.Entry<MetadataIdentifier, Object>> iterable = metadataMap.entrySet().iterator();
        while (iterable.hasNext()) {
            Map.Entry<MetadataIdentifier, Object> item = iterable.next();
            if (PROVIDER_SIDE.equals(item.getKey().getSide())) {
                this.storeProviderMetadata(item.getKey(), (FullServiceDefinition) item.getValue());
            } else if (CONSUMER_SIDE.equals(item.getKey().getSide())) {
                this.storeConsumerMetadata(item.getKey(), (Map) item.getValue());
            }
        }
        return false;
    }

publishAll的作用是调用doHandleMetadataCollection方法遍历allMetadataReports,然后将服务端和客户端的数据分别存储到元数据中心。
allMetadataReports是AbstractMetadataReport的一个属性,类型是Map<MetadataIdentifier, Object>,存储两类值:

  • 当存储服务端元数据时,value是ServiceDefinition对象,该对象详细记录了服务接口的所有信息,包括接口有哪些方法,每个方法的入参返回值各是什么;
  • 当存储客户端元数据时,因为保存客户端元数据的方法storeConsumerMetadata没有原始调用点,所以对客户端数据的保存情况未知

MetadataIdentifier对象记录了服务接口的名字、版本、分组、是服务端还是消费端标示、应用名。

(2)storeProviderMetadata

该方法用于保存服务接口元数据信息。
服务端发布服务、客户端创建代理时,都会调用storeProviderMetadata方法。该方法的入参可以参考allMetadataReports的key和value值的介绍。
客户端也会调用该方法,但是客户端调用该方法保存的信息与服务端保存的内容是一样的,客户端以引用的服务接口为基础,构建MetadataIdentifier和ServiceDefinition对象。在一些情况下可能会存在客户端和服务端存储的数据相互覆盖的情况。

public void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) {
		//是同步保存,还是异步保存,如果异步的话,使用线程池,
		//reportCacheExecutor是单线程的线程池,
		//reportCacheExecutor=Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveMetadataReport", true));
        if (syncReport) {
            storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition);
        } else {
            reportCacheExecutor.execute(() -> storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition));
        }
    }
    private void storeProviderMetadataTask(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) {
        try {//代码有删减
            allMetadataReports.put(providerMetadataIdentifier, serviceDefinition);
            //failedReports记录保存失败的数据,如果保存成功,则删除
            failedReports.remove(providerMetadataIdentifier);
            Gson gson = new Gson();
            String data = gson.toJson(serviceDefinition);
            //doStoreProviderMetadata由子类实现,
            //例如,ZookeeperMetadataReport是将数据直接保存到zookeeper
            //providerMetadataIdentifier用于构造zk的路径,data是zk节点上的数据
            doStoreProviderMetadata(providerMetadataIdentifier, data);
            //将数据保存到本地文件,本地文件名在构造方法中已经创建
            saveProperties(providerMetadataIdentifier, data, true, !syncReport);
        } catch (Exception e) {
        	//记录保存失败的元数据
            failedReports.put(providerMetadataIdentifier, serviceDefinition);
            //重试,下面小节分析
            metadataReportRetry.startRetryTask();
        }
    }

保存到元数据中心和文件中的元数据是以json格式存储的。
saveProperties方法是将元数据存储到本地文件中,这个方法里面使用了一个自增的数字作为版本,每修改一次文件数字加1,如果下次更新时发现版本比当前版本小,则不修改文件。

(3)storeConsumerMetadata

与storeProviderMetadata相对,但是该方法没有原始调用点,可能会在后续版本更新中使用,该方法原理与storeProviderMetadata类似。

(4)saveServiceMetadata、getExportedURLs

saveServiceMetadata方法将本dubbo实例所有暴露的服务存储到元数据中心。与saveServiceMetadata相对,getExportedURLs方法是查询暴露的服务。

(5)saveSubscribedData、getSubscribedURLs

saveSubscribedData将客户端引用的服务存储到元数据中心。getSubscribedURLs从元数据中心查询客户端引用的服务。

(6)MetadataReportRetry

该类是一个内部类。方法storeConsumerMetadata和storeProviderMetadata,如果出现访问元数据中心失败,那么会调用MetadataReportRetry的startRetryTask进行重试。
在AbstractMetadataReport构造方法中创建MetadataReportRetry对象,其入参有两个:1、重试次数;2、多长时间重试一次。MetadataReportRetry的startRetryTask方法使用异步线程池中的线程按照要求重试。每次重试调用retry方法,尝试将数据保存至元数据中心:

	public boolean retry() {
        return doHandleMetadataCollection(failedReports);
    }

当全局重试次数超过了retry.times的设定值,则调用cancelRetryTask方法:

		void cancelRetryTask() {
            retryScheduledFuture.cancel(false);//取消正在运行的任务
            //关闭线程池,之后重试机制无法使用,
            //当超过重试次数后,dubbo认为元数据中心出现问题,可能宕机或者网络断了
            //这有一个缺点,即使元数据中心可以使用了或者网络连通了,重试功能也无法使用了,只能重启应用程序
            retryExecutor.shutdown();
        }

2、ZookeeperMetadataReport

该类以zookeeper作为元数据中心,该类在构造方法中创建与zookeeper的连接,之后如果元数据有变更,直接将数据存储到zookeeper。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

dubbo解析-详解元数据中心MetadataReport 的相关文章

随机推荐

  • mybatis_plus

    目录 一 简介 二 特性 三 快速入门 一 创建并初始化数据库 1 创建数据库 2 创建 User 表 二 初始化工程 三 添加依赖 1 引入依赖 2 idea中安装lombok插件 四 配置 五 编写代码 1 主类 2 实体 3 mapp
  • [ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it.(入职小灰)

    mariadb剧本安装后自动重启不了 飞要一次手动重启 这对于重要业务来说是致命的 今天遇到的错误 ERROR Can t open the mysql plugin table Please run mysql upgrade to cr
  • threejs教程(一)

    插件安装 npm i three 项目引入 这里我随便找的VUE项目练习的 import as THREE from three 大致介绍一下threejs的逻辑 一般我们用它是来搭建三维模型的 搭建三维模型就需要的三个要素 场景 scen
  • 【Xilinx Vivado 时序分析/约束系列11】FPGA开发时序分析/约束-FPGA DDR-PLL接口的 input delay 约束优化方法

    目录 DDR PLL 简述 实际操作 实际工程 顶层代码 PLL配置 添加时钟约束 添加 input delay 约束 添加 False Path Setup Time Hold Time Multicycle约束 解决办法 PLL配置 发
  • css transition 实现滑入滑出

    transition是css最简单的动画 通常当一个div属性变化时 我们会立即看的变化 从旧样式到新样式是一瞬间的 嗖嗖嗖 但是 如果我希望是慢慢的从一种状态 转变成另外一种状态 怎么办 transition可以做到 第一问 哪些属性值变
  • 电脑连着无线wifi(外网)和有线内网,如何实现双网访问?

    做交付难免会遇到 需要开发远程解决问题 但是客户方是内网 开发无法远程 因为自己遇到很多次 记性又差 所以就写着给自己看看 以管理员身份运行命令提示符 场景描述 访问地址 http 172 31 27 15 内网必须要可以访问这个地址 内网
  • CUID卡写入错误数据被锁死——入坑NFC的一段经历

    最开始想到做NFC是还在学校上自习的时候 学校有种氛围很好的自习室 每个位置都是一个小隔间 小隔间里还有小灯和插座以及网线口 但是需要插卡取电 对就是用很普通的那种校园卡插进去就有电了 这个校园卡是NFC卡 但是学校很nt的一点是只有上一届
  • vue 项目中通过监听 localStorage 的变化进行父子页面传参

    vue实时监听 localStorage 变化 应用场景 1 页面B需要实时获取页面A数据更改 2 父子页面之间的传参 代码实例 B页面实时获取A页面的数据变化 在 页面A 进行缓存修改or插入缓存 localStorage setItem
  • MySQL8.0_JDBC笔记

    第一章 JDBC概述 之前我们学习了JavaSE 编写了Java程序 数据保存在变量 数组 集合等中 无法持久化 后来学习了IO流可以将数据写入文件 但不方便管理数据以及维护数据的关系 后来我们学习了数据库管理软件MySQL 可以方便的管理
  • Java对象的生命周期

    Java对象的生命周期 Java语言除了原始数据类型外 还有一种类型被称之为引用类型 对象的创建一般需要使用new关键字 将创建的对象存储在堆上 heap 而在线程栈中会保留一个指向堆上地址的引用 下图将展示堆栈之间的具体关系 栈中被分割成
  • [UE4] C++实现Delegate Event实例(例子、example、sample)

    相关文章 如何用蓝图实现Delegate Event http aigo iteye com blog 2269663 原文作者 玄冬Wong 转载请注明出处 http aigo iteye com blog 2301010 虽然官方doc
  • 数据库实验3-单表查询

    2021011203 1 查询全体学生的姓名 出生年份和所在系 2 查询选修了课程的学生学号 SELECT DISTINCT sno FROM scfcy WHERE cno IS NOT NULL distinct去除重复的 从名为scf
  • Python 循环嵌套

    Python 语言允许在一个循环体里面嵌入另一个循环 Python for 循环嵌套语法 for iterating var in sequence for iterating var in sequence statements s st
  • 有什么职业入行时间短,薪资高?

    有什么职业入行时间短 薪资高 1 可以通过短期 半年以内 的学习入行 2 入职后 排除运气等不可控因素 能拿到 10k 以上 百分之十的人能做到就算 3 工作期间晚上有极其充足的生物钟休息时间 4 能用脑子解决的事情别用体力 说到入行时间短
  • 收集金币(人人网笔试)

    题目描述 小M来到了一个迷宫中 这个迷宫可以用一个N M的矩阵表示 在这个迷宫的某些位置中存在金币 一开始小M在迷宫的入口 矩阵的左上角 位置 1 1 处 迷宫的出口位于矩阵的右下角 位置 N M 处 每一次小M可以选择向下或者向右走到一个
  • const 指针 常量指针 指针常量 常量指针常量

    常量指针 指向常量的指针 在指针定义语句的类型前加const 表示该指针指向一个常量 const int a 666 const int p a 常量a只能被访问而不能被改写 但指向常量a的常量指针可以改写 指针常量 在指针定义语句的指针名
  • LeetCode-从尾到头打印链表

    用vector的reverse函数实现翻转hh Definition for singly linked list struct ListNode int val ListNode next ListNode int x val x nex
  • 解决Unable to find vcvarsall.bat问题

    目录 前言 方法1 方法2 前言 用Windows平台做Python开发时 难免会遇到Unable to find vcvarsall bat错误 我在网上疯搜了一段时间之后 才找到解决办法 还是两个 不想装VS的朋友可以用第二个 方法1
  • winows搭建远程仓库Github(linux通用)

    winows搭建远程仓库Github linux通用 文章目录 winows搭建远程仓库Github linux通用 创建远程仓库 配置SSH 克隆项目 多人协同开发 代码冲突 标签 分支 创建远程仓库 以下操作为演示在Github网站上创
  • dubbo解析-详解元数据中心MetadataReport

    本文基于dubbo 2 7 5版本代码 详解元数据中心MetadataReport 一 配置 二 注册中心参数simplified原理 三 元数据中心 1 AbstractMetadataReport 1 publishAll 2 stor