| <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – Java</title><link>https://dubbo.apache.org/zh-cn/tags/java/</link><description>Recent content in Java on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Wed, 28 Jun 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://dubbo.apache.org/zh-cn/tags/java/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: 走向 Native 化:Spring&Dubbo AOT 技术示例与原理讲解</title><link>https://dubbo.apache.org/zh-cn/blog/2023/06/28/%E8%B5%B0%E5%90%91-native-%E5%8C%96springdubbo-aot-%E6%8A%80%E6%9C%AF%E7%A4%BA%E4%BE%8B%E4%B8%8E%E5%8E%9F%E7%90%86%E8%AE%B2%E8%A7%A3/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/06/28/%E8%B5%B0%E5%90%91-native-%E5%8C%96springdubbo-aot-%E6%8A%80%E6%9C%AF%E7%A4%BA%E4%BE%8B%E4%B8%8E%E5%8E%9F%E7%90%86%E8%AE%B2%E8%A7%A3/</guid><description> |
| <p>Java 应用在云计算时代面临“冷启动”慢、内存占用高、预热时间长等问题,无法很好的适应 Serverless 等云上部署模式,GraalVM 通过静态编译、打包等技术在很大程度上解决了这些问题,同时针对 GraalVM 的一些使用限制,Spring 和 Dubbo 等主流框架也都提供了相应的 AOT 解决方案。</p> |
| <p>本文我们将详细分析 Java 应用在云时代面临的挑战,GraalVM Native Image 是如何解决这些问题,GraalVM 的基本概念与工作原理,最后我们通过一个 Spring6 + Dubbo3 的微服务应用示例演示了如何将一个普通微服务应用进行静态化打包。</p> |
| <p>本文主要分为以下四个部分展开</p> |
| <ol> |
| <li>首先我们会先看一下在云计算快速发展的当下,云上应用应该具备的特点,Java 应用在云上所面临的挑战有哪些。</li> |
| <li>其次,我会介绍一下 GraalVM,什么是 Native Image,如何通过 GraalVM 对 Java 应用进行静态化打出 Native Image 可执行的二进制程序。</li> |
| <li>第三部分,我们知道 GraalVM 的使用是有一定限制的,比如 Java 的反射等动态特性是不被支持的,因此我们需要提供特殊的 Metadata 配置来绕过这些限制,在这一部分我们会讲解如何加入引入 AOT Processing 来实现自动化的 Metadata 配置,包括 Spring6 框架中 AOT 处理、Dubbo3 框架的 AOT 处理等。</li> |
| <li>最后,我们将通过一个 Spring6+Dubbo3 的应用示例,来演示如何将这么一个 Java 应用进行静态化打包。</li> |
| </ol> |
| <h2 id="java-应用在云时代所面临的挑战">Java 应用在云时代所面临的挑战</h2> |
| <p>首先,我们先看一下云计算时代的应用特点,以及 Java 在云时代所面临的挑战。从各个统计机构给出的数据来看,Java 语言仍然是当今最受开发者欢迎的编程语言之一,仅次于一些脚本开发语言。使用 Java 语言可以非常高效的开发业务应用,丰富的生态使得 Java 具有非常高的开发和运行效率,有无数的应用基于 Java 语言开发。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/language-rank.png" alt="image.png"></p> |
| <p>但在来到云计算时代,Java 应用的部署和运行开始面临非常多的问题。我们以Serverless为例,Serverless是云上的一种越来越主流的部署模式,它让开发者更专注业务逻辑、通过快速弹性等帮助解决资源问题,根据最新的一些数据,Java在所有云计算厂商的 Serverless 运行时中所占比例并不高,远远不能和它在传统应用开发中所占的比例相匹配。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/serverless-lang-rank.png" alt="image.png"></p> |
| <p>出现这样的原因,主要是Java应用不能很好的满足Serverless场景的几个关键要求。</p> |
| <ul> |
| <li><strong>首先是启动速度问题,Java 冷启动的耗时是比较长的</strong>。这对于Serverless需要快速弹起的场景是一个非常大的挑战,因为 Java 应用的拉起时间可能是秒、数十秒级别的;</li> |
| <li><strong>第二点,Java应用往往都需要一定的预热时间,才能达到最佳的性能状态</strong>,刚刚拉起的应用如果分配比较大的流量是不合适的,往往会出现请求超时、资源占用过高等问题,这就进一步拉长了 Java 应用的有效拉起时间;</li> |
| <li><strong>第三点是 Java 应用对运行环境的要求,它往往需要较大的内存、计算资源</strong>,而这些真正分配给业务自身的并不多,都消耗在一些JVM运行时上,这与用云降本提效的目标并补匹配;</li> |
| <li><strong>最后,Java应用打出来的包或者镜像也是非常大</strong>,从总体上也影响存储、拉取的效率。</li> |
| </ul> |
| <p>接下来,我们具体看一下针对 Java 应用所面临的这些问题, GraalVM 这样一种打包和运行时技术是如何解决的。</p> |
| <h2 id="graalvm-简介">GraalVM 简介</h2> |
| <blockquote> |
| <p>GraalVM compiles your Java applications ahead of time into standalone binaries that start instantly, provide peak performance with no warmup, and use fewer resources.</p> |
| </blockquote> |
| <p>引用官方介绍来看,GraalVM 为 Java 应用提供 AOT 编译和二进制打包能力,基于 GraalVM 打出的二进制包可以实现快速启动、具有超高性能、无需预热时间、同时需要非常少的资源消耗。这里所说的 AOT 是发生在编译期间的一个技术简称,即 Ahead-of-time,这一点我们后续会讲到。总的来说 GraalVM 可以分为两部分内容来看</p> |
| <ul> |
| <li>首先,<strong>GraalVM 是一个完整的 JDK 发行版本</strong>,从这一点它是与 OpenJDK 对等的,可以运行任何面向jvm的语言开发的应用;</li> |
| <li>其次,<strong>GraalVM提供了 Native Image 打包技术</strong>,这可以将应用打包为可以独立运行的二进制包,这个包是自包含的、可脱离 JVM 运行的应用程序。</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-compilation.png" alt="image.png"></p> |
| <p>如上图所示,GraalVM 编译器提供了 JIT 和 AOT 两种模式。</p> |
| <ul> |
| <li>对于 JIT 而言,我们都知道Java类会被编译为 .class 格式的文件,这里编译后就是 jvm 识别的字节码,在 Java 应用运行的过程中,而 JIT 编译器又将一些热点路径上的字节码编译为机器码,已实现更快的执行速度;</li> |
| <li>对于 AOT 模式来说,它直接在编译期间就将字节码转换为机器码,直接省去了运行时对jvm的依赖,由于省去了 jvm 加载和字节码运行期预热的时间,AOT 编译和打包的程序具有非常高的运行时效率。</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-compilation2.png" alt="image.png"></p> |
| <p>总的来说,JIT 使得应用可以具备更高的极限处理能力,可以降低请求的最大延迟这一关键指标;而 AOT 则可以进一步的提升应用的冷启动速度、具有更小的二进制包提及、在运行态需要更少的内存等资源。</p> |
| <h2 id="什么是-native-image">什么是 Native Image?</h2> |
| <p>我们上面多次提到 GraalVM 中 Native Image 概念,Native Image 是一项将 Java 代码编译打包为可执行二进制程序的技术,打出的包中仅包含运行期所需要的代码,包括应用自身代码、标准依赖包、 语言运行时、JDK 库关联的静态代码。这个包的运行不再需要 jvm 环境,当然它是和具体的机器环境相绑定的,需要为不同的机器环境单独打包。 Native Image 有这里列出来的一系列特点:</p> |
| <ul> |
| <li>仅包含 JVM 运行所需的一部分资源,运行成本更低</li> |
| <li>毫秒级的启动时间</li> |
| <li>启动后即进入最佳状态,无需预热</li> |
| <li>可打包为更轻量的二进制包,让部署速度更快更高效</li> |
| <li>安全程度更高</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-advantages.png" alt="image.png"></p> |
| <p>总结起来就是这里的关键几项:更快的启动个速度、更少的资源占用、更小的安全漏洞风险、更紧凑的二进制包体积。解决 Java 应用在 Sererless 等云计算应用场景中面临的突出问题。</p> |
| <h2 id="graalvm-native-image-的基本原理与使用">GraalVM Native Image 的基本原理与使用</h2> |
| <p>接下来,我们看一下 GraalVM 的基本使用方式,首先,需要安装 native-image 需要的相关基础依赖,根据不同的操作系统环境会有所差异,接下来可以使用 GraalVM JDK 下载器下载 native-image。都安装好之后,第二步,就可以使用 native-image 命令编译和打包 Java 应用了,输入可以是 class 文件、jar文件、Java模块等,最终打包为一个可独立运行的可执行文件,比如这里的 HelloWorld。另外,GraalVM 也提供了对应的 Maven和Gradle构建工具插件,让打包过程更容易。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-principal.png" alt="image.png"></p> |
| <p>GraalVM 基于叫做 “closed world assumption” 即封闭世界假设的概念,要求在编译期间程序的所有运行时资源和行为即能被完全确定下来。图中是具体的 AOT 编译和打包过程,左侧应用代码、仓库、jdk等全部作为输入,GraalVM以 main 为入口,扫描所有可触达的代码与执行路径,在处理过程中可能会涉及到一些前置初始化动作,最终 AOT 编译的机器码和一些初始化资源等状态数据,被打包为可执行的 Native 包。</p> |
| <p>相比于传统的 JVM 部署模式,GraalVM Native Image 模式带来的非常大的不同。</p> |
| <ul> |
| <li>GraalVM 在编译构建期间就会以 main 函数为入口,完成对应用代码的静态分析</li> |
| <li>在静态分析期间无法被触达的代码,将会被移除,不会包含在最终的二进制包中</li> |
| <li>GraalVM 无法识别代码中的一些动态调用行为,如反射、resource资源加载、序列化、动态代理等都动态行为都将受限</li> |
| <li>Classpath 在构建阶段就固化下来,无法修改</li> |
| <li>不再支持延迟的类加载,所有可用类和代码在程序启动阶段就确定了</li> |
| <li>还有一些其他的 Java 应用能力是受限使用的(比如类初始化提前等)</li> |
| </ul> |
| <p>GraalVM 不支持反射等动态特性,而我们的很多应用和框架中却大量使用了反射、动态代理等特性,如何才能将这些应用打包为 Native Image 实现静态化那? GraalVM 提供了元数据配置入口,通过为所有动态特性提供配置文件,“closed world assumption” 模式还是成立的,可以让 GraalVM 在编译期知道所有的预期行为。这里给了两个例子:</p> |
| <ol> |
| <li>编码方式上,比如这里反射的编码方式,可以让 GraalVM 通过代码分析计算 Metadata</li> |
| </ol> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/metadata-1.png" alt="image.png"></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/metadata-2.png" alt="image.png"></p> |
| <ol start="2"> |
| <li>另一个示例是提供额外的 json 配置文件并放在指定的目录 META-INF/native-image/&lt;group.id&gt;/&lt;artifact.id&gt; 下。</li> |
| </ol> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/metadata-3.png" alt="image.png"></p> |
| <h2 id="aot-processing">AOT Processing</h2> |
| <p>Java 应用或框架中的反射等动态特性的使用是影响 GraalVM 使用的障碍,而大量的框架都存在这个限制,如果都要求应用或者开发者提供 Metadata 配置的话将会是一项非常有挑战的任务,因此,Spring 和 Dubbo 等框架都在 AOT Compilation 即 AOT 编译之前引入了 AOT Processing 即 AOT 预处理的过程,AOT Processing 用来完成自动化的 Metadata 采集,并将 Metadata 提供给 AOT 编译器使用。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/aot.png" alt="image.png"></p> |
| <p>AOT 编译机制是对所有 Java 应用通用的,但相比于 AOT 编译,AOT Processing 采集 Metadata 的过程是每个框架都不同的,因为每个框架对于反射、动态代理等都有自己的用法。 |
| 我们以一个典型的 Spring + Dubbo 的微服务应用为例,要实现这个应用的静态化打包,这里涉及到 Spring、Dubbo 以及一众第三方依赖的 Metadata 处理过程。</p> |
| <ul> |
| <li>Spring - Spring AOT processing</li> |
| <li>Dubbo - Dubbo AOT processing</li> |
| <li>Third-party libraries - Reachability Metadata</li> |
| </ul> |
| <p>对于 Spring 来说,Spring6 中发布了 Spring AOT 机制,用来支持 Spring 应用的静态化预处理;Dubbo 最近也在 3.2 版本中发布了 Dubbo AOT 机制,让 Dubbo 相关组件可以自动化实现 Native 预处理;除了这两个与业务开发密切相关的框架,一个应用中往往还有大量的第三方依赖,这些依赖的 Metadata 也是影响静态化的关键,如果它们中有反射、类加载等行为,那么需要为它们提供 Metadata 配置,对于这些第三方应用目前有两个渠道,一个是 GraalVM 官方提供的共享空间,这里有相当一部分依赖的 Metadata 配置可供使用(https://github.com/oracle/graalvm-reachability-metadata),另一种方式则是要求组件官方发布的发布中包含 Metadata 配置,对于这两种情况 GraalVM 都可以做到对于 Metadata 的自动读取。</p> |
| <h3 id="spring-aot">Spring AOT</h3> |
| <p>接下来我们看一下 Spring AOT 做了哪些编译之前的预处理工作,Spring 框架中有非常多的动态特性,比如自动配置、条件 Bean 等特性。Spring AOT 就是针对针对这些动态特性,在构建阶段进行预处理,生成可供 GraalVM 使用的一系列 Metadata 输入,这里生成的内容包括:</p> |
| <ul> |
| <li>Spring Bean 定义相关的代码预生成,如下图展示代码段</li> |
| <li>在构建阶段生成动态代理相关代码</li> |
| <li>关于一些反射等使用的 JSON 元数据文件</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/spring-aot.png" alt="image.png"></p> |
| <h3 id="dubbo-aot">Dubbo AOT</h3> |
| <p>Dubbo AOT 做的事情与 Spring AOT 非常类似,只不过 Dubbo AOT 是专门针对 Dubbo 框架特有的使用方式进行预处理,这包括:</p> |
| <ul> |
| <li>SPI 扩展相关的源代码生成</li> |
| <li>一些反射使用的 JSON 配置文件生成</li> |
| <li>RPC 代理类代码生成</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/dubbo-aot-1.png" alt="image.png"></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/dubbo-aot-2.png" alt="image.png"></p> |
| <h2 id="spring6--dubbo3-示例演示">Spring6 + Dubbo3 示例演示</h2> |
| <p>接下来,我们通过一个 Spring6 + Dubbo3 的示例微服务应用,演示如何使用 Spring AOT、Dubbo AOT 等,来实现应用的 Native Image 打包。</p> |
| <p>完整的代码示例可在这里下载:<a href="https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-native-image">dubbo-samples-native-image</a></p> |
| <h3 id="第一步安装graalvm">第一步:安装GraalVM</h3> |
| <ol> |
| <li>在Graalvm官网根据自己的系统选取对应Graalvm版本:<a href="https://www.graalvm.org/downloads/">https://www.graalvm.org/downloads/</a></li> |
| <li>根据官方文档安装 native-image:<a href="https://www.graalvm.org/latest/reference-manual/native-image/#install-native-image">Getting Started with Native Image</a></li> |
| </ol> |
| <h3 id="第二步创建项目">第二步:创建项目</h3> |
| <p>这个示例应用就是普通的、常见的微服务应用,我们使用 SpringBoot3 进行应用配置开发,使用 Dubbo3 定义并发布 RPC 服务;应用构建工具使用 Maven。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/demo-1.png" alt="image.png"></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/demo-2.png" alt="image.png"></p> |
| <h3 id="第三步配置-maven-插件">第三步:配置 Maven 插件</h3> |
| <p>重点是增加 spring-boot-maven-plugin、native-maven-plugin、dubbo-maven-plugin 三个插件配置,开启 AOT 处理过程,修改dubbo-maven-plugin中的mainClass为所需的启动类全路径。(其中API使用方式无需添加spring-boot-maven-plugin依赖。)</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;profiles&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;profile&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;id&gt;</span>native<span style="color:#268bd2">&lt;/id&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;build&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;plugins&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>maven-compiler-plugin<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;release&gt;</span>17<span style="color:#268bd2">&lt;/release&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;fork&gt;</span>true<span style="color:#268bd2">&lt;/fork&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;verbose&gt;</span>true<span style="color:#268bd2">&lt;/verbose&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.springframework.boot<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>spring-boot-maven-plugin<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;executions&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;id&gt;</span>process-aot<span style="color:#268bd2">&lt;/id&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goal&gt;</span>process-aot<span style="color:#268bd2">&lt;/goal&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/executions&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.graalvm.buildtools<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>native-maven-plugin<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>0.9.20<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;classesDirectory&gt;</span>${project.build.outputDirectory}<span style="color:#268bd2">&lt;/classesDirectory&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;metadataRepository&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;enabled&gt;</span>true<span style="color:#268bd2">&lt;/enabled&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/metadataRepository&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;requiredVersion&gt;</span>22.3<span style="color:#268bd2">&lt;/requiredVersion&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;executions&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;id&gt;</span>add-reachability-metadata<span style="color:#268bd2">&lt;/id&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goal&gt;</span>add-reachability-metadata<span style="color:#268bd2">&lt;/goal&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/executions&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.dubbo<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>dubbo-maven-plugin<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${dubbo.version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;mainClass&gt;</span>com.example.nativedemo.NativeDemoApplication<span style="color:#268bd2">&lt;/mainClass&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;executions&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;phase&gt;</span>process-sources<span style="color:#268bd2">&lt;/phase&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goal&gt;</span>dubbo-process-aot<span style="color:#268bd2">&lt;/goal&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/executions&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/plugins&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/build&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/profile&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/profiles&gt;</span> |
| </span></span></code></pre></div><h3 id="第四步在pom依赖中添加native相关的依赖">第四步:在Pom依赖中添加native相关的依赖</h3> |
| <p>另外,对于 Dubbo 而言,由于当前一些 Native 机制依赖 JDK17 等版本,Dubbo 没有将一些包默认打包到发行版本中,因此需要增加两个额外的依赖 dubbo-spring6 适配和 dubbo-native 组件。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.dubbo<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>dubbo-config-spring6<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${dubbo.version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.dubbo<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>dubbo-native<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${dubbo.version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span></code></pre></div><h3 id="第五步调整compilerproxyserialization和logger">第五步:调整compiler、proxy、serialization和logger</h3> |
| <p>同时,这个示例对于第三方组件的支持目前也是受限的,主要是第三方组件的 Reachability Metadata 。比如目前支持的网络通信或编码组件有 Netty 和 Fastjson2;支持的日志等组件为 Logback;微服务组件有 Nacos、Zookeeper 等。</p> |
| <ul> |
| <li>序列化方式目前支持的比较好的是Fastjson2</li> |
| <li>compiler、proxy目前只能选择jdk</li> |
| <li>logger目前需要配置slf4j,目前仅支持logback</li> |
| </ul> |
| <p>示例配置如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">dubbo</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">application</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: ${spring.application.name} |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">logger</span>: slf4j |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protocol</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: dubbo |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">port</span>: -<span style="color:#2aa198">1</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">serialization</span>: fastjson2 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">registry</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">id</span>: zk-registry |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">address</span>: zookeeper://127.0.0.1:2181 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">config-center</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">address</span>: zookeeper://127.0.0.1:2181 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">metadata-report</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">address</span>: zookeeper://127.0.0.1:2181 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">provider</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">serialization</span>: fastjson2 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">consumer</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">serialization</span>: fastjson2 |
| </span></span></code></pre></div><h3 id="第六步编译">第六步:编译</h3> |
| <p>在项目根路径下执行以下编译命令:</p> |
| <ul> |
| <li>API方式直接执行</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span> mvn clean install -P native -Dmaven.test.skip=true |
| </span></span></code></pre></div><ul> |
| <li>注解和xml方式(Springboot3集成的方式)</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span> mvn clean install -P native native:compile -Dmaven.test.skip<span style="color:#719e07">=</span><span style="color:#b58900">true</span> |
| </span></span></code></pre></div><h3 id="第七步执行二进制文件即可">第七步:执行二进制文件即可</h3> |
| <p>二进制文件在 target/ 目录下,一般以工程名称为二进制包的名称,比如 target/native-demo</p> |
| <h2 id="总结">总结</h2> |
| <p>GraalVM 技术为 Java 在云计算时代的应用带来了新的变革,帮助解决了 Java 应用启动慢、资源占用,但同时我们也看到了 GraalVM 的使用也存在一些限制,因此 Spring6、SpringBoot3、Dubbo3 都提供了相应的 Native 解决方案。 Apache Dubbo 社区接下来将在周边生态组件等推进整体的 Native 静态化。</p></description></item><item><title>Blog: 引言</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</guid><description> |
| <h2 id="引言">引言</h2> |
| <p>服务指标统计体系是 Dubbo 可观测能力的重要组成部分。 |
| dubbo-metrics 指标模块旨在将 dubbo 内部零散的 Metrics 相关类综合到一个单独的模块中,提供一套更加完善、全面、可拓展、解耦合的指标采样-统计-导出解决方案。</p> |
| <p>dubbo-metrics 模块包括:</p> |
| <ul> |
| <li>dubbo-metrics-api 公用接口包</li> |
| <li>dubbo-metrics-prometheus 普罗米修斯适配包</li> |
| <li>dubbo-metrics-metadata 元数据中心指标监控包</li> |
| <li>dubbo-metrics-registry 注册中心指标监控包</li> |
| <li>dubbo-metrics-config-center 配置中心指标监控包</li> |
| <li>dubbo-metrics-default 接口默认实现包,提供dubbo内部核心指标的监控功能</li> |
| </ul> |
| <p>在设计上,dubbo-metrics 深入应用事件驱动编程思想,总体体现出下图的事件处理链路:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/metrics-event-struct.png" alt="metrics-event-struct"></p> |
| <p>在拓展上,dubbo-metrics 抽象了一套指标导出接口与抽象实现,可实现兼容多种指标统计监控中心,默认提供了普罗米修斯实现。</p></description></item><item><title>Blog: 1-指标样本的收集与存储</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</guid><description> |
| <h2 id="一指标样本的收集与存储">一、指标样本的收集与存储</h2> |
| <h3 id="指标样本收集">指标样本收集</h3> |
| <p><strong>指标收集器(Collector)是指标对外导出的入口</strong>。最终导出到指标统计中心的指标采样实际均直接来源于各个指标采样器。因此,我们将从各个收集器实现开始,分析 dubbo-metrics 模块是如何工作的。</p> |
| <p>指标收集操作定义在 MetricsCollector (指标采集器,SPI)接口中,可以通过它的实现收集某一类的指标样本(MetricSample)。它主要有以下实现,对应着不同类型的指标:</p> |
| <ul> |
| <li>ConfigCenterMetricsCollector <strong>配置中心操作相关指标收集器</strong> ,收集配置信息的变化次数</li> |
| <li>MetadataMetricsCollector <strong>元数据中心操作相关指标收集器</strong>,收集提供者、消费者对元数据中心操作(推送数据、拉取数据)情况的计数、耗时统计。</li> |
| <li>RegistryMetricsCollector <strong>注册中心相关操作指标收集器</strong>,收集应用级、接口级服务注册成功、失败、耗时的相关计数。</li> |
| <li>DefaultMetricsCollector <strong>默认指标收集器</strong>,内置多种采样器来完成不同类型的内部指标采样。</li> |
| <li>HistogramMetricsCollector <strong>直方图指标收集器</strong>,利用 micrometer API 处理直方图类型的指标,它的实现较为特殊。</li> |
| </ul> |
| <p>配置中心 、元数据、服务注册及默认指标收集器均实现自混合指标收集器(CombMetricsCollector)。混合指标收集器实现了 ApplicationMetricsCollector 、ServiceMetricsCollector 、MethodMetricsCollector 三个接口(定义按应用名收集、按应用名-服务名收集和按应用-方法名收集指标的操作),因此它们可以进行应用、服务和方法三个层面的指标收集工作。</p> |
| <p>默认指标收集器的特点是通过内部的指标采样器(MetricsSampler)完成指标事件的处理操作,而不是其它收集器的指标监听器(MetricsListener)</p> |
| <p>直方图指标收集器则负责收集直方图类型的指标。它利用直方图度量寄存器(HistogramMetricRegister)借助 micrometer API 完成直方图样本的采集。直方图类型包括百分位数、服务水平目标、最小预期值、最大预期值、统计数据分布有效期等。</p> |
| <p><strong>Collector的继承关系:</strong></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/collectors.png" alt="collectors"></p> |
| <p>可以看出,每个指标收集器都具有来自 MetricsListener 的监听指标事件的能力。为什么指标收集器本身需要监听指标事件? 在后文中,我们将讨论指标收集器是如何利用内置的子转发器(SubDispatcher)转发指标事件,并完成计数处理的。</p> |
| <h3 id="指标样本存储">指标样本存储</h3> |
| <p>前文中,我们了解了指标收集的入口是指标收集器(Collector)。那么各个收集器从哪里收集指标样本?</p> |
| <p><strong>对于配置中心、元数据中心、 注册中心的指标收集器:</strong></p> |
| <p>它们分别负责采集三大中心模块的指标,均继承于<strong>混合数据收集器(CombMetricsCollector)</strong>,而混合数据收集器中实现了 export 方法 。</p> |
| <p>混合数据收集器内部有一个<strong>基本数据聚合器(BaseStatComposite)</strong>,它实现了 MetricsExport 接口,该接口定义了指标导出操作,混合数据收集器则利用它的 export 方法导出指标。</p> |
| <p>基本数据聚合器是一个抽象类,内有三个属性:ApplicationStatComposite 、ServiceStatComposite 和 RtStatComposite 。它们的作用:</p> |
| <ul> |
| <li><strong>ApplicationStatComposite 应用数据聚合器</strong>,应用程序级别相关事件的计数 ,根据指标Key( MetricsKey )和应用名统计指标,提供计数递增操作</li> |
| <li><strong>ServiceStatComposite 服务数据聚合器</strong>,服务级别相关事件的计数,根据指标Key、应用名和服务名统计指标,提供计数递增操作</li> |
| <li><strong>MethodStatComposite 方法数据聚合器</strong>,方法级别相关事件的计数,存储各方法RPC调用相关计数。</li> |
| <li><strong>RtStatComposite,Rt(Response Time,响应时间)数据聚合器</strong>,包括应用级别和服务级别。根据应用名、服务名、注册的指标名及相应相应时间统计指标,提供添加操作。</li> |
| </ul> |
| <p>对于以上四个聚合器,他们的职责就是存储某一类型的采样样本。</p> |
| <p>** 基本数据聚合器 (BaseStatComposite)** 对这三个子聚合器的操作进行了简单整合,统一提供给外界。<strong>而混合指标收集器(CombMetricsCollector)</strong> 也基本保留了内部基本数据聚合器的所有操作,将其封装为 <code>increment</code>、<code>setNum</code>、<code>addRt </code>三个方法(及它们的重载,分别收集应用级数据和服务级数据)向上提供。外部组件可以直接调用这些收集器完成指标更新操作。</p> |
| <p><strong>当调用元数据中心指标收集器、注册中心指标收集器的 collect 方法时,最终会调用<code>BaseStatComposite.export(MetricsCategory category)</code> , 该方法会收集内部三个聚合器的指标并返回。</strong></p> |
| <p>需要注意的是, 配置中心指标收集器不依赖于基本数据聚合器 导出数据,它在创建时将基本数据聚合器置为null,而使用自己的 updatedMetrics 字段存储采样:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ConfigCenterMetricsCollector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>ConfigCenterMetric<span style="color:#719e07">,</span> AtomicLong<span style="color:#719e07">&gt;</span> updatedMetrics <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ConfigCenterMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//BaseStatComposite = null |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span><span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>混合指标收集器和数据聚合器之间呈现如下的包含关系:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/composite-struct.png" alt="composite-struct"></p> |
| <p><strong>DefaultMetricsCollector 默认指标采集器:</strong></p> |
| <p>它不直接存储采样数据,而是通过收集其下**指标采样器(MetricsSampler)**的样本来完成采样工作。这些采样器包括:</p> |
| <ul> |
| <li>方法采样器</li> |
| <li>应用采样器</li> |
| <li>线程池采样器</li> |
| </ul> |
| <p>这些采样器完成采样后,还会利用采集器中的**事件多播器(Multicaster)**将指标事件发布出去,可以被其它监听器处理。详细流程将在后文中探讨。</p> |
| <p><strong>HistogramMetricsCollector 直方图指标采集器:</strong></p> |
| <p>由于需要使用Timer完成直方图属性的统计,它使用自己的容器存储采样数据。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">HistogramMetricsCollector</span> <span style="color:#268bd2">implements</span> MetricsListener <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//方法指标样本与对应的Timer |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> ConcurrentHashMap<span style="color:#719e07">&lt;</span>MethodMetric<span style="color:#719e07">,</span> Timer<span style="color:#719e07">&gt;</span> rt <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>Timer(计时器)由 micrometer API 提供,常用于统计一分钟内的大量事件。</p></description></item><item><title>Blog: 2-指标收集器的指标采集流程</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</guid><description> |
| <h2 id="二指标收集器的指标采集流程">二、指标收集器的指标采集流程</h2> |
| <p>在前文中,我们了解了指标收集器(Collector)最终收集的数据只有三个来源:</p> |
| <ul> |
| <li> |
| <p>实现自混合指标收集器(CombMetricsCollector) 的元数据指标收集器(MetadataMetricsCollector)和注册中心指标收集器(RegistryMetricsCollector),它们的样本均存储在内置的基本数据聚合器中。具体来说,是基本数据聚合器下的四个子数据聚合器中:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/composite-struct.png" alt="composite-struct"></p> |
| </li> |
| <li> |
| <p><strong>DefaultMetricsCollector 默认指标收集器</strong>,它的样本不仅来自于指标事件,还来自其下**采样器(Sampler)**中,用于Dubbo核心模块的采样。</p> |
| </li> |
| <li> |
| <p><strong>HistogramMetricsCollector 直方图指标收集器</strong>,由于采样数据的特殊性,它的样本直接以 Map 存储在内部。</p> |
| </li> |
| </ul> |
| <p>接下来,我们需要明确它们存储的指标是如何添加进去的。</p> |
| <h3 id="1服务治理模块的指标采集流程">1,服务治理模块的指标采集流程</h3> |
| <p>通过之前的分析,我们知道服务治理模块的指标采集器均实现自混合指标收集器(CombMetricsCollector)。它对基本数据聚合器(BaseStatComposite) 的大部分方法做了封装。基本数据聚合器又封装了四个负责存储不同类型指标采样的子聚合器。</p> |
| <p>这四个子聚合器包括:</p> |
| <ul> |
| <li>ApplicationStatComposite</li> |
| <li>ServiceStatComposite</li> |
| <li>MethodStatComposite</li> |
| <li>RtStatComposite</li> |
| </ul> |
| <p>实际上,<strong>元数据、注册中心指标收集器</strong>更新、添加指标的操作都是通过混合指标收集器暴露的方法进行。而具体的,是通过 <code>setNum</code>、<code>increment</code>、<code>addRt</code> 这三个方法(及它们的重载)进行操作。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//CombMetricsCollector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> BaseStatComposite stats<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setNum</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> String applicationName<span style="color:#719e07">,</span> String serviceKey<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> num<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>stats<span style="color:#719e07">.</span>setServiceKey<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> applicationName<span style="color:#719e07">,</span> serviceKey<span style="color:#719e07">,</span> num<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">increment</span><span style="color:#719e07">(</span>String applicationName<span style="color:#719e07">,</span> MetricsKey metricsKey<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>stats<span style="color:#719e07">.</span>incrementApp<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> applicationName<span style="color:#719e07">,</span> SELF_INCREMENT_SIZE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">increment</span><span style="color:#719e07">(</span>String applicationName<span style="color:#719e07">,</span> String serviceKey<span style="color:#719e07">,</span> MetricsKey metricsKey<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> size<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>stats<span style="color:#719e07">.</span>incrementServiceKey<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> applicationName<span style="color:#719e07">,</span> serviceKey<span style="color:#719e07">,</span> size<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addRt</span><span style="color:#719e07">(</span>String applicationName<span style="color:#719e07">,</span> String registryOpType<span style="color:#719e07">,</span> Long responseTime<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> stats<span style="color:#719e07">.</span>calcApplicationRt<span style="color:#719e07">(</span>applicationName<span style="color:#719e07">,</span> registryOpType<span style="color:#719e07">,</span> responseTime<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addRt</span><span style="color:#719e07">(</span>String applicationName<span style="color:#719e07">,</span> String serviceKey<span style="color:#719e07">,</span> String registryOpType<span style="color:#719e07">,</span> Long responseTime<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> stats<span style="color:#719e07">.</span>calcServiceKeyRt<span style="color:#719e07">(</span>applicationName<span style="color:#719e07">,</span> serviceKey<span style="color:#719e07">,</span> registryOpType<span style="color:#719e07">,</span> responseTime<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span></code></pre></div><p>由于几个方法实际上的调用链路类似,我们选择从其中的 setNum 方法开始分析。</p> |
| <p>其在<strong>数据聚合器层面的调用链路</strong>可以总结为:setNum 方法调用基本数据聚合器的 setServiceKey 方法,该方法又会调用服务数据聚合器(ServiceStatComposite)的同名 setServiceKey 方法(我们已经知道基本数据聚合器内封装了四个不同类型的子聚合器),这个方法实质上是<strong>对应用层面的特定指标(由指标Key决定)进行注册并赋初始值(参数中的 num)。</strong></p> |
| <p>setNum 的用法均位于注册中心事件多播器(RegistryMetricsEventMulticaster)中声明的 MCat 接口中,在 APPLICATION_NOTIFY_FINISH 和 APPLICATION_DIRECTORY_POST 两个常量初始化时被调用。MCat 接口本身作为常量类使用,并在初始化时<strong>注册真正的指标常量</strong>:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//RegistryMetricsEventMulticaster.MCat |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>MetricsCat APPLICATION_NOTIFY_FINISH <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCat<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>NOTIFY_METRIC_NUM_LAST<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> AbstractMetricsListener<span style="color:#719e07">.</span>onFinish<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>addRt<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> placeType<span style="color:#719e07">.</span>getType<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;</span> lastNumMap <span style="color:#719e07">=</span> Collections<span style="color:#719e07">.</span>unmodifiableMap<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>ATTACHMENT_KEY_LAST_NUM_MAP<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> lastNumMap<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>k<span style="color:#719e07">,</span> v<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>setNum<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> k<span style="color:#719e07">,</span> v<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> MetricsCat APPLICATION_DIRECTORY_POST <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCat<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_VALID<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> AbstractMetricsListener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;&gt;</span> summaryMap <span style="color:#719e07">=</span> event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>ATTACHMENT_DIRECTORY_MAP<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> summaryMap<span style="color:#719e07">.</span>forEach<span style="color:#719e07">((</span>metricsKey<span style="color:#719e07">,</span> map<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>k<span style="color:#719e07">,</span> v<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>setNum<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> k<span style="color:#719e07">,</span> v<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//... |
| </span></span></span></code></pre></div><p>此处声明的指标常量都是 MetricsCat 类型的。其中部分常量在创建时还传入了该指标的收集逻辑,如Key 为 NOTIFY_METRIC_NUM_LAST 的常量。以下为 MetricsCat 的定义:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MetricsCat</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> MetricsPlaceType placeType<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> Function<span style="color:#719e07">&lt;</span>CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;,</span> AbstractMetricsListener<span style="color:#719e07">&gt;</span> eventFunc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsCat</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> BiFunction<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;,</span> AbstractMetricsListener<span style="color:#719e07">&gt;</span> biFunc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>eventFunc <span style="color:#719e07">=</span> collector <span style="color:#719e07">-&gt;</span> biFunc<span style="color:#719e07">.</span>apply<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> collector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsCat</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> TpFunction<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> MetricsPlaceType<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;,</span> AbstractMetricsListener<span style="color:#719e07">&gt;</span> tpFunc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>eventFunc <span style="color:#719e07">=</span> collector <span style="color:#719e07">-&gt;</span> tpFunc<span style="color:#719e07">.</span>apply<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetricsCat <span style="color:#268bd2">setPlaceType</span><span style="color:#719e07">(</span>MetricsPlaceType placeType<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>placeType <span style="color:#719e07">=</span> placeType<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Function<span style="color:#719e07">&lt;</span>CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;,</span> AbstractMetricsListener<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getEventFunc</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> eventFunc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一个接受三个入参,一个返回值的函数接口。通过构造函数我们可以知道这三个入参分别是MetricsKey, MetricsPlaceType, CombMetricsCollector&lt;TimeCounterEvent&gt;,返回值为AbstractMetricsListener。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">@FunctionalInterface</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">TpFunction</span><span style="color:#719e07">&lt;</span>T<span style="color:#719e07">,</span> U<span style="color:#719e07">,</span> K<span style="color:#719e07">,</span> R<span style="color:#719e07">&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> R <span style="color:#268bd2">apply</span><span style="color:#719e07">(</span>T t<span style="color:#719e07">,</span> U u<span style="color:#719e07">,</span> K k<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>MetricsCat 类除了构造器,只提供了两个public方法,都是获取其内部属性的。</p> |
| <p>其实质上 eventFunc 字段的载体,提供了为特定指标生产监听器的逻辑,因此 <code>MetricsCat </code> 可以看做<strong>为特定指标生产指标监听器的工厂</strong>,用户在创建时传入这个监听器的处理逻辑。</p> |
| <p>通过泛型,我们可以知道它构造时使用的两个参数分别为 MetricsKey(指标Key)和一个接受 <code>MetricsKey, MetricsPlaceType, CombMetricsCollector&lt;TimeCounterEvent&gt;</code> 三个参数,返回一个 <code>AbstractMetricsListener</code> 的函数。之所以要多封装一层函数,是因为 <code>placeType</code> 字段在 <code>MetricsKey</code> 实例构造之后才会提供,借此实现延迟初始化。</p> |
| <p>回到之前两个<strong>在 MCat 中定义了监听器生产方法</strong>的两个常量的初始化流程:它们在创建 MetricsCat 时传入的TpFunction中定义的操作为:返回通过 AbstractMetricsListener.onFinish获取的事件完成监听器。当指定MetricsKey 的指标统计事件完成时,这个监听器中的 onEventFinish 方法就会被调用。 而 MetricsCat 构造时传入的 MetricsKey 会被作为 AbstractMetricsListener 的构造参数,用于指定监听的指标。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//RegistryMetricsEventMulticaster.MCat |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">new</span> MetricsCat<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>NOTIFY_METRIC_NUM_LAST<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> AbstractMetricsListener<span style="color:#719e07">.</span>onFinish<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>addRt<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> placeType<span style="color:#719e07">.</span>getType<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;</span> lastNumMap <span style="color:#719e07">=</span> Collections<span style="color:#719e07">.</span>unmodifiableMap<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>ATTACHMENT_KEY_LAST_NUM_MAP<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> lastNumMap<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>k<span style="color:#719e07">,</span> v<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>setNum<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> k<span style="color:#719e07">,</span> v<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">));</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//AbstractMetricsListener |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onFinish</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> Consumer<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> finishFunc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> AbstractMetricsListener<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onEventFinish</span><span style="color:#719e07">(</span>TimeCounterEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//此处是finishFunc就是之前 event -&gt;{...} 中定义的lambda函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> finishFunc<span style="color:#719e07">.</span>accept<span style="color:#719e07">(</span>event<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">};</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>三个形参 (key, placeType, collector) 中的 collector 为 <code>CombMetricsCollector&lt;TimeCounterEvent&gt;</code>,意味着它的三个实现(ConfigCenterMetricsCollector 、MetadataMetricsCollector、RegistryMetricsCollector)都可以作为参数。</p> |
| <p>至此,我们可以总结,对于这两个参数, <code>MetricsCat</code> 创建时嵌套的两层 lambda 函数最终是为了注册特定指标的监听器,并定义事件结束时的处理逻辑(内层的lambda)。在处理事件时,会调用混合指标收集器(CombMetricsCollector) 的 <code>addRT</code> 方法添加响应时间计时,还会调用 <code>setNum</code> 来添加指标计数。</p> |
| <p>由于此处的 <code>MetricsKey</code> 在 MetricsCat创建时就被传入,我们可以确定这两个字段存储了以下两个指标的统计逻辑:</p> |
| <ul> |
| <li> |
| <p>NOTIFY_METRIC_NUM_LAST:Last Notify Nums , 最后一个事件完成时的计数 。监听器中使用的是 <code>setNum</code>,事件结束时直接更新指定key指标的计数为传入的值,同时使用 <code>addRt </code> 来统计事件持续时长</p> |
| </li> |
| <li> |
| <p>DIRECTORY_METRIC_NUM_VALID:Valid Directory Urls,服务目录中注册成功的url数量。监听器中同样使用的是 <code>setNum</code>,事件结束后直接更新为服务目录中的最新计数</p> |
| </li> |
| </ul> |
| <p>之后,三个相关的 MetricsCat(指标类型) 实例会被绑定到一个 CategoryOverall(指标综合) 实例中,绑定的逻辑按一个事件进行的三个过程:<strong>事件发生、事件结束、事件失败</strong>,分别对应 CategoryOverall 的第2、3、4个参数,其中事件发生时的逻辑不能为 null。而第一个参数为 <code>MetricsPlaceType</code>,该参数封装了指标类型标识(如 register 服务注册、subscribe 服务订阅)和该指标的收集级别(应用还是服务)。</p> |
| <p>还记得 <code>MetricsCat</code> 中 <code>TpFunction</code> 的三个入参吗?其中第二个 placeType 就是这个参数。 <code>CategoryOverall</code> 在构造时会将它设置到其中的三个 MetricsCat 中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// CategorySet:常量接口,同样位于RegistryMetricsEventMulticaster中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">interface</span> <span style="color:#268bd2">CategorySet</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> CategoryOverall APPLICATION_NOTIFY <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CategoryOverall<span style="color:#719e07">(</span>OP_TYPE_NOTIFY<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>APPLICATION_NOTIFY_POST<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>APPLICATION_NOTIFY_FINISH<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategoryOverall SERVICE_DIRECTORY <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CategoryOverall<span style="color:#719e07">(</span>OP_TYPE_DIRECTORY<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>APPLICATION_DIRECTORY_POST<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategoryOverall SERVICE_REGISTER <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CategoryOverall<span style="color:#719e07">(</span>OP_TYPE_REGISTER_SERVICE<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>SERVICE_REGISTER_POST<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>SERVICE_REGISTER_FINISH<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>SERVICE_REGISTER_ERROR<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>CategoryOverall<span style="color:#719e07">&gt;</span> ALL <span style="color:#719e07">=</span> Arrays<span style="color:#719e07">.</span>asList<span style="color:#719e07">(</span>APPLICATION_REGISTER<span style="color:#719e07">,</span> APPLICATION_SUBSCRIBE<span style="color:#719e07">,</span> APPLICATION_NOTIFY<span style="color:#719e07">,</span> SERVICE_DIRECTORY<span style="color:#719e07">,</span> SERVICE_REGISTER<span style="color:#719e07">,</span> SERVICE_SUBSCRIBE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><code>CategorySet</code> 中的常量都会被封装到List中,在 <code>RegistryMetricsEventMulticaster</code> 创建时统一调用:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">RegistryMetricsCollector</span> <span style="color:#268bd2">extends</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">RegistryMetricsEventMulticaster</span><span style="color:#719e07">(</span>RegistryMetricsCollector collector<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategorySet<span style="color:#719e07">.</span>ALL<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>categorySet <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过 MetricsCat 实例中的定义的监听器创建逻辑,逐个注册监听器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>addListener<span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getPost<span style="color:#719e07">().</span>getEventFunc<span style="color:#719e07">().</span>apply<span style="color:#719e07">(</span>collector<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getFinish<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>addListener<span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getFinish<span style="color:#719e07">().</span>getEventFunc<span style="color:#719e07">().</span>apply<span style="color:#719e07">(</span>collector<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getError<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>addListener<span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getError<span style="color:#719e07">().</span>getEventFunc<span style="color:#719e07">().</span>apply<span style="color:#719e07">(</span>collector<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//... |
| </span></span></span></code></pre></div><p><strong>由此,我们也明确了 RegistryMetricsEventMulticaster (指标注册事件多播器)的作用</strong>:统一定义、管理事件,并在初始化时注册其中定义各种事件的<strong>监听器</strong>。</p> |
| <p>它继承了 SimpleMetricsEventMulticaster,其中的 publishEvent 方法在被调用时就会尝试调用所有监听器,判断其是否对当前事件类型感兴趣,选择是否进行调用。同时,这些监听器会对特定指标数据进行计算,更新到对应的收集器中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsEventMulticaster |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishEvent</span><span style="color:#719e07">(</span>MetricsEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>event <span style="color:#719e07">instanceof</span> EmptyEvent<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>validateIfApplicationConfigExist<span style="color:#719e07">(</span>event<span style="color:#719e07">))</span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricsListener listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>listener<span style="color:#719e07">.</span>isSupport<span style="color:#719e07">(</span>event<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> listener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>event<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>我们通过分析混合指标收集器(CombMetricsCollector) 中的 setNum 方法的用法,了解到了 Composite 中的数据来源之一是注册指标事件多播器(RegistryMetricsEventMulticaster)中为服务注册相关指标创建的指标监听器。实际上,increment、addRt方法都是由指标监听器的各个实现调用的。</p> |
| <p>应用程序指标监听器(MetricsApplicationListener)中提供了 AbstractMetricsListener 的几个匿名实现,提供<strong>应用层面事件发生、完成、抛出异常三种情况下对给定指标的计数或RT的计算</strong>,大多数用做处理应用层面指标事件的 MetricsListener 都是它提供的三个监听器实现:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MetricsApplicationListener</span> <span style="color:#268bd2">extends</span> AbstractMetricsListener <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsApplicationListener</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//此处的Event均为TimeCounterEvent,在它被创建时就会自动开始计时 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onPostEventBuild</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> AbstractMetricsListener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>increment<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> metricsKey<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onFinishEventBuild</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> MetricsPlaceType placeType<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> AbstractMetricsListener<span style="color:#719e07">.</span>onFinish<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>increment<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> metricsKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>addRt<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> placeType<span style="color:#719e07">.</span>getType<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onErrorEventBuild</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> MetricsPlaceType placeType<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> AbstractMetricsListener<span style="color:#719e07">.</span>onError<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>increment<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> metricsKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>addRt<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> placeType<span style="color:#719e07">.</span>getType<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>还有 MetricsServiceListener(服务指标监听器),它和 MetricsApplicationListener 十分类似,提供的是服务层面的指标监听器的通用实现,不再重复分析。</p> |
| <p><strong>可以用一句话简单的总结这三个 Collector 注册指标监听器的流程 : Collector 内部的 Mulicaster/Dispatcher 在被 Collector 创建时直接向自己注册已声明的指标监听器。</strong></p> |
| <p>至此,我们可以总结出 MetricsEvent 的部分消息转发路径 :</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/event-dispatch-simple.png" alt="event-dispatch-simple"></p> |
| <h3 id="2dubbo-核心模块的指标采集流程">2,Dubbo 核心模块的指标采集流程</h3> |
| <p>DefaultMetricsCollector(默认指标采集器) 作为指标采集器的默认实现,其主要通过采样器(Sampler)收集dubbo应用核心RPC功能的相关指标。 采样器包括以下几种:</p> |
| <ul> |
| <li>线程池线程状态(最大线程数、最小线程数、活跃线程数等),对应 <strong>ThreadPoolMetricsSampler,线程池指标采样器</strong></li> |
| <li>线程池中线程耗尽事件的计数,对应 <strong>ThreadRejectMetricsCountSampler, 线程耗尽次数采样器</strong></li> |
| <li>应用指标收集情况(收集次数),对应 DefaultMetricsCollector 中实现的 SimpleMetricsCountSampler 匿名子类</li> |
| </ul> |
| <p><strong>这些采样器内部会存储其负责采样类型指标的样本。由于默认指标采集器同样继承自 CombMetricsCollector,它也同时具有与前文中分析的三大中心指标收集器相似的指标转发流程。</strong></p> |
| <p>除了线程池指标采样器,其它两个采样器均实现自简单指标计数采样器(SimpleMetricsCountSampler)。它实现了通用的指标存取操作。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/sampler-struct.png" alt="sampler-struct"></p> |
| <p>简单指标计数采样器内部的指标样本容器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">abstract</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">SimpleMetricsCountSampler</span><span style="color:#719e07">&lt;</span>S<span style="color:#719e07">,</span> K<span style="color:#719e07">,</span> M <span style="color:#268bd2">extends</span> Metric<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">implements</span> MetricsCountSampler<span style="color:#719e07">&lt;</span>S<span style="color:#719e07">,</span> K<span style="color:#719e07">,</span> M<span style="color:#719e07">&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>K<span style="color:#719e07">,</span> ConcurrentMap<span style="color:#719e07">&lt;</span>M<span style="color:#719e07">,</span> AtomicLong<span style="color:#719e07">&gt;&gt;</span> metricCounter <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>其中:泛型 M 为指标类型,如方法指标 MethodMetric;泛型 K 为指标名称类型,如 String;泛型 S 为请求源类型,如 String 或 Invocation。请求源用于定位触发采样的请求来源,指标名称则用于对指标进行分组,便于按名称来分组检索指标数据。</strong></p> |
| <p>以及对特定指标的增减操作:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsCountSampler |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">inc</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doExecute<span style="color:#719e07">(</span>source<span style="color:#719e07">,</span> metricName<span style="color:#719e07">,</span> counter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> counter<span style="color:#719e07">.</span>incrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">dec</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doExecute<span style="color:#719e07">(</span>source<span style="color:#719e07">,</span> metricName<span style="color:#719e07">,</span> counter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> counter<span style="color:#719e07">.</span>decrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">incOnEvent</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doExecute<span style="color:#719e07">(</span>source<span style="color:#719e07">,</span> metricName<span style="color:#719e07">,</span> counter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> counter<span style="color:#719e07">.</span>incrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">decOnEvent</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doExecute<span style="color:#719e07">(</span>source<span style="color:#719e07">,</span> metricName<span style="color:#719e07">,</span> counter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> counter<span style="color:#719e07">.</span>decrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>对于四个增加、减少计数的方法,它们最终都会调用 <code>doExecute</code> 方法来完成计数操作,其中 counter 函数定义了对计数器的操作(增加、减少)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsCountSampler |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExecute</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricsName<span style="color:#719e07">,</span> Function<span style="color:#719e07">&lt;</span>AtomicLong<span style="color:#719e07">,</span> Boolean<span style="color:#719e07">&gt;</span> counter<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetricsCountSampleConfigurer<span style="color:#719e07">&lt;</span>S<span style="color:#719e07">,</span> K<span style="color:#719e07">,</span> M<span style="color:#719e07">&gt;</span> sampleConfigure <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCountSampleConfigurer<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> sampleConfigure<span style="color:#719e07">.</span>setSource<span style="color:#719e07">(</span>source<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> sampleConfigure<span style="color:#719e07">.</span>setMetricsName<span style="color:#719e07">(</span>metricsName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//利用子类重写的countConfigure为 sampleConfigure 设置事件发布函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>countConfigure<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过指标名获取对应的指标计数器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>M<span style="color:#719e07">,</span> AtomicLong<span style="color:#719e07">&gt;</span> metricAtomic <span style="color:#719e07">=</span> metricCounter<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>metricsName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metricAtomic <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metricAtomic <span style="color:#719e07">=</span> metricCounter<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>metricsName<span style="color:#719e07">,</span> k <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">.</span>getMetric<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;metrics is null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> AtomicLong atomicCounter <span style="color:#719e07">=</span> metricAtomic<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">.</span>getMetric<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>atomicCounter <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> atomicCounter <span style="color:#719e07">=</span> metricAtomic<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">.</span>getMetric<span style="color:#719e07">(),</span> k <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> AtomicLong<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// counter函数定义了对atomicCounter的增减操作,如 inc方法定义的counter是对atomicCounter+1,dec方法定义的是对atomicCounter-1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Boolean isEvent <span style="color:#719e07">=</span> counter<span style="color:#719e07">.</span>apply<span style="color:#719e07">(</span>atomicCounter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果本次计数操作应该触发事件... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isEvent<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取子类设置的事件发布函数,发布事件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> sampleConfigure<span style="color:#719e07">.</span>getFireEventHandler<span style="color:#719e07">().</span>accept<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><code>doExecute</code> 做了两件事:</p> |
| <p>1,判断当前指标是否存在,如果不存在就放到容器中。</p> |
| <p>2,调用提供的计数函数对指标进行修改,对应 counter 字段。</p> |
| <p>以下为各采样器在 countConfigure 方法中提供的创建指标实例的逻辑:</p> |
| <ul> |
| <li>DefaultMetricsCollector 中 SimpleMetricsCountSampler 的匿名实现 (applicationSampler)提供的<code>countConfigure</code> 方法:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">countConfigure</span><span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> MetricsCountSampleConfigurer<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> MetricsEvent<span style="color:#719e07">.</span>Type<span style="color:#719e07">,</span> ApplicationMetric<span style="color:#719e07">&gt;</span> sampleConfigure<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供根据 configure 创建指标实例的函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> sampleConfigure<span style="color:#719e07">.</span>configureMetrics<span style="color:#719e07">(</span>configure <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ApplicationMetric<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">.</span>getSource<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ul> |
| <li>ThreadRejectMetricsCountSampler 中提供的 <code>countConfigure</code> 方法:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">countConfigure</span><span style="color:#719e07">(</span>MetricsCountSampleConfigurer<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">,</span> ThreadPoolRejectMetric<span style="color:#719e07">&gt;</span> sampleConfigure<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供根据 configure 创建指标实例的函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> sampleConfigure<span style="color:#719e07">.</span>configureMetrics<span style="color:#719e07">(</span>configure <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ThreadPoolRejectMetric<span style="color:#719e07">(</span>collector<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">(),</span>configure<span style="color:#719e07">.</span>getSource<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><hr> |
| <p>默认指标收集器继承自 CombMetricsCollector,内部包含一个 DefaultSubDispatcher,因此它自身也可以作为指标事件的转发器,接受其它指标监听器的注册。</p> |
| <p>在之前,我们发现了 AggregateMetricsCollector(聚合指标收集器)会将自己注册为 DefaultMetricsCollector 的监听器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AggregateMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> registerListener<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerListener</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span>applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>DefaultMetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>addListener<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>还有 HistogramMetricsCollector (直方图指标收集器)也会将自己注册为它的监听器。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">HistogramMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> registerListener<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerListener</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>DefaultMetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getEventMulticaster<span style="color:#719e07">().</span>addListener<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>因此,聚合指标收集器和直方图指标收集器的指标事件来源于默认指标收集器转发的指标事件</strong>。通过默认指标转发器的 isSupport 方法,还可以发现这些指标事件的类型是 RequestEvent (RPC请求事件)或 RequestBeforeEvent(请求前失败事件:实际请求发送之前在 ClusterFilter 中产生的异常)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isSupport</span><span style="color:#719e07">(</span>MetricsEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> event <span style="color:#719e07">instanceof</span> RequestEvent <span style="color:#719e07">||</span> event <span style="color:#719e07">instanceof</span> RequestBeforeEvent<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/default-metrics-collector-struct.png" alt="default-metrics-collector-struct"></p> |
| <p><strong>至此,我们也明确了 Dubbo 应用内部核心模块的相关指标是如何收集的:默认指标收集器除了接受上层指标转发器的指标事件之外,还会通过各种采样器对埋点采样,通过 SubDispatcher 统一转发指标事件,通知注册为它的监听器的其它 Collector 完成采样。</strong></p> |
| <h3 id="3-直方图相关指标的采集流程">3, 直方图相关指标的采集流程</h3> |
| <p>直方图指标收集器(HistogramMetricsCollector)也是一个较为特殊的收集器,它主要负责RPC调用响应时间直方图指标这一种指标的收集。</p> |
| <p>由于直方图指标收集器只需要采集单一类型的指标,它直接使用Map来存储采样数据,而非更复杂的数据聚合器(Composite)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//HistogramMetricsCollector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> ConcurrentHashMap<span style="color:#719e07">&lt;</span>MethodMetric<span style="color:#719e07">,</span> Timer<span style="color:#719e07">&gt;</span> rt <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span></code></pre></div><p><strong>其中,key为方法指标,Timer则是该方法对应的RT计时器。该计时器由 micrometer 提供,在跟踪短时间内的大量事件时具有良好的性能。</strong></p> |
| <p>前文中已经提到,直方图指标收集器在初始化时会将自己注册为默认指标收集器(DefaultMetricsCollector)中的监听器,与聚合指标收集器相同(AggregateMetricsCollector)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerListener</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>DefaultMetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getEventMulticaster<span style="color:#719e07">().</span>addListener<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这意味着它接收的指标事件实际也来自于默认指标收集器中的采样器。之前的分析中,我们知道默认指标收集器目前实际只转发来自 MetricsDispatcher 的请求相关事件,因此直方图指标收集器也只会收集请求响应时间相关的指标采样。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//HistogramMetricsCollector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onRTEvent</span><span style="color:#719e07">(</span>RequestEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metricRegister <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MethodMetric metric <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MethodMetric<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>MetricsConstants<span style="color:#719e07">.</span>INVOCATION<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">long</span> responseTime <span style="color:#719e07">=</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> HistogramMetricSample sample <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HistogramMetricSample<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>METRIC_RT_HISTOGRAM<span style="color:#719e07">.</span>getNameByType<span style="color:#719e07">(</span>metric<span style="color:#719e07">.</span>getSide<span style="color:#719e07">()),</span> |
| </span></span><span style="display:flex;"><span> MetricsKey<span style="color:#719e07">.</span>METRIC_RT_HISTOGRAM<span style="color:#719e07">.</span>getDescription<span style="color:#719e07">(),</span> metric<span style="color:#719e07">.</span>getTags<span style="color:#719e07">(),</span> RT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Timer timer <span style="color:#719e07">=</span> ConcurrentHashMapUtils<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>rt<span style="color:#719e07">,</span> metric<span style="color:#719e07">,</span> k <span style="color:#719e07">-&gt;</span> metricRegister<span style="color:#719e07">.</span>register<span style="color:#719e07">(</span>sample<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> timer<span style="color:#719e07">.</span>record<span style="color:#719e07">(</span>responseTime<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>当接收到事件时,直方图指标收集器会先计算当前调用花费的时间,然后为计时器(Time)添加一条响应时间记录。</p></description></item><item><title>Blog: 3-指标监听注册梳理</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</guid><description> |
| <h2 id="三指标监听注册梳理">三、指标监听注册梳理</h2> |
| <p>在前一章中,我们了解了不同收集器中的指标样本是如何被监听器添加进去的。接下来,我们将归纳指标监听器 的创建位置,及它们对应统计的指标。</p> |
| <p>通过之前的分析,我们已经知道指标 注册事件多播器(RegistryMetricsEventMulticaster)中定义了并绑定了服务注册相关的指标。这种绑定操作同样存在于其它几个简单指标事件多播器(SimpleMetricsEventMulticaster)的几个实现中。</p> |
| <h3 id="转发器注册">转发器注册</h3> |
| <p><strong>RegistrySubDispatcher (服务注册指标转发器)注册了服务注册相关指标:</strong></p> |
| <ul> |
| <li>应用级实例注册成功/失败/总数计数 (APPLICATION_REGISTER_&hellip;)</li> |
| <li>应用级服务接口订阅成功/失败/总数计数 (APPLICATION_SUBSCRIBE_&hellip;)</li> |
| <li>服务级注册成功/失败/总数计数 (SERVICE_REGISTER_&hellip;)</li> |
| <li>特殊的 APPLICATION_NOTIFY_FINISH 和 APPLICATION_DIRECTORY_POST (应用服务目录变化次数)</li> |
| </ul> |
| <p><strong>MetadataSubDispatcher(元数据指标转发器)注册应用元数据相关指标</strong></p> |
| <ul> |
| <li> |
| <p>应用推送元数据相关计数 (APPLICATION_PUSH_&hellip;)</p> |
| </li> |
| <li> |
| <p>应用订阅元数据相关计数 (APPLICAITON_SUBSCRIBE_&hellip;)</p> |
| </li> |
| <li> |
| <p>服务订阅元数据相关计数 (SERVICE_SUBSCRIBE_&hellip;)</p> |
| </li> |
| </ul> |
| <p><strong>ConfigCenterSubDispatcher (配置中心指标转发器) 注册配置中心配置更新次数指标</strong></p> |
| <ul> |
| <li>配置中心推送新配置次数 (CONFIGCENTER_METRIC_TOTAL)</li> |
| </ul> |
| <p><strong>DefaultSubDispatcher (默认转发器) 注册核心RPC调用次数指标</strong></p> |
| <ul> |
| <li>请求次数 (METRIC_REQUESTS)</li> |
| <li>请求成功次数(METRIC_REQUESTS_SUCCEED)</li> |
| <li>请求失败次数(METRIC_REQUEST_BUSINESS_FAILED)</li> |
| </ul> |
| <p><strong>MetricsDispatcher</strong></p> |
| <p>MetricsDispatcher 较为特殊,它负责 ApplicationModel 下所有 MetricsCollector(前文中提到的指标收集器) 的初始化注册工作,并将它们添加到自己的监听器列表中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MetricsDispatcher</span> <span style="color:#268bd2">extends</span> SimpleMetricsEventMulticaster <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@SuppressWarnings</span><span style="color:#719e07">({</span><span style="color:#2aa198">&#34;rawtypes&#34;</span><span style="color:#719e07">})</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsDispatcher</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ScopeBeanFactory beanFactory <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>MetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>extensionLoader <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> customizeCollectors <span style="color:#719e07">=</span> extensionLoader |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getActivateExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricsCollector customizeCollector <span style="color:#719e07">:</span> customizeCollectors<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> beanFactory<span style="color:#719e07">.</span>registerBean<span style="color:#719e07">(</span>customizeCollector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> customizeCollectors<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>addListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>需要注意,以上几个实现均继承自 SimpleMetricsEventMulticaster,因此它们都具有注册监听、转发事件的能力。它们将自己注册到对应领域的指标 Collector 中,并在收到指标事件时转发到自己注册的监听器中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsEventMulticaster |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addListener</span><span style="color:#719e07">(</span>MetricsListener<span style="color:#719e07">&lt;?&gt;</span> listener<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> listeners<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>listener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishEvent</span><span style="color:#719e07">(</span>MetricsEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>event <span style="color:#719e07">instanceof</span> EmptyEvent<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>validateIfApplicationConfigExist<span style="color:#719e07">(</span>event<span style="color:#719e07">))</span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricsListener listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>listener<span style="color:#719e07">.</span>isSupport<span style="color:#719e07">(</span>event<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> listener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>event<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//... |
| </span></span></span></code></pre></div><p><strong>SubDispatcher 和 Collector 之间的对应关系:</strong></p> |
| <ul> |
| <li>MetadataSubDispatcher -&gt; MetadataMetricsCollector 元数据指标事件</li> |
| <li>RegistrySubDispatcher -&gt; RegistryMetricsCollector 服务注册指标事件</li> |
| <li>ConfigCenterSubDispatcher -&gt; ConfigCenterMetricsCollector 配置中心指标事件</li> |
| <li>MetricsDispatcher 由 MetricsEventBus 通过 BeanFactory 加载。它是所有事件转发的入口。</li> |
| </ul> |
| <h3 id="事件触发">事件触发</h3> |
| <p>剩下的问题就是这些监听器是如何被触发的。</p> |
| <p>可以发现三大中心的指标转发器都是在它们对应的Collector中创建的:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ConfigCenterMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>setEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ConfigCenterMetricsDispatcher<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">MetadataMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>setEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">new</span> MetadataMetricsEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">RegistryMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>setEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryMetricsEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这意味这想要通过它们发布事件,需要通过它们对应的 <code>Collector</code> 来访问。</p> |
| <p>如前文所述, MetricsDispatcher 在初始化时会尝试获取并加载所有 MetricsCollector 的SPI拓展,</p> |
| <p>三大中心的MetricsCollector (Metadata/Registry/ConfigCenter)也会在这里被初始化,并添加为 MetricsDispatcher 的监听器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsDispatcher</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>MetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricsCollector customizeCollector <span style="color:#719e07">:</span> customizeCollectors<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> beanFactory<span style="color:#719e07">.</span>registerBean<span style="color:#719e07">(</span>customizeCollector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> customizeCollectors<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>addListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>对于 MetricsDispatcher,它由 MetricsEventBus 创建。而 MetricsEventBus 自身作为指标相关消息的总线,会接收所有指标消息,并将它们转发给监听者。</p> |
| <p>MetricsEvenetBus 提供了三个方法来发布指标事件:</p> |
| <ul> |
| <li><code>publish(MetricsEvent event)</code> ,将事件发布给所有订阅者,只发布一次且不关心事件处理结果</li> |
| <li><code>post(MetricsEvent event, Supplier&lt;T&gt; targetSupplier)</code> ,将事件发布给所有订阅者,并根据是否产生异常判断事件成功或失败,调用 MetricsDispatcher 发布对应的事件。</li> |
| <li><code>post(MetricsEvent event, Supplier&lt;T&gt; targetSupplier, Function&lt;T, Boolean&gt; trFunction)</code> ,额外的 trFunction 可用于通过业务结果判断事件成功或失败。 <code>targetSupplier</code> 为业务操作函数,泛型T为业务结果类型。</li> |
| </ul> |
| <p>这三个方法均会通过 MetricsDispatcher 来转发事件。</p> |
| <p>在之前的分析中,我们知道 MetricsDispatcher 创建了所有 MetricsCollector 拓展,并将它们注册为自己的监听者。</p> |
| <p>因此,当 MetricsEventBus 接收到发布的信息时,它会将信息转发到所有 MetricsCollector 中。对于 CombMetricsCollector 的实现,它们又会调用自己创建的 MetricsEventMulticaster 再次转发消息,到具体指标的监听器。</p> |
| <p>之后,这些监听器就会根据自己的逻辑修改Collector中的指标计数。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/metris-event-dispatch-full.png" alt="image-20230629160012950"></p> |
| <h3 id="事件发布">事件发布</h3> |
| <p>接下来,我们将寻找指标事件发布的源头。</p> |
| <p>通过前文的分析,我们知道 MetricsEventBus 是所有指标事件发布的入口。具体来说,它有以下的用法:</p> |
| <ul> |
| <li>AbstractDirectory</li> |
| <li>ServiceConfig</li> |
| <li>DefaultApplicationDeployer</li> |
| <li>ApolloDynamicConfiguration</li> |
| <li>NacosDynamicConfiguration</li> |
| <li>ZookeeperDataListener</li> |
| <li>AbstractMetadataReport</li> |
| </ul> |
| <p>我们将逐个分析每个用法。</p> |
| <p><strong>AbstractDirectory</strong></p> |
| <p>AbstractDirectory 在修改 Invoker 状态相关的操作完成后都会通过 MetricsEventBus 发布 refreshDirectoryEvent(服务目录更新事件,类型为 RegistryEvent ),将当前目录中各种状态 Invoker 实例的最新数量作为附件添加到 RegistryEvent 中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//AbstractDirectory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">recoverDisabledInvoker</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>RegistryEvent<span style="color:#719e07">.</span>refreshDirectoryEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> getSummary<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setInvokers</span><span style="color:#719e07">(</span>BitList<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;&gt;</span> invokers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>RegistryEvent<span style="color:#719e07">.</span>refreshDirectoryEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> getSummary<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setInvokers</span><span style="color:#719e07">(</span>BitList<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;&gt;</span> invokers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>RegistryEvent<span style="color:#719e07">.</span>refreshDirectoryEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> getSummary<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> Map<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;&gt;</span> <span style="color:#268bd2">getSummary</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;&gt;</span> summaryMap <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目录中可用的Invoker数量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> summaryMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_VALID<span style="color:#719e07">,</span> groupByServiceKey<span style="color:#719e07">(</span>getValidInvokers<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目录中不可用的Invoker数量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> summaryMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_DISABLE<span style="color:#719e07">,</span> groupByServiceKey<span style="color:#719e07">(</span>getDisabledInvokers<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目录中等待重连的Invoker数量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> summaryMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_TO_RECONNECT<span style="color:#719e07">,</span> groupByServiceKey<span style="color:#719e07">(</span>getInvokersToReconnect<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> summaryMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_ALL<span style="color:#719e07">,</span> groupByServiceKey<span style="color:#719e07">(</span>getInvokers<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> summaryMap<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span></code></pre></div><p>该事件最终会由 RegistryMetricsCollector 中的 RegistryMetricsDispatcher 转发到关系该事件的监听器中。<strong>事件和监听器之间通过 MetricsKey匹配</strong> 。</p> |
| <p>最终,MetricsKey 为 <code>**DIRECTORY_METRIC_NUM_VALID**</code> 的监听器会处理这个事件,并更新 Collector中的计数。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//DIRECTORY_METRIC_NUM_VALID 对应的 Listener。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>MetricsCat APPLICATION_DIRECTORY_POST <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCat<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_VALID<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> AbstractMetricsListener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;&gt;</span> summaryMap <span style="color:#719e07">=</span> event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>ATTACHMENT_DIRECTORY_MAP<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> summaryMap<span style="color:#719e07">.</span>forEach<span style="color:#719e07">((</span>metricsKey<span style="color:#719e07">,</span> map<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>k<span style="color:#719e07">,</span> v<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>setNum<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> k<span style="color:#719e07">,</span> v<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">));</span> |
| </span></span></code></pre></div><p>这样,服务目录中不同状态 Invoker 的计数就通过 RegistryMetricsCollector 更新到了 ServiceStatComposite 中。</p> |
| <p><strong>ServiceConfig</strong></p> |
| <p>当通过 ServiceConfig 导出、注册一个服务时,它会发布一个服务导出事件。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ServiceConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">protected</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExport</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> doExportUrls<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> exported<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrls</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>post<span style="color:#719e07">(</span>RegistryEvent<span style="color:#719e07">.</span>toRsEvent<span style="color:#719e07">(</span>module<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">(),</span> getUniqueServiceName<span style="color:#719e07">(),</span> protocols<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">*</span> registryURLs<span style="color:#719e07">.</span>size<span style="color:#719e07">()),</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//该函数会被同步执行,如果抛出异常则触发 MetricsEvent 的 onError方法,否则触发 onFinish |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ProtocolConfig protocolConfig <span style="color:#719e07">:</span> protocols<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String pathKey <span style="color:#719e07">=</span> URL<span style="color:#719e07">.</span>buildKey<span style="color:#719e07">(</span>getContextPath<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span>p <span style="color:#719e07">-&gt;</span> p <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;/&#34;</span> <span style="color:#719e07">+</span> path<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>orElse<span style="color:#719e07">(</span>path<span style="color:#719e07">),</span> group<span style="color:#719e07">,</span> version<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>serverService<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>pathKey<span style="color:#719e07">,</span> interfaceClass<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> doExportUrlsFor1Protocol<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> providerModel<span style="color:#719e07">.</span>setServiceUrls<span style="color:#719e07">(</span>urls<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>事件发布时,该事件会被转发到 RegistryMetricsCollector,触发对应的 Listener 增加 <strong>SERVICE_REGISTER_METRIC_REQUESTS</strong> (当前服务级注册请求总数)的计数,然后执行定义的 provider 函数。根据是否抛出异常,之后执行 onError 方法 或 onFinish 方法,增加 <strong>SERVICE_REGISTER_METRIC_REQUESTS_FAILED</strong> (当前服务级注册请求失败总数)或<strong>SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED</strong> (当前服务级注册请求成功总数) 的计数。</p> |
| <p><strong>DefaultApplicationDeployer</strong></p> |
| <p>它在应用部署过程中,初始化配置中心时,发布配置发生改变的事件。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startConfigCenter</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> compositeDynamicConfiguration<span style="color:#719e07">.</span>addConfiguration<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> prepareEnvironment<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> DynamicConfiguration <span style="color:#268bd2">prepareEnvironment</span><span style="color:#719e07">(</span>ConfigCenterConfig configCenter<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Add metrics |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> configCenter<span style="color:#719e07">.</span>getConfigFile<span style="color:#719e07">(),</span> configCenter<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span>configCenter<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">(),</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> configMap<span style="color:#719e07">.</span>size<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isNotEmpty<span style="color:#719e07">(</span>appGroup<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> appConfigFile<span style="color:#719e07">,</span> appGroup<span style="color:#719e07">,</span> configCenter<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">(),</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> appConfigMap<span style="color:#719e07">.</span>size<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在 <code>prepareEnvironment</code> 方法中,会按配置中心设置的组(group)和当前应用程序的名称作为组名发布两次事件。该事件会被转发到 ConfigCenterMetricsCollector,增加 <strong>CONFIGCENTER_METRIC_TOTAL</strong> (配置中心配置变化次数)的计数。</p> |
| <p><strong>ApolloDynamicConfiguration</strong></p> |
| <p>动态配置功能的 Apollo 配置中心实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ApolloDynamicConfiguration |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onChange</span><span style="color:#719e07">(</span>com<span style="color:#719e07">.</span>ctrip<span style="color:#719e07">.</span>framework<span style="color:#719e07">.</span>apollo<span style="color:#719e07">.</span>model<span style="color:#719e07">.</span>ConfigChangeEvent changeEvent<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span>ConfigCenterEvent<span style="color:#719e07">.</span>APOLLO_PROTOCOL<span style="color:#719e07">,</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> SELF_INCREMENT_SIZE<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>当Apollo配置中心的配置发生变化时,它的 <code>onChange</code> 方法会被触发,并在最后发布一个 ConfigCenterEvent。该事件最终转发到ConfigCenterMetricsCollector 中,同样增加 **CONFIGCENTER_METRIC_TOTAL **的计数。</p> |
| <p><strong>NacosDynamicConfiguration</strong></p> |
| <p>动态配置功能的 Nacos 配置中心实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//NacosDynamicConfiguration |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">innerReceive</span><span style="color:#719e07">(</span>String dataId<span style="color:#719e07">,</span> String group<span style="color:#719e07">,</span> String configInfo<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> ConfigCenterEvent<span style="color:#719e07">.</span>NACOS_PROTOCOL<span style="color:#719e07">,</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> SELF_INCREMENT_SIZE<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>当Nacos配置中心的配置发生变化时,它的 <code>innerReceive</code> 方法被触发,发布一个 ConfigCenterEvent。它的处理流程和 ApolloDynamicConfiguration 一致,最终增加 **CONFIGCENTER_METRIC_TOTAL **的计数。</p> |
| <p><strong>ZookeeperDataListener</strong></p> |
| <p>动态配置功能的 Zookeeper 实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ZookeeperDataListener |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">dataChanged</span><span style="color:#719e07">(</span>String path<span style="color:#719e07">,</span> Object value<span style="color:#719e07">,</span> EventType eventType<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> configChangeEvent<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> configChangeEvent<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> ConfigCenterEvent<span style="color:#719e07">.</span>ZK_PROTOCOL<span style="color:#719e07">,</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> SELF_INCREMENT_SIZE<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在指标收集层面,它的行为和前文中两个配置中心一致,此处不详细展开三个配置中心具体实现的异同。</p> |
| <p><strong>AbstractMetadataReport</strong></p> |
| <p>元数据报告接口的抽象实现。它的三个实现 (NacosMetadataReport、RedisMetadataReport、ZookeeperMetadataReport)均使用它的指标事件逻辑。</p> |
| <p>当订阅新服务并获取它的元数据时,它会发布一个 MetadataEvent 触发相关指标的修改。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadataTask</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>post<span style="color:#719e07">(</span>metadataEvent<span style="color:#719e07">,</span> <span style="color:#719e07">()</span> <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> result <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> doStoreProviderMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> saveProperties<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> <span style="color:#719e07">!</span>syncReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> result <span style="color:#719e07">=</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> aBoolean <span style="color:#719e07">-&gt;</span> aBoolean |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在前文中,我们提到 MetricsEventBus.post 的第二个参数是实际要进行的业务操作,第三个参数则是根据业务操作返回值判断操作是否成功的逻辑。</p> |
| <p>此处的业务操作是尝试存储目标服务的元数据。执行操作之前,会先发布事件,最终增加 <strong>STORE_PROVIDER_METADATA</strong> (尝试存储服务元数据次数)的计数。如果产生异常,会增加 <strong>STORE_PROVIDER_METADATA_ERROR</strong> (存储服务元数据失败) 的计数,否则增加<strong>STORE_PROVIDER_METADATA_SUCCEED</strong>(存储服务元数据成功) 的计数。</p></description></item><item><title>Blog: 4-指标转换与导出</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</guid><description> |
| <h2 id="四指标转换与导出">四、指标转换与导出</h2> |
| <p>本章主要梳理指标收集完成后,向外部收集器导出的流程。</p> |
| <p>通过之前的分析,我们知道不同类型指标的收集分别由各个 Collector 实现进行。它们底层的 MetricsCollector 接口定义了指标导出的操作。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@SPI</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">MetricsCollector</span><span style="color:#719e07">&lt;</span>E <span style="color:#268bd2">extends</span> TimeCounterEvent<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">extends</span> MetricsLifeListener<span style="color:#719e07">&lt;</span>E<span style="color:#719e07">&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isCollectEnabled</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Collect metrics as {@link MetricSample} |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return List of MetricSample |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricSample<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">collect</span><span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>而指标报告器 (MetricsReporter) 的实现会定时调用Collector 的 <code>collect</code> 方法,更新并导出指标数据。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">MetricsReporter</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新统计数据,定时调用collect() |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshData</span><span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取指标数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String <span style="color:#268bd2">getResponse</span><span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取带指标名的指标样本(单个指标) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">default</span> String <span style="color:#268bd2">getResponseWithName</span><span style="color:#719e07">(</span>String metricsName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>指标报告器有两个实现:DefaultMetricsReporter 和 PrometheusMetricsReporter,它们都实现自 AbstractMetricsRepoter,并使用它的指标刷新逻辑 (<code>refreshData</code>方法)。</p> |
| <p>AbstractMetricsRepoter 初始化时会获取并保存所有 Collector 的实现,每次刷新数据,调用<code>refreshData</code>方法时都会遍历这些收集器,更新指标数据。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//AbstractMetricsRepoter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initCollectors</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> otherCollectors <span style="color:#719e07">=</span> beanFactory<span style="color:#719e07">.</span>getBeansOfType<span style="color:#719e07">(</span>MetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> collectors<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>otherCollectors<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshData</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collectors<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>collector <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricSample<span style="color:#719e07">&gt;</span> samples <span style="color:#719e07">=</span> collector<span style="color:#719e07">.</span>collect<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricSample sample <span style="color:#719e07">:</span> samples<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将Dubbo的度量类型适配为micrometer的度量类型,并将其添加到CompositeMeterRegistry中,借此实现多监控系统的支持。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span></code></pre></div><p>DefaultMetricsReporter 和 PrometheusMetricsReporter 各自实现了自己的指标采样逻辑 (<code>getResponse</code>方法)。</p> |
| <p><strong>PrometheusMetricsReporter</strong></p> |
| <p>它通过 PrometheusMeterRegistry 获取普罗米修斯支持格式的样本数据:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//PrometheusMetricsReporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">getResponse</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> prometheusRegistry<span style="color:#719e07">.</span>scrape<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在初始化时,它会开启一个定时任务,定时向普罗米修斯服务端推送采样数据:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//PrometheusMetricsReporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> SimpleMeterRegistry meterRegistry <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SimpleMeterRegistry<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doInit</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> addMeterRegistry<span style="color:#719e07">(</span>prometheusRegistry<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> schedulePushJob<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">schedulePushJob</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里的URL是DefaultMetricsReporter中定义的指标报告URL,提供了指标服务的具体地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> pushEnabled <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>PROMETHEUS_PUSHGATEWAY_ENABLED_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>pushEnabled<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> pushJobExecutor<span style="color:#719e07">.</span>scheduleWithFixedDelay<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> push<span style="color:#719e07">(</span>pushGateway<span style="color:#719e07">,</span> job<span style="color:#719e07">),</span> pushInterval<span style="color:#719e07">,</span> pushInterval<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>SECONDS<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">push</span><span style="color:#719e07">(</span>PushGateway pushGateway<span style="color:#719e07">,</span> String job<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> refreshData<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将本次采样数据添加到pushGateway,等待下次抓取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> pushGateway<span style="color:#719e07">.</span>pushAdd<span style="color:#719e07">(</span>prometheusRegistry<span style="color:#719e07">.</span>getPrometheusRegistry<span style="color:#719e07">(),</span> job<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>PushGateway 和 PrometheusRegistry 均为 micrometer 提供的 API。</p> |
| <p><strong>PrometheusRegistry 将度量数据转换为普罗米修斯支持的格式,而 PushGateway 存储样本,暴露一个HTTP端点供普罗米修斯服务端抓取。</strong></p> |
| <p>PushGateway 本身只是一个度量数据的缓存区,普罗米修斯服务端每从其中抓取一次数据,其内部的样本就会被清除。</p> |
| <p><strong>DefaultMetricsReporter</strong></p> |
| <p>指标报告器的默认实现,提供了按指标名称导出特定指标的方法。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//DefaultMetricsReporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">getResponse</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">getResponseWithName</span><span style="color:#719e07">(</span>String metricsName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> meterRegistry<span style="color:#719e07">.</span>getMeters<span style="color:#719e07">().</span>stream<span style="color:#719e07">().</span>filter<span style="color:#719e07">(</span>meter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据名称过滤样本 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> metricsValue<span style="color:#719e07">.</span>forEach<span style="color:#719e07">((</span>key<span style="color:#719e07">,</span> value<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//按格式拼装结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> sb<span style="color:#719e07">.</span>toString<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doInit</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> addMeterRegistry<span style="color:#719e07">(</span>meterRegistry<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doDestroy</span><span style="color:#719e07">()</span> <span style="color:#719e07">{}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>该实现使用的 SimpleMeterRegistry 本身只会存储指标数据的功能,而不像 PrometheusMeterRegistry 那样提供发布数据的方法。</p> |
| <p><strong>因此该指标报告器不会主动向外部发布数据,而是被动的通过 <code>getResponseWithName</code> 提供数据。</strong></p> |
| <p>而且,该报告器在任何情况下都会被初始化:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//DefaultApplicationDeployer#initMetricsReporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">//If the protocol is not the default protocol, the default protocol is also initialized. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>PROTOCOL_DEFAULT<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>metricsConfig<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> DefaultMetricsReporterFactory defaultMetricsReporterFactory <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DefaultMetricsReporterFactory<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> MetricsReporter defaultMetricsReporter <span style="color:#719e07">=</span> defaultMetricsReporterFactory<span style="color:#719e07">.</span>createMetricsReporter<span style="color:#719e07">(</span>metricsConfig<span style="color:#719e07">.</span>toUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> defaultMetricsReporter<span style="color:#719e07">.</span>init<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>registerBean<span style="color:#719e07">(</span>defaultMetricsReporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span></code></pre></div><p>实际上,该类主要在用户使用Qos命令查询时提供指定指标数据,而非提供给某个特定的外部指标中心。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//Qos命令 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DefaultMetricsReporterCmd</span> <span style="color:#268bd2">implements</span> BaseCommand <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> String <span style="color:#268bd2">getResponseByApplication</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">,</span> String metricsName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String response <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;DefaultMetricsReporter not init&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> MetricsReporter metricsReporter <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>DefaultMetricsReporter<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metricsReporter <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metricsReporter<span style="color:#719e07">.</span>refreshData<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取指定名称指标的数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> response <span style="color:#719e07">=</span> metricsReporter<span style="color:#719e07">.</span>getResponseWithName<span style="color:#719e07">(</span>metricsName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> response<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>*需要注意的是,PrometheusMetricsReporter 也支持使用 Qos 命令查询内部指标数据,同样有对应的 PrometheusMetricsReporterCmd 实现,它们的工作原理相似。</p> |
| <p>以上就是指标样本从收集完成到最终导出到外部指标中心的大致流程。</p></description></item><item><title>Blog: Dubbo 连接异构微服务体系 - 多协议&多注册中心</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/05/dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB-%E5%A4%9A%E5%8D%8F%E8%AE%AE%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</link><pubDate>Thu, 05 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/05/dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB-%E5%A4%9A%E5%8D%8F%E8%AE%AE%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</guid><description> |
| <p>从编程开发的角度来说,Dubbo 首先是一款 RPC 服务框架,它最大的优势在于提供了面向接口代理的服务编程模型,对开发者屏蔽了底层的远程通信细节。同时 Dubbo 也是一款服务治理框架,它为分布式部署的微服务提供了服务发现、流量调度等服务治理解决方案。</p> |
| <p>在这篇文章中,我们将以以上基础能力为背景,尝试突破 Dubbo 体系自身,探索如何利用 Dubbo 对多协议、多服务发现模型的支持,来实现异构微服务体系间的互联互通。在实际业务场景中,这可以用来解决异构技术体系共存场景下的通信问题,帮助公司实现在异构技术体系间作平滑迁移,解决大规模跨区域、多集群部署场景的地址发现及流量调度等问题。</p> |
| <h2 id="面向接口代理的透明服务开发框架">面向接口代理的透明服务开发框架</h2> |
| <p>我们还是从 <strong>Dubbo 是一个微服务开发框架</strong> 这个大家熟知的概念开始。就像 Spring 是开发 Java 应用的基础框架一样,我们经常会选用 Dubbo 作为开发微服务业的基础框架。 Dubbo 框架的最大优势我认为就在其面向接口的编程模型,使得开发远程服务调用就像开发本地服务一样(以 Java 语言为例):</p> |
| <ol> |
| <li>服务定义</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">GreetingsService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String <span style="color:#268bd2">sayHi</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ol start="2"> |
| <li>消费方调用服务</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// 和调用本地服务一样,完全透明。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@Reference</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> GreetingService greetingService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doSayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> greetingService<span style="color:#719e07">.</span>sayHi<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello world!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>下图是 Dubbo 的基本工作原理图,服务提供者与服务消费者之间通过注册中心协调地址,通过约定的协议实现数据交换。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img.png" alt="Dubbo basic work flow"></p> |
| <h2 id="同构异构微服务体系面临的问题">同构/异构微服务体系面临的问题</h2> |
| <p>关于 Dubbo 协议本身及其服务治理相关功能细节并不是本文的重点,我们今天将从一个更高的层次,来看看公司内部构建微服务体系所面的挑战,以及 Dubbo 能为架构选型和迁移等提供哪些解决思路。</p> |
| <p>一个公司内部的微服务可能都是基于某一个相同的服务框架开发的,比如说 Dubbo,对于这样的架构,我们称之为是<strong>同构的微服务体系</strong>;而有些公司的微服务可能是使用多个不同的服务框架所建设,我们称之为<strong>异构的微服务体系</strong>,多个不同技术栈微服务体系的共存在大型组织内还是非常普遍的,造成这种局面可能有很多原因。比如,可能是遗留系统带来的,也可能是公司正在做技术栈迁移,或者就是不同业务部门为了满足各自特殊需求而做的独立选型(这也意味着异构微服务体系的长期共存)。</p> |
| <p><strong>1. 异构微服务体系共存</strong></p> |
| <p>我们很容易想到的一个挑战是:**不同的体系间通常是使用不同的 RPC 通信协议、部署独立的注册中心集群,面对这种多协议、多注册中心集群的场景,要如何实现相互之间透明的地址发现和透明的 RPC 调用?**如果我们什么都不做,那么每个微服务体系就只能感知到自己体系内的服务状态,流量也在各自的体系内封闭。而要做到从体系 A 平滑的迁移到体系 B,或者想长期的保持公司内部多个体系的共存,则解决不同体系间的互联互通,实现流量的透明调度将是非常重要的环节。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_1.png" alt="2"></p> |
| <p><strong>2. Dubbo 体系内部</strong></p> |
| <p><strong>多协议、多注册中心集群的问题在同构的微服务体系中也可能存在,尤其是当一个组织内部的微服务规模增长到一定量级的时候。</strong></p> |
| <ul> |
| <li> |
| <p>我们可能要在不同的服务之间采用不同的通信协议,因为不同的服务面临不同的业务场景,而这也进一步导致了数据传输特点的不同,我们需要分别采用更适合各类业务特点的协议。比如典型的场景:我们可能对于普通的业务服务采用 Dubbo 协议,对于和 FrontEnd 交互的服务需要 HTTP 协议,而对于需要流式数据传输的业务则采用 gRPC 协议等等。</p> |
| </li> |
| <li> |
| <p>Dubbo 体系内部另一个常出现的问题是,在大规模分布式部署的场景下,微服务系统会做跨区域、跨注册中心的部署,这个时候就会出现多集群间地址同步和流量调度的问题。</p> |
| </li> |
| </ul> |
| <p>总结起来,**不论是同构体系还是异构体系,都面临对多协议通信、多注册中心集群地址发现的问题。**Dubbo 目前是支持多协议、多注册中心的,可以说就是为解决我们上面分析的 Dubbo 同构体系内的场景而设计的,因此下面我们从同构体系的多协议、多注册中心场景讲起,先了解 Dubbo 多协议、多注册中心的基本支持情况以及它们是如何工作的。而在后面的一章再进一步探索怎么扩展这个能力来支持异构微服务体系的互联互通。</p> |
| <h2 id="dubbo-体系内的多协议多注册中心机制">Dubbo 体系内的多协议、多注册中心机制</h2> |
| <p>我们将通过两个场景示例,来分别具体的讲一下 Dubbo 的多协议、多注册中心机制的使用方式和工作原理。</p> |
| <h3 id="多协议">多协议</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_2.png" alt="undefined"></p> |
| <p>以上是使用 Dubbo 开发的一套微服务,服务间通信使用到了不同的协议,根据我们的调研发现,公司内部启用多协议其实是非常普遍需求,具体场景在此我们暂不做解释。</p> |
| <p>应用 B 作为服务提供者,发布了 5 个服务,其中:</p> |
| <ul> |
| <li><code>DemoService1</code> <code>DemoService2</code> 通过 <code>dubbo</code> 协议发布</li> |
| <li><code>DemoService3</code> <code>DemoService4</code> 通过 <code>gRPC</code> 协议发布</li> |
| <li><code>DemoService0</code> 通过 <code>dubbo</code> 、<code>gRPC</code> 双协议发布</li> |
| </ul> |
| <p>应用 A 作为消费者,使用 dubbo 协议消费 <code>DemoService1</code> <code>DemoService2</code>,使用 gRPC 协议消费 <code>DemoService0</code>。</p> |
| <p>应用 B 作为消费者,使用 gRPC 协议消费 <code>DemoService2</code> <code>DemoService4</code>,使用 dubbo 协议消费 <code>DemoService0</code>。</p> |
| <p>以下是具体的代码配置:</p> |
| <ol> |
| <li>提供端应用 B</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService1&#34;</span> protocol=<span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService2&#34;</span> protocol=<span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService3&#34;</span> protocol=<span style="color:#2aa198">&#34;grpc&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService4&#34;</span> protocol=<span style="color:#2aa198">&#34;grpc&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService0&#34;</span> protocol=<span style="color:#2aa198">&#34;dubbo, grpc&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><ol start="2"> |
| <li>消费端应用 A</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> protocol=<span style="color:#2aa198">&#34;dubbo&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService1&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> protocol=<span style="color:#2aa198">&#34;dubbo&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService2&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> protocol=<span style="color:#2aa198">&#34;grpc&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService0&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><ol start="3"> |
| <li>消费端应用 C</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> protocol=<span style="color:#2aa198">&#34;grpc&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService3&#34;</span><span style="color:#268bd2">/&gt;</span> <span style="color:#268bd2">&lt;dubbo:reference</span> protocol=<span style="color:#2aa198">&#34;grpc&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService4&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> protocol=<span style="color:#2aa198">&#34;dubbo&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService0&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><h4 id="dubbo-多协议支持现状">Dubbo 多协议支持现状</h4> |
| <p>Dubbo 目前所支持的协议包括 Dubbo、REST、Thrift、gRPC、JsonRPC、Hessian 等,基本涵盖了业界大多数主流的 RPC 通信协议。需要注意的是,这些协议的支持都是以直接集成官方 Release 实现的形式来做的,我认为这是一个很好的选择,既保证了协议解析自身的稳定性,又能使 Dubbo 社区更专注的将更多的精力放在 Dubbo 外围服务治理能力的改善上。试想如果 Dubbo 社区自己为每个协议提供实现,那是要花费多少精力和时间才能使每种协议达到稳定的生产可用。</p> |
| <p>除了以上官方提供支持的协议之外,得益于 Dubbo 灵活的扩展机制,想要为 Dubbo 扩展协议非常容易,开发者可以随时为 Dubbo 增加更多的协议支持,包括自有协议扩展。</p> |
| <p>关于对 gRPC (HTTP/2) 协议的支持,请参阅上期文档</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_3.png" alt="3"></p> |
| <h4 id="多协议能解决的问题">多协议能解决的问题</h4> |
| <ul> |
| <li> |
| <p>将 RPC 框架无缝地接入 Dubbo 的服务治理体系。</p> |
| <p>通过协议扩展将 RPC 协议纳入 Dubbo 服务开发体系,从而复用 Dubbo 的编程模型和服务发现、流量管控等能力。比如 gRPC,其服务治理体系相对比较弱、编程 API 不够友好,很难直接用于微服务开发。</p> |
| </li> |
| <li> |
| <p>满足不同场景的调用需求。</p> |
| <p>各个服务可能是为了满足不同业务需求而开发,同时外围消费端应用的技术栈也可能多种多样,通过启用不同的通信协议,可以最优化不同场景的通信需求。</p> |
| </li> |
| <li> |
| <p>实现协议间的迁移。</p> |
| <p>通过支持多种协议,借助注册中心的协调,可以快速满足公司内协议迁移的需求。如从自有协议升级到 Dubbo 协议,Dubbo 协议自身升级,从 Dubbo 协议迁移到 gRPC,从 REST 迁移到 Dubbo 协议等。</p> |
| </li> |
| </ul> |
| <h3 id="多注册中心">多注册中心</h3> |
| <p>当服务集群规模小的时候,一个中心化的集群部署方案能很好的解决我们的业务问题。但是随着应用规模的增长、用户流量的增加,我们就不得不考虑要为业务系统引入跨区域、多集群的部署方案,而此时同业务系统密切相关的注册中心集群也面临部署方案的选型:</p> |
| <ol> |
| <li> |
| <p>继续维持全局共享的注册中心集群。这种架构方案的优点是简单;缺点是注册中心集群由于要保存全量的地址数据,存储和推送压力会变得很大,另外对于一些注册中心产品(如 Zookeeper 等)在跨集群网络部署的场景下稳定性和性能可能都会面临挑战。</p> |
| </li> |
| <li> |
| <p>每个业务集群部署独立的注册中心集群。多注册中心集群的优点是能解决跨集群网络可用性的问题,同时也能够减轻注册中心的存储和推送压力;缺点则是要求服务框架(如 Dubbo 等)能有同时发布/监听多个注册中心集群的能力。</p> |
| </li> |
| </ol> |
| <p>下面我们具体看一下,Dubbo 为多注册中心集群场景提供的解决方案。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_4.png" alt="4"></p> |
| <p>上图有两个业务集群,分别部署在北京和上海,每个业务集群有自己独立的注册中心集群,要解决两个业务集群间服务的透明 RPC 通信问题。</p> |
| <ol> |
| <li>服务提供端,双注册中心发布</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> id=<span style="color:#2aa198">&#34;beijingRegistry&#34;</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address1}&#34;</span> default=<span style="color:#2aa198">&#34;false&#34;</span><span style="color:#268bd2">/&gt;</span> <span style="color:#268bd2">&lt;dubbo:registry</span> id=<span style="color:#2aa198">&#34;shanghaiRegistry&#34;</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address2}&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.multi.registry.api.HelloService&#34;</span> ref=<span style="color:#2aa198">&#34;helloService&#34;</span> registry=<span style="color:#2aa198">&#34;shanghaiRegistry,beijingRegistry&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.multi.registry.api.DemoService&#34;</span> ref=<span style="color:#2aa198">&#34;demoService&#34;</span> registry=<span style="color:#2aa198">&#34;shanghaiRegistry,beijingRegistry&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><ol start="2"> |
| <li>服务消费端,根据消费需求做单/双注册中心订阅</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> id=<span style="color:#2aa198">&#34;beijingRegistry&#34;</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address1}&#34;</span> default=<span style="color:#2aa198">&#34;false&#34;</span> preferred=<span style="color:#2aa198">&#34;true&#34;</span> weight=<span style="color:#2aa198">&#34;100&#34;</span><span style="color:#268bd2">/&gt;</span> <span style="color:#268bd2">&lt;dubbo:registry</span> id=<span style="color:#2aa198">&#34;shanghaiRegistry&#34;</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address2}&#34;</span> default=<span style="color:#2aa198">&#34;true&#34;</span> weight=<span style="color:#2aa198">&#34;20&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.multi.registry.api.DemoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.multi.registry.api.DemoService&#34;</span> registry=<span style="color:#2aa198">&#34;beijingRegistry, shanghaiRegistry&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.multi.registry.api.HelloService&#34;</span> registry=<span style="color:#2aa198">&#34;beijingRegistry&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.multi.registry.api.HelloService&#34;</span> registry=<span style="color:#2aa198">&#34;shanghaiRegistry,shanghaiRegistry&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><h4 id="dubbo-对异构注册中心集群的支持">Dubbo 对异构注册中心集群的支持</h4> |
| <p>虽然我们会做多注册中心集群部署,但通常情况下,我们部署的都是相同的注册中心产品,如都是 Zookeeper、Nacos;而对于注册中心迁移的场景,则要求 Dubbo 能提供对更多的注册中心产品的支持,或者最重要的要有很好的扩展能力。Dubbo 官方目前支持的注册中心实现有:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_5.png" alt="5"></p> |
| <p>这里需要特别提到的一点是,当前 Dubbo 的服务注册/发现模型是以接口为粒度的,而从 2.7.5 版本开始,Dubbo 新引入了应用粒度的服务注册/发现模型。这一方面有助于优化 Dubbo 当前服务发现机制、提升服务容量,另一方面对于联通以 SpringCloud 为代表的微服务体系也非常重要(关于这点在下一章中有进一步提及)。更多关于《应用粒度服务发现:服务自省》的介绍,我们将在接下来的文章或文档中予以补充,请持续关注。</p> |
| <h4 id="多订阅带来的流量调度问题">多订阅带来的流量调度问题</h4> |
| <p>在引入多注册中心集群后,Dubbo 在流量选址时的多了一层注册中心集群间的负载均衡:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_6.png" alt="6"></p> |
| <p>在 Cluster Invoker 这一级,我们支持的选址策略有(2.7.5+ 版本,具体使用请参见文档):</p> |
| <ul> |
| <li> |
| <p>指定优先级</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 来自 preferred=“true” 注册中心的地址将被优先选择,只有该中心无可用地址时才 Fallback 到其他注册中心 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address1}&#34;</span> preferred=<span style="color:#2aa198">&#34;true&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div></li> |
| <li> |
| <p>同 zone 优先</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 选址时会和流量中的 zone key 做匹配,流量会优先派发到相同 zone 的地址 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address1}&#34;</span> zone=<span style="color:#2aa198">&#34;beijing&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div></li> |
| <li> |
| <p>权重轮询</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 来自北京和上海集群的地址,将以 10:1 的比例来分配流量 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> id=<span style="color:#2aa198">&#34;beijing&#34;</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address1}&#34;</span> weight=<span style="color:#2aa198">”100“</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> id=<span style="color:#2aa198">&#34;shanghai&#34;</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address2}&#34;</span> weight=<span style="color:#2aa198">”10“</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div></li> |
| <li> |
| <p>默认,stick to 任意可用</p> |
| </li> |
| </ul> |
| <h4 id="多注册中心适用的场景">多注册中心适用的场景</h4> |
| <ul> |
| <li> |
| <p>同区域流量优先调度</p> |
| <p>出于容灾或者服务伸缩性需求,服务/应用往往需要部署在多个独立的机房/区域,在每个区域有独立注册中心集群的场景下,实现同区域的流量优先调度就能很好的解决延迟和可用性问题。</p> |
| </li> |
| <li> |
| <p>注册中心迁移</p> |
| <p>公司的服务一直以来可能是存储在某一个注册中心,如 Zookeeper,但到了某个时间节点,因为各种各样的原因,当我们要迁移到另外的注册中心时,多注册中心模型能够保证平滑的迁移。</p> |
| </li> |
| <li> |
| <p>异构系统互通</p> |
| <p>不同微服务体系开发的服务,都封闭在各自的服务发现体系中,而通过统一的多注册中心模型,可以实现不同体系的服务互相发现。</p> |
| </li> |
| </ul> |
| <h2 id="借助-dubbo-联通异构的微服务体系">借助 Dubbo 联通异构的微服务体系</h2> |
| <p>上文我们提到了在组织内存在异构微服务体系的各种合理可能性,现在我们来具体看一下异构微服务体系的实际场景,以及使用 Dubbo 实现互联互通的解决方法。首先我们先通过一张图来看一下,联通异构的微服务体系具体是一个什么样的场景。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_7.png" alt="7"></p> |
| <p>如上图所示,我们有部分微服务可以是基于 SpringCloud、gRPC、K8S 或者是自建体系构建的,他们各自之间默认是相互隔离无法联通的。当我们再构建一套基于 Dubbo 的微服务体系时,则利用 Dubbo 的多协议、多服务发现模型,我们就可以做到和各个微服务体系间的两两之间的互联互通。进一步的,如图中橙色箭头所示,依赖 Dubbo 体系作为桥接层,我们还可以实现两个异构微服务体系间的打通。</p> |
| <p>对于以下几个示例场景,由于在地址发现层面目前没有统一的标准,我们暂且假设地址发现层面不同的体系建是没有障碍的,我们将重点关注迁移的基本流程以及通信协议环节。(关于地址发现部分,我们将在后续《服务自省:基于应用粒度的服务发现》之后再深入探讨)</p> |
| <h3 id="dubbo-体系内的协议迁移共存">Dubbo 体系内的协议迁移(共存)</h3> |
| <p>绝大多数开发者对 Dubbo 有这么一个固有认知:使用 Dubbo 开发微服务系统,则就要用 Dubbo 协议来作为服务间的通信协议才是最优方案。实际上,我们完全没有必要只束缚在 Dubbo RPC 协议上。Dubbo 作为微服务开发框架和 Dubbo 作为 RPC 协议这是两个概念,其实是完全可以分开来看待的,比如我们用 Dubbo 框架开发的业务系统,选用 rest、gRPC 通信是完全没有问题的(参加 Dubbo 支持的协议列表),具体用什么协议根据业务特点和技术规划才是最适合的。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_8.png" alt="8"></p> |
| <p>当前在云原生、Mesh 的大背景下, HTTP1/2、gRPC 协议开始受到越来越多的关注,一方面原因自然是因为它们在标准化方面做的更好,得到的更多的网络设备和基础设施的支持,具备更好的通用性和穿透性。对于很多有云原生迁移意愿的企业来说,往此类协议迁移无疑将对之后的架构升级有更多的帮助。</p> |
| <p>下图演示了在 Dubbo 体系内,从 Dubbo 协议向 gRPC 协议迁移的一个中间状态。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_9.png" alt="9"></p> |
| <ul> |
| <li>最左边的代表尚未迁移的老应用,这类应用在迁移过程中仍然要消费和提供 Dubbo 协议的服务。</li> |
| <li>中间的代表处于迁移中的应用,他们中间可能有些是服务提供者,既要为左边的老系统提供提供 Dubbo 协议服务;又要为右边的新系统提供 gRPC 服务;因此他们都是双协议暴露服务。</li> |
| <li>最右边则代表是新开发的或者已经迁移完成的应用,这个体系内已能完全用 gRPC 协议通信。</li> |
| <li>最终度过中间态后,我们期望所有的应用都达到最左边应用的状态,实现完全的 gRPC 协议通信。</li> |
| </ul> |
| <h3 id="spring-cloud-体系迁移到-dubbo-体系共存">Spring Cloud 体系迁移到 Dubbo 体系(共存)</h3> |
| <p>如前文所述,由于 SpringCloud 和 Dubbo 间服务发现模型的问题,要两个体系间的地址互通需要 Dubbo 侧作相应的适配,关于这部分内容将在接下来的 2.7.5 版本《服务自省》部分发布,在此我们暂且认为已经打通。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_10.png" alt="10"></p> |
| <p>Dubbo 体系内的部分应用作为透明的联通两个体系的关键节点,部分服务提供者应用要双协议发布、部分消费者应用要做到选定协议消费。由于老的 Spring Cloud 体系不允许做任何改动,因此联通两套体系的关键是 REST 协议,对 Dubbo 侧的应用来说:</p> |
| <ul> |
| <li>部分应用可能要以 REST 协议消费 SpringCloud 的服务;</li> |
| <li>部分应用可能要暴露 REST 协议共 SpringCloud 消费;</li> |
| <li>Dubbo 自有体系内则通过自己选定的协议通信,这里就比较灵活了,可以是 Dubbo、REST、gRPC 等其中的任一种。而如果选定 REST 协议则对于与 SpringCloud 体系的联通就变得更加自然了,因为两端的协议都是统一的。</li> |
| </ul> |
| <p>对于消费 Spring Cloud 服务的应用,要配置服务 :</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface =<span style="color:#2aa198">&#34;xxx.SpringService&#34;</span> protocol=<span style="color:#2aa198">&#34;rest&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>对于提供服务给 Spring Cloud 侧消费的应用,则指定服务暴露为 rest 协议,或者双协议暴露(因如果这个服务还要被新体系内的应用调用到):</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;xxx.NewService&#34;</span> protocol=<span style="color:#2aa198">&#34;rest,dubbo&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>作为 Dubbo 的维护者,虽然我们这里有明显的偏向性,讲的是从如何从 SpringCloud 体系迁移到 Dubbo 体系。但是反过来考虑,如果你已经或者即将选型 Dubbo 来开发微服务,则未来从 Dubbo 迁移到 SpringCloud 也是同样的思路,Dubbo 的多协议、多注册模型为双向迁移都提供了同样的灵活性。</p> |
| <h3 id="自建体系迁移到-dubbo-体系共存">自建体系迁移到 Dubbo 体系(共存)</h3> |
| <p>这个场景和上一节中讲到的的 SpringCloud 迁移有些类似,最大的区别在于 rest 协议是 Dubbo 官方默认提供支持的,而对于已有的微服务体系内的私有通信协议,则需要先要自己去扩展 Dubbo Protocol 来提供协议层面的支持。</p> |
| <h2 id="总结与展望">总结与展望</h2> |
| <p>要实现异构微服务体系间的共存或迁移,关键点在打通异构体系间的<code>协议</code>与<code>服务发现</code>,得益于 Dubbo 自身对多协议、多注册模型的支持,我们可以很容易的使 Dubbo 成为桥接异构微服务体系的中间层。熟悉 Dubbo 多协议实现细节的同学,可能会担心在服务数量较多的场景下,多协议注册会导致地址数量翻倍从而影响地址推送性能;另外在文中《借助 Dubbo 联通异构的微服务体系》一节,关于如何实现异构体系间的透明服务发现部分我们没有做详细的说明。关于涉及服务发现的这部分,我们将在接下来的文章中做具体阐述,看看 Dubbo 2.7.5 版本引入新的服务发现机制是如何解决这个问题的,请持续关注后续文章及 Dubbo 官方文档。</p></description></item><item><title>Blog: Dubbo 3 之 Triple 流控反压原理解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/12/28/dubbo-3-%E4%B9%8B-triple-%E6%B5%81%E6%8E%A7%E5%8F%8D%E5%8E%8B%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</link><pubDate>Wed, 28 Dec 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/12/28/dubbo-3-%E4%B9%8B-triple-%E6%B5%81%E6%8E%A7%E5%8F%8D%E5%8E%8B%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</guid><description> |
| <p>Triple 是 Dubbo 3 提出的基于 HTTP2 的开放协议, |
| 旨在解决 Dubbo 2 私有协议带来的互通性问题。 |
| Triple 基于 HTTP/2 定制自己的流控,支持通过特定的异常通知客户端业务层服务端负载高情况, |
| 保护了服务端被大流量击垮,提高系统高可用能力。</p> |
| <h2 id="一流控反压现状">一、流控反压现状</h2> |
| <p>客户端和服务器端在接收数据的时候有一个缓冲区来临时存储数据, |
| 但是缓冲区的大小是有限制的,所以有可能会出现缓冲区溢出的情况, |
| HTTP 通过流控保护数据溢出丢失风险。</p> |
| <h3 id="1http1-流控">1、HTTP/1 流控</h3> |
| <p>在 HTTP/1.1 中,流量的控制依赖的是底层TCP协议,在客户端和服务器端建立连接的时候, |
| 会使用系统默认的设置来建立缓冲区。在数据进行通信的时候,会告诉对方它的接收窗口的大小, |
| 这个接收窗口就是缓冲区中剩余的可用空间。如果接收窗口大小为零,则说明接收方缓冲区已满, |
| 则发送方将不再发送数据,直到客户端清除其内部缓冲区,然后请求恢复数据传输。</p> |
| <h3 id="2http2-流控">2、HTTP/2 流控</h3> |
| <p>HTTP/2 使用了多路复用机制,一个TCP连接可以有多个 HTTP/2 连接, |
| 故在 HTTP/2 中,有更加精细的流控制机制,允许服务端实现自己数据流和连接级的流控制。 |
| 服务端与客户端第一次连接时,会通过发送 <code>HTTP/2 SettingsFrame</code>设置初始化的流控窗口大小, |
| 用于 <code>Stream</code> 级别流控,默认为 65,535 字节。 |
| 定好流控窗口后,每次客户端发送数据就会减少流控窗口的大小, |
| 服务端收到数据后会发送窗口更新包(<code>WINDOW_UPDATE frame</code>)通知客户端更新窗口。 |
| 客户端收到窗口更新包后就会增加对应值的流控窗口,从而达到动态控制的目的。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/1.png" alt="1.png"></p> |
| <h2 id="二triple流控反压">二、Triple流控反压</h2> |
| <p>Netty 基于 HTTP/2 实现了基础的流控,当服务端负载过高,客户端发送窗口为 0 时, |
| 新增请求就无法被发送出去,会在缓存到客户端待发送请求队列中,缓存数据过大, |
| 就会造成客户端内存溢出,影响业务程序。</p> |
| <p>Triple 基于 Netty 实现了 HTTP/2 协议,通过 <code>HTTP/2 FlowController</code>接口统一封装, |
| 在实现分为进站(inbound)和出站(outbound)两个维度的实现。 |
| Triple 在 inbound 流量上使用了 Netty 的默认流控实现, |
| 在 outbound 上实现了自己流控,基于服务端负载, |
| 将服务端流量压力透传到客户端业务层,实现客户端的业务反压,暂停业务继续发送请求, |
| 保护服务端不被大流量击垮。</p> |
| <h3 id="1连接初始化">1、连接初始化</h3> |
| <p>Triple在初次建立连接时,通过 <code>TripleHttpProtocol</code> 初始化 HTTP/2 配置, |
| 默认流控窗口 <code>DEFAULT_WINDOW_INIT_SIZE = MIB_8</code>, |
| 并在服务端和客户端加入自己的 outbound 流控接口。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/2.png" alt="2.png"></p> |
| <h3 id="2inbound流控">2、Inbound流控</h3> |
| <p>Inbound 流量会通过 <code>DefaultHttpLocalFlowController</code> 的 <code>consumeBytes</code> 方法实现流控窗口更新与发送。</p> |
| <h4 id="1-入口传入http-流与更新数据大小">1) 入口传入HTTP 流与更新数据大小</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/3.png" alt="3.png"></p> |
| <h4 id="2-找到对应连接实现数据消费">2) 找到对应连接实现数据消费</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/4.png" alt="4.png"></p> |
| <h4 id="3-更新流控窗口">3) 更新流控窗口</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/5.png" alt="5.png"></p> |
| <h4 id="4-发送流控更新数据包window_update">4) 发送流控更新数据包(window_update)</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/6.png" alt="6.png"></p> |
| <h3 id="3outbound流控">3、Outbound流控</h3> |
| <p>Outbound 通过 Triple 自己的流控实现 <code>TriHttpRemoteFlowController</code>, |
| 将服务端压力反馈到业务层,保护服务端被大流量击垮。</p> |
| <h4 id="1-发送数据时判断是否还有窗口">1) 发送数据时判断是否还有窗口</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/7.png" alt="7.png"></p> |
| <h4 id="2-窗口为0时抛出特定异常">2) 窗口为0时抛出特定异常</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/8.png" alt="8.png"></p> |
| <h4 id="3-反馈客户端流控异常">3) 反馈客户端流控异常</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/9.png" alt="9.png"></p> |
| <h3 id="4总结">4、总结</h3> |
| <p>Triple 通过将底层客户端发送窗口为 0 场景封装为特定流控异常, |
| 透传至客户端上层业务,阻止客户端业务继续数据发送, |
| 有效的保护了服务端被大流量击垮和客户端的内存溢出的问题。</p> |
| <h2 id="三未来展望">三、未来展望</h2> |
| <p>目前 Triple 已经基本实现了流控反压能力,未来我们将深度联动业务, |
| 基于业务负载自适应调整反压流控, |
| 一是在 inbound 上将流控窗口包发送时机调整到服务端业务处理完成后, |
| 二是在 outbound 流量上关联客户端业务层,动态调整客户端发送速率。 |
| 从而实现基于服务端业务负载动态反压流控机制。</p></description></item><item><title>Blog: Triple 协议支持 Java 异常回传的设计与实现</title><link>https://dubbo.apache.org/zh-cn/blog/2022/12/19/triple-%E5%8D%8F%E8%AE%AE%E6%94%AF%E6%8C%81-java-%E5%BC%82%E5%B8%B8%E5%9B%9E%E4%BC%A0%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</link><pubDate>Mon, 19 Dec 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/12/19/triple-%E5%8D%8F%E8%AE%AE%E6%94%AF%E6%8C%81-java-%E5%BC%82%E5%B8%B8%E5%9B%9E%E4%BC%A0%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</guid><description> |
| <h2 id="背景">背景</h2> |
| <p>在一些业务场景, 往往需要自定义异常来满足特定的业务, 主流用法是在catch里抛出异常, 例如:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">deal</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span><span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//doSomething |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span><span style="color:#719e07">(</span>IGreeterException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>或者通过ExceptionBuilder,把相关的异常对象返回给consumer:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>provider<span style="color:#719e07">.</span>send<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ExceptionBuilders<span style="color:#719e07">.</span>IGreeterExceptionBuilder<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setDescription<span style="color:#719e07">(</span>&#39;异常描述信息&#39;<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>在抛出异常后, 通过捕获和instanceof来判断特定的异常, 然后做相应的业务处理,例如:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> greeterProxy<span style="color:#719e07">.</span>echo<span style="color:#719e07">(</span>REQUEST_MSG<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>IGreeterException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//做相应的处理 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在 Dubbo 2.x 版本,可以通过上述方法来捕获 Provider 端的异常。 |
| 而随着云原生时代的到来, Dubbo 也开启了 3.0 的里程碑。</p> |
| <p>Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生, |
| 在 3.0 的许多特性中,很重要的一个改动就是支持<strong>新的一代Rpc协议Triple</strong>。</p> |
| <p>Triple 协议基于 HTTP 2.0 进行构建,对网关的穿透性强,<strong>兼容 gRPC</strong>, |
| 提供 Request Response、Request Streaming、Response Streaming、 |
| Bi-directional Streaming 等通信模型; |
| 从 Triple 协议开始,Dubbo 还支持基于 IDL 的服务定义。</p> |
| <p>采用 Triple 协议的用户可以在 provider 端生成用户定义的异常信息, |
| 记录异常产生的堆栈,triple 协议可保证将用户在客户端获取到异常的message。</p> |
| <p>Triple 的回传异常会在 <code>AbstractInvoker</code> 的 <code>waitForResultIfSync</code> |
| 中把异常信息堆栈统一封装成 <code>RpcException</code>, |
| 所有来自 Provider 端的异常都会被封装成 <code>RpcException</code> 类型并抛出, |
| 这会导致用户无法根据特定的异常类型捕获来自 Provider 的异常, |
| 只能通过捕获 RpcException 异常来返回信息, |
| 且 Provider 携带的异常 message 也无法回传,只能获取打印的堆栈信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> greeterProxy<span style="color:#719e07">.</span>echo<span style="color:#719e07">(</span>REQUEST_MSG<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>RpcException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> e<span style="color:#719e07">.</span>printStackTrace<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>自定义异常信息在社区中的呼声也比较高, |
| 因此本次改动将支持自定义异常的功能, 使得服务端能抛出自定义异常后被客户端捕获到。</p> |
| <h2 id="dubbo异常处理简介">Dubbo异常处理简介</h2> |
| <p>我们从Consumer的角度看一下一次Triple协议 Unary请求的大致流程:</p> |
| <p>Dubbo Consumer 从 Spring 容器中获取 bean 时获取到的是一个代理接口, |
| 在调用接口的方法时会通过代理类远程调用接口并返回结果。</p> |
| <p>Dubbo提供的代理工厂类是 <code>ProxyFactory</code>,通过 SPI 机制默认实现的是 <code>JavassistProxyFactory</code>, |
| <code>JavassistProxyFactory</code> 创建了一个继承自 <code>AbstractProxyInvoker</code> 类的匿名对象, |
| 并重写了抽象方法 <code>doInvoke</code>。 |
| 重写后的 <code>doInvoke</code> 只是将调用请求转发给了 <code>Wrapper</code> 类的 <code>invokeMethod</code> 方法, |
| 并生成 <code>invokeMethod</code> 方法代码和其他一些方法代码。</p> |
| <p>代码生成完毕后,通过 <code>Javassist</code> 生成 <code>Class</code> 对象, |
| 最后再通过反射创建 <code>Wrapper</code> 实例,随后通过 <code>InvokerInvocationHandler</code> -&gt; <code>InvocationUtil</code> -&gt; <code>AbstractInvoker</code> -&gt; 具体实现类发送请求到Provider端。</p> |
| <p>Provider 进行相应的业务处理后返回相应的结果给 Consumer 端,来自 Provider 端的结果会被封装成 <code>AsyncResult</code> ,在 <code>AbstractInvoker</code> 的具体实现类里, |
| 接受到来自 Provider 的响应之后会调用 <code>appResponse</code> 到 <code>recreate</code> 方法,若 <code>appResponse</code> 里包含异常, |
| 则会抛出给用户,大体流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/1.jpeg" alt="1.jpeg"></p> |
| <p>上述的异常处理相关环节是在 Consumer 端,在 Provider 端则是由 <code>org.apache.dubbo.rpc.filter.ExceptionFilter</code> 进行处理, |
| 它是一系列责任链 Filter 中的一环,专门用来处理异常。</p> |
| <p>Dubbo 在 Provider 端的异常会在封装进 <code>appResponse</code> 中。下面的流程图揭示了 <code>ExceptionFilter</code> 源码的异常处理流程:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/2.jpeg" alt="2.jpeg"></p> |
| <p>而当 <code>appResponse</code> 回到了 Consumer 端,会在 <code>InvocationUtil</code> 里调用 <code>AppResponse</code> 的 <code>recreate</code> 方法抛出异常, |
| 最终可以在 Consumer 端捕获:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> Object <span style="color:#268bd2">recreate</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> Throwable <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>exception <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Object stackTrace <span style="color:#719e07">=</span> exception<span style="color:#719e07">.</span>getStackTrace<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>stackTrace <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exception<span style="color:#719e07">.</span>setStackTrace<span style="color:#719e07">(</span><span style="color:#719e07">new</span> StackTraceElement<span style="color:#719e07">[</span>0<span style="color:#719e07">]);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> exception<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="triple-通信原理">Triple 通信原理</h2> |
| <p>在上一节中,我们已经介绍了 Dubbo 在 Consumer 端大致发送数据的流程, |
| 可以看到最终依靠的是 <code>AbstractInvoker</code> 的实现类来发送数据。 |
| 在 Triple 协议中,<code>AbstractInvoker</code> 的具体实现类是 <code>TripleInvoker</code> , |
| <code>TripleInvoker</code> 在发送前会启动监听器,监听来自 Provider 端的响应结果, |
| 并调用 <code>ClientCallToObserverAdapter</code> 的 <code>onNext</code> 方法发送消息, |
| 最终会在底层封装成 Netty 请求发送数据。</p> |
| <p>在正式的请求发起前,TripleServer 会注册 <code>TripleHttp2FrameServerHandler</code>, |
| 它继承自 Netty 的 <code>ChannelDuplexHandler</code>, |
| 其作用是会在 <code>channelRead</code> 方法中不断读取 Header 和 Data 信息并解析, |
| 经过层层调用, |
| 会在 <code>AbstractServerCall</code> 的 <code>onMessage</code> 方法里把来自 consumer 的信息流进行反序列化, |
| 并最终由交由 <code>ServerCallToObserverAdapter</code> 的 <code>invoke</code> 方法进行处理。</p> |
| <p>在 <code>invoke</code> 方法中,根据 consumer 请求的数据调用服务端相应的方法,并异步等待结果;' |
| 若服务端抛出异常,则调用 <code>onError</code> 方法进行处理, |
| 否则,调用 <code>onReturn</code> 方法返回正常的结果,大致代码逻辑如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">invoke</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用invoke方法请求服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Result response <span style="color:#719e07">=</span> invoker<span style="color:#719e07">.</span>invoke<span style="color:#719e07">(</span>invocation<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//异步等待结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> response<span style="color:#719e07">.</span>whenCompleteWithContext<span style="color:#719e07">((</span>r<span style="color:#719e07">,</span> t<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//若异常不为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>t <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用方法过程出现异常,调用onError方法处理 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> responseObserver<span style="color:#719e07">.</span>onError<span style="color:#719e07">(</span>t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>response<span style="color:#719e07">.</span>hasException<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用onReturn方法处理业务异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onReturn<span style="color:#719e07">(</span>response<span style="color:#719e07">.</span>getException<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//正常返回结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onReturn<span style="color:#719e07">(</span>r<span style="color:#719e07">.</span>getValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>大体流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/3.jpeg" alt="3.jpeg"></p> |
| <h2 id="实现版本">实现版本</h2> |
| <p>了解了上述原理,我们就可以进行相应的改造了, |
| 能让 consumer 端捕获异常的<strong>关键在于把异常对象以及异常信息序列化后再发送给consumer端</strong>。 |
| 常见的序列化协议很多,例如 Dubbo/HSF 默认的 hessian2 序列化; |
| 还有使用广泛的 JSON 序列化;以及 gRPC 原生支持的 protobuf(PB) 序列化等等。 |
| Triple协议因为兼容grpc的原因,默认采用 Protobuf 进行序列化。 |
| 上述提到的这三种典型的序列化方案作用类似,但在实现和开发中略有不同。 |
| PB 不可由序列化后的字节流直接生成内存对象, |
| 而 Hessian 和 JSON 都是可以的。后两者反序列化的过程不依赖“二方包”, |
| 其序列化和反序列化的代码由 proto 文件相同,只要客户端和服务端用相同的 proto 文件进行通信, |
| 就可以构造出通信双方可解析的结构。</p> |
| <p>单一的 protobuf 无法序列化异常信息, |
| 因此我们采用 <code>Wrapper + PB</code> 的形式进行序列化异常信息, |
| 抽象出一个 <code>TripleExceptionWrapperUtils</code> 用于序列化异常, |
| 并在 <code>trailer</code> 中采用 <code>TripleExceptionWrapperUtils</code> 序列化异常,大致代码流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/4.jpeg" alt="4.jpeg"></p> |
| <p>上面的实现方案看似非常合理,已经能把 Provider 端的异常对象和信息回传, |
| 并在 Consumer 端进行捕获。但仔细想想还是有问题的: |
| 通常在 HTTP2 为基础的通信协议里会对 header 大小做一定的限制, |
| 太大的header size 会导致性能退化严重,为了保证性能, |
| 往往以 HTTP2 为基础的协议在建立连接的时候是要协商最大 header size 的, |
| 超过后会发送失败。对于 Triple 协议来说,在设计之初就是基于 HTTP 2.0, |
| 能无缝兼容 Grpc,而 Grpc header 头部只有 8KB 大小, |
| 异常对象大小可能超过限制,从而丢失异常信息; |
| 且多一个 header 携带序列化的异常信息意味着用户能加的 header 数量会减少, |
| 挤占了其他 header 所能占用的空间。</p> |
| <p>经过讨论,考虑将异常信息放置在 Body,将序列化后的异常从 trailer 挪至 body, |
| 采用 <code>TripleWrapper + protobuf</code> 进行序列化,把相关的异常信息序列化后回传。 |
| 社区围绕这个问题进行了一系列的争论,读者也可尝试先思考一下:</p> |
| <p>1.在 body 中携带回传的异常信息,其对应HTTP header状态码该设置为多少?</p> |
| <p>2.基于 http2 构建的协议,按照主流的 grpc 实现方案,相关的错误信息放在 <code>trailer</code>,理论上不存在body,上层协议也需要保持语义一致性,若此时在payload回传异常对象,且grpc并没有支持在Body回传序列化对象的功能, 会不会破坏Http和grpc协议的语义?从这个角度出发,异常信息更应该放在trailer里。</p> |
| <p>3.作为开源社区,不能一味满足用户的需求,非标准化的用法注定是会被淘汰的,应该尽量避免更改 Protobuf的语义,是否在Wrapper层去支持序列化异常就能满足需求?</p> |
| <p>首先回答第二、三个问题:HTTP 协议并没有约定在状态码非 2xx 的时候不能返回 body,返回之后是否读取取决于用户。grpc 采用protobuf进行序列化,所以无法返回 exception;且try catch机制为java独有,其他语言并没有对应的需求,但Grpc暂时不支持的功能并一定是unimplemented,Dubbo的设计目标之一是希望能和主流协议甚至架构进行对齐,但对于用户合理的需求也希望能进行一定程度的修改。且从throw本身的语义出发,throw 的数据不只是一个 error message,序列化的异常信息带有业务属性,根据这个角度,更不应该采用类似trailer的设计。至于单一的Wrapper层,也没办法和grpc进行互通。至于Http header状态码设置为200,因为其返回的异常信息已经带有一定的业务属性,不再是单纯的error,这个设计也与grpc保持一致,未来考虑网关采集可以增加新的triple-status。</p> |
| <p>更改后的版本只需在异常不为空时返回相关的异常信息,采用 <code>TripleWrapper + Protobuf</code> 进行序列化异常信息,并在consumer端进行解析和反序列化,大体流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/5.jpeg" alt="5.jpeg"></p> |
| <h2 id="总结">总结</h2> |
| <p>通过对 Dubbo 3.0 新增自定义异常的版本迭代中可以看出,尽管只能新增一个小小的特性,流程下并不复杂,但由于要考虑互通、兼容和协议的设计理念,因此思考和讨论的时间可能比写代码的时间更多。</p></description></item><item><title>Blog: Proxyless Mesh在Dubbo中的实践</title><link>https://dubbo.apache.org/zh-cn/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</link><pubDate>Mon, 05 Sep 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</guid><description> |
| <h2 id="背景">背景</h2> |
| <p>随着 Dubbo 3.1 的 release,Dubbo 在云原生的路上又迈出了重要的一步。在这个版本中添加了 Proxyless Mesh 的新特性,Dubbo Proxyless Mesh 直接实现 xDS 协议解析, |
| 实现 Dubbo 与 Control Plane 的直接通信,进而实现控制面对流量管控、服务治理、可观测性、安全等的统一管控,规避 Sidecar 模式带来的性能损耗与部署架构复杂性。</p> |
| <h2 id="什么是service-mesh">什么是Service Mesh</h2> |
| <p>Service Mesh 又译作 “服务网格”,作为服务间通信的基础设施层。Buoyant 公司的 CEO Willian Morgan 在他的这篇文章 <a href="https://linkerd.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/">WHAT’S A Service Mesh? AND WHY DO I NEED ONE? </a> |
| 中解释了什么是 Service Mesh,为什么云原生应用需要 Service Mesh。</p> |
| <p><strong>下面是 Willian Morgan 对 Service Mesh 的解释。</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>A Service Mesh is a dedicated infrastructure layer for handling service-to-service communication. |
| </span></span><span style="display:flex;"><span>It’s responsible for the reliable delivery of requests through the complex topology of services |
| </span></span><span style="display:flex;"><span>that comprise a modern, cloud native application. In practice, the Service Mesh is typically implemented |
| </span></span><span style="display:flex;"><span>as an array of lightweight network proxies that are deployed alongside application code, without the |
| </span></span><span style="display:flex;"><span>application needing to be aware. |
| </span></span></code></pre></div><p><strong>翻译成中文</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>服务网格(Service Mesh)是处理服务间通信的基础设施层。它负责构成现代云原生应用程序的复杂服务拓扑来可靠地交付请求。 |
| </span></span><span style="display:flex;"><span>在实践中,Service Mesh 通常以轻量级网络代理阵列的形式实现,这些代理与应用程序代码部署在一起,对应用程序来说无需感知代理的存在。 |
| </span></span></code></pre></div><p>说到 Service Mesh 一定离不开 Sidecar 经典架构模式。它通过在业务 Pod 中注入 Sidecar 容器,接管业务容器的通信流量,同时 Sidecar 容器与网格平台的控制平面对接, |
| 基于控制平面下发的策略,对代理流量实施治理和管控,将原有服务框架的治理能力下层到 Sidecar 容器中,从而实现了基础框架能力的下沉,与业务系统解耦。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/1.png" alt="Service Mesh"></p> |
| <p>经典的 Sidecar Mesh 部署架构有很多优势,如平滑升级、多语言、业务侵入小等,但也带来了一些额外的问题,比如:</p> |
| <ul> |
| <li>Proxy 带来的性能损耗,在复杂拓扑的网络调用中将变得尤其明显</li> |
| <li>流量拦截带来的架构复杂性</li> |
| <li>Sidecar 生命周期管理</li> |
| <li>部署环境受限,并不是所有环境都满足 Sidecar 流量拦截条件</li> |
| </ul> |
| <p>针对 Sidecar Mesh 模型的问题,Dubbo 社区自很早之前就做了 Dubbo 直接对接到控制面的设想与思考,并在国内开源社区率先提出了 Proxyless Mesh 的概念,当然就 Proxyless 概念的说法而言,最开始是谷歌提出来的。</p> |
| <h2 id="dubbo-proxyless-mesh">Dubbo Proxyless Mesh</h2> |
| <p>Dubbo Proxyless 模式是指 Dubbo 直接与 Istiod通信,通过 xDS协议实现服务发现和服务治理等能力。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/2.png" alt="Proxyless"></p> |
| <p>Proxyless 模式使得微服务又回到了 2.x 时代的部署架构,同 Dubbo 经典服务治理模式非常相似,所以说这个模式并不新鲜, Dubbo 从最开始就是这样的设计模式。 |
| 这样做可以极大的提升应用性能,降低网络延迟。有人说这种做法又回到了原始的基于 SDK 的微服务模式,其实非也,它依然使用了 Envoy 的 xDS API, |
| 但是因为不再需要向应用程序中注入 Sidecar 代理,因此可以减少应用程序性能的损耗。</p> |
| <p>但相比于 Mesh 架构,Dubbo 经典服务治理模式并没有强调控制面的统一管控,而这点恰好是 Service Mesh 所强调的,强调对流量、可观测性、证书等的标准化管控与治理,也是 Mesh 理念先进的地方。</p> |
| <p>在 Dubbo Proxyless 架构模式下,Dubbo 进程将直接与控制面通信,Dubbo 进程之间也继续保持直连通信模式,我们可以看出 Proxyless 架构的优势:</p> |
| <ul> |
| <li>没有额外的 Proxy 中转损耗,因此更适用于性能敏感应用</li> |
| <li>更有利于遗留系统的平滑迁移</li> |
| <li>架构简单,容易运维部署</li> |
| <li>适用于几乎所有的部署环境</li> |
| </ul> |
| <h2 id="服务发现">服务发现</h2> |
| <p>xDS 接入以注册中心的模式对接,节点发现同其他注册中心的服务自省模型一样,对于 xDS 的负载均衡和路由配置通过 ServiceInstance 的动态运行时配置传出, |
| 在构建 Invoker 的时候将配置参数传入配置地址。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/3.png" alt="服务发现"></p> |
| <h2 id="证书管理">证书管理</h2> |
| <p>零信任架构下,需要严格区分工作负载的识别和信任,而签发 X.509 证书是推荐的一种认证方式。在 Kubernetes 集群中,服务间是通过 DNS 名称互相访问的,而网络流量可能被 DNS 欺骗、BGP/路由劫持、ARP 欺骗等手段劫持,为了将服务名称(DNS 名称)与服务身份强关联起来,Istio 使用置于 X.509 证书中的安全命名机制。SPIFFE是 Istio 所采用的安全命名的规范,它也是云原生定义的一种标准化的、可移植的工作负载身份规范。</p> |
| <p>Secure Production Identity Framework For Everyone (SPIFFE) 是一套服务之间相互进行身份识别的标准,主要包含以下内容:</p> |
| <ul> |
| <li>SPIFFE ID 标准,SPIFFE ID 是服务的唯一标识,具体实现使用 URI 资源标识符</li> |
| <li>SPIFFE Verifiable Identity Document (SVID) 标准,将 SPIFFE ID 编码到一个加密的可验证的数据格式中</li> |
| <li>颁发与撤销 SVID 的 API 标准(SVID 是 SPIFFE ID 的识别凭证)</li> |
| </ul> |
| <p>SPIFFE ID 规定了形如 <code>spiffe://&lt;trust domain&gt;/&lt;workload identifier&gt;</code> 的 URI 格式,作为工作负载(Workload)的唯一标识。 |
| 而 Istio 在自身的生态中只使用到了 SPIFFE ID 作为安全命名,其数据格式由自己实现,通信格式采用 CNCF 支持的 xDS 协议规范(证书认证通信更具体来说是 xDS 的 SDS)。</p> |
| <p>Istio 使用形如 <code>spiffe://&lt;trust_domain&gt;/ns/&lt;namespace&gt;/sa/&lt;service_account&gt;</code> 格式的 SPIFFE ID 作为安全命名,注入到 X.509 证书的 subjectAltName 扩展中。 |
| 其中&quot;trust_domain&quot;参数通过 Istiod 环境变量TRUST_DOMAIN 注入,用于在多集群环境中交互。</p> |
| <p>以下是 Dubbo Proxyless Mesh 证书颁发的过程</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/4.png" alt="证书颁发"></p> |
| <ul> |
| <li>创建 RSA 私钥(Istio 还不支持 ECDSA 私钥)</li> |
| <li>构建 CSR(Certificate signing request)模板</li> |
| <li>自签名 CSR 生成证书</li> |
| <li>创建 Kubernetes Secret 资源储存 CA 证书和私钥(CA Service处理)</li> |
| </ul> |
| <h2 id="案例实践">案例实践</h2> |
| <p>接下来我将带领大家通过一个例子使已有的项目快速跑在 Proxyless Mesh 模式下。</p> |
| <h2 id="环境准备">环境准备</h2> |
| <h3 id="安装docker">安装docker</h3> |
| <p><a href="https://www.docker.com/">https://www.docker.com/</a></p> |
| <h3 id="安装minikube">安装minikube</h3> |
| <p>墙裂推荐:<a href="https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/">https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/</a></p> |
| <h3 id="安装istio">安装istio</h3> |
| <p><a href="https://istio.io/latest/docs/setup/getting-started/">https://istio.io/latest/docs/setup/getting-started/</a></p> |
| <p>❗❗❗ 安装 Istio 的时候需要开启 first-party-jwt 支持(使用 istioctl 工具安装的时候加上 &ndash;set values.global.jwtPolicy=first-party-jwt 参数),否则将导致客户端认证失败的问题。 |
| 参考命令如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -L https://istio.io/downloadIstio | sh - |
| </span></span><span style="display:flex;"><span><span style="color:#b58900">cd</span> istio-1.xx.x |
| </span></span><span style="display:flex;"><span><span style="color:#b58900">export</span> <span style="color:#268bd2">PATH</span><span style="color:#719e07">=</span><span style="color:#268bd2">$PWD</span>/bin:<span style="color:#268bd2">$PATH</span> |
| </span></span><span style="display:flex;"><span>istioctl install --set <span style="color:#268bd2">profile</span><span style="color:#719e07">=</span>demo --set values.global.jwtPolicy<span style="color:#719e07">=</span>first-party-jwt -y |
| </span></span></code></pre></div><h2 id="代码准备">代码准备</h2> |
| <h3 id="xds-provider">xds-provider</h3> |
| <h4 id="定义一个接口">定义一个接口</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">GreetingService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="实现对应的接口">实现对应的接口</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@DubboService</span><span style="color:#719e07">(</span>version <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;1.0.0&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">AnnotatedGreetingService</span> <span style="color:#268bd2">implements</span> GreetingService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;greeting service received: &#34;</span> <span style="color:#719e07">+</span> name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;hello, &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;! from host: &#34;</span> <span style="color:#719e07">+</span> NetUtils<span style="color:#719e07">.</span>getLocalHost<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="编写启动类">编写启动类</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ProviderBootstrap</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> AnnotationConfigApplicationContext context <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> AnnotationConfigApplicationContext<span style="color:#719e07">(</span>ProviderConfiguration<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> context<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo service started&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> CountDownLatch<span style="color:#719e07">(</span>1<span style="color:#719e07">).</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Configuration</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@EnableDubbo</span><span style="color:#719e07">(</span>scanBasePackages <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;org.apache.dubbo.samples.impl&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@PropertySource</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;classpath:/spring/dubbo-provider.properties&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ProviderConfiguration</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="编写配置信息">编写配置信息</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>dubbo.application.name=dubbo-samples-xds-provider |
| </span></span><span style="display:flex;"><span># 由于 Dubbo 3 应用级服务发现的元数据无法从 istio 中获取,需要走服务自省模式。 |
| </span></span><span style="display:flex;"><span># 这要求了 Dubbo MetadataService 的端口在全集群的是统一的。 |
| </span></span><span style="display:flex;"><span>dubbo.application.metadataServicePort=20885 |
| </span></span><span style="display:flex;"><span># 走xds协议 |
| </span></span><span style="display:flex;"><span>dubbo.registry.address=xds://istiod.istio-system.svc:15012 |
| </span></span><span style="display:flex;"><span>dubbo.protocol.name=tri |
| </span></span><span style="display:flex;"><span>dubbo.protocol.port=50051 |
| </span></span><span style="display:flex;"><span># 对齐k8s pod生命周期,由于 Kubernetes probe 探活机制的工作原理限制, |
| </span></span><span style="display:flex;"><span># 探活请求的发起方不是 localhost,所以需要配置 qosAcceptForeignIp 参数开启允许全局访问 |
| </span></span><span style="display:flex;"><span>dubbo.application.qosEnable=true |
| </span></span><span style="display:flex;"><span>dubbo.application.qosAcceptForeignIp=true |
| </span></span></code></pre></div><h4 id="编写deploymentyml和serviceyml">编写Deployment.yml和Service.yml</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>apiVersion: apps/v1 |
| </span></span><span style="display:flex;"><span>kind: Deployment |
| </span></span><span style="display:flex;"><span>metadata: |
| </span></span><span style="display:flex;"><span> name: dubbo-samples-xds-provider |
| </span></span><span style="display:flex;"><span> namespace: dubbo-demo |
| </span></span><span style="display:flex;"><span>spec: |
| </span></span><span style="display:flex;"><span> replicas: 3 |
| </span></span><span style="display:flex;"><span> selector: |
| </span></span><span style="display:flex;"><span> matchLabels: |
| </span></span><span style="display:flex;"><span> app: dubbo-samples-xds-provider |
| </span></span><span style="display:flex;"><span> template: |
| </span></span><span style="display:flex;"><span> metadata: |
| </span></span><span style="display:flex;"><span> labels: |
| </span></span><span style="display:flex;"><span> app: dubbo-samples-xds-provider |
| </span></span><span style="display:flex;"><span> spec: |
| </span></span><span style="display:flex;"><span> containers: |
| </span></span><span style="display:flex;"><span> - name: server |
| </span></span><span style="display:flex;"><span> image: apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1 |
| </span></span><span style="display:flex;"><span> livenessProbe: |
| </span></span><span style="display:flex;"><span> httpGet: |
| </span></span><span style="display:flex;"><span> path: /live |
| </span></span><span style="display:flex;"><span> port: 22222 |
| </span></span><span style="display:flex;"><span> initialDelaySeconds: 5 |
| </span></span><span style="display:flex;"><span> periodSeconds: 5 |
| </span></span><span style="display:flex;"><span> readinessProbe: |
| </span></span><span style="display:flex;"><span> httpGet: |
| </span></span><span style="display:flex;"><span> path: /ready |
| </span></span><span style="display:flex;"><span> port: 22222 |
| </span></span><span style="display:flex;"><span> initialDelaySeconds: 5 |
| </span></span><span style="display:flex;"><span> periodSeconds: 5 |
| </span></span><span style="display:flex;"><span> startupProbe: |
| </span></span><span style="display:flex;"><span> httpGet: |
| </span></span><span style="display:flex;"><span> path: /startup |
| </span></span><span style="display:flex;"><span> port: 22222 |
| </span></span><span style="display:flex;"><span> failureThreshold: 30 |
| </span></span><span style="display:flex;"><span> periodSeconds: 10 |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>apiVersion: v1 |
| </span></span><span style="display:flex;"><span>kind: Service |
| </span></span><span style="display:flex;"><span>metadata: |
| </span></span><span style="display:flex;"><span> name: dubbo-samples-xds-provider |
| </span></span><span style="display:flex;"><span> namespace: dubbo-demo |
| </span></span><span style="display:flex;"><span>spec: |
| </span></span><span style="display:flex;"><span> clusterIP: None |
| </span></span><span style="display:flex;"><span> selector: |
| </span></span><span style="display:flex;"><span> app: dubbo-samples-xds-provider |
| </span></span><span style="display:flex;"><span> ports: |
| </span></span><span style="display:flex;"><span> - name: grpc |
| </span></span><span style="display:flex;"><span> protocol: TCP |
| </span></span><span style="display:flex;"><span> port: 50051 |
| </span></span><span style="display:flex;"><span> targetPort: 50051 |
| </span></span></code></pre></div><h4 id="编写dockerfile">编写Dockerfile</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>FROM openjdk:8-jdk |
| </span></span><span style="display:flex;"><span>ADD ./target/dubbo-samples-xds-provider-1.0-SNAPSHOT.jar dubbo-samples-xds-provider-1.0-SNAPSHOT.jar |
| </span></span><span style="display:flex;"><span>CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-provider-1.0-SNAPSHOT.jar |
| </span></span></code></pre></div><h3 id="xds-consumer">xds-consumer</h3> |
| <h4 id="定义一个接口-1">定义一个接口</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>public interface GreetingService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String sayHello(String name); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h4 id="实现对应的接口-1">实现对应的接口</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>@Component(&#34;annotatedConsumer&#34;) |
| </span></span><span style="display:flex;"><span>public class GreetingServiceConsumer { |
| </span></span><span style="display:flex;"><span> // 这里特别注意的是、由于当前 Dubbo 版本受限于 istio 的通信模型无法获取接口所对应的应用名, |
| </span></span><span style="display:flex;"><span> // 因此需要配置 providedBy 参数来标记此服务来自哪个应用。 |
| </span></span><span style="display:flex;"><span> @DubboReference(version = &#34;1.0.0&#34;, providedBy = &#34;dubbo-samples-xds-provider&#34;) |
| </span></span><span style="display:flex;"><span> private GreetingService greetingService; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> public String doSayHello(String name) { |
| </span></span><span style="display:flex;"><span> return greetingService.sayHello(name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h4 id="编写启动类-1">编写启动类</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>public class ConsumerBootstrap { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> public static void main(String[] args) { |
| </span></span><span style="display:flex;"><span> AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); |
| </span></span><span style="display:flex;"><span> context.start(); |
| </span></span><span style="display:flex;"><span> GreetingServiceConsumer greetingServiceConsumer = context.getBean(GreetingServiceConsumer.class); |
| </span></span><span style="display:flex;"><span> while (true) { |
| </span></span><span style="display:flex;"><span> try { |
| </span></span><span style="display:flex;"><span> String hello = greetingServiceConsumer.doSayHello(&#34;xDS Consumer&#34;); |
| </span></span><span style="display:flex;"><span> System.out.println(&#34;result: &#34; + hello); |
| </span></span><span style="display:flex;"><span> Thread.sleep(100); |
| </span></span><span style="display:flex;"><span> } catch (Throwable t) { |
| </span></span><span style="display:flex;"><span> t.printStackTrace(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> @Configuration |
| </span></span><span style="display:flex;"><span> @EnableDubbo(scanBasePackages = &#34;org.apache.dubbo.samples.action&#34;) |
| </span></span><span style="display:flex;"><span> @PropertySource(&#34;classpath:/spring/dubbo-consumer.properties&#34;) |
| </span></span><span style="display:flex;"><span> @ComponentScan(value = {&#34;org.apache.dubbo.samples.action&#34;}) |
| </span></span><span style="display:flex;"><span> static class ConsumerConfiguration { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h4 id="编写配置信息-1">编写配置信息</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>dubbo.application.name=dubbo-samples-xds-consumer |
| </span></span><span style="display:flex;"><span>dubbo.application.metadataServicePort=20885 |
| </span></span><span style="display:flex;"><span>dubbo.registry.address=xds://istiod.istio-system.svc:15012 |
| </span></span><span style="display:flex;"><span>dubbo.consumer.timeout=3000 |
| </span></span><span style="display:flex;"><span>dubbo.consumer.check=false |
| </span></span><span style="display:flex;"><span>dubbo.application.qosEnable=true |
| </span></span><span style="display:flex;"><span>dubbo.application.qosAcceptForeignIp=true |
| </span></span></code></pre></div><h4 id="编写deploymentyml和serviceyml-1">编写Deployment.yml和Service.yml</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>apiVersion: apps/v1 |
| </span></span><span style="display:flex;"><span>kind: Deployment |
| </span></span><span style="display:flex;"><span>metadata: |
| </span></span><span style="display:flex;"><span> name: dubbo-samples-xds-consumer |
| </span></span><span style="display:flex;"><span> namespace: dubbo-demo |
| </span></span><span style="display:flex;"><span>spec: |
| </span></span><span style="display:flex;"><span> replicas: 2 |
| </span></span><span style="display:flex;"><span> selector: |
| </span></span><span style="display:flex;"><span> matchLabels: |
| </span></span><span style="display:flex;"><span> app: dubbo-samples-xds-consumer |
| </span></span><span style="display:flex;"><span> template: |
| </span></span><span style="display:flex;"><span> metadata: |
| </span></span><span style="display:flex;"><span> labels: |
| </span></span><span style="display:flex;"><span> app: dubbo-samples-xds-consumer |
| </span></span><span style="display:flex;"><span> spec: |
| </span></span><span style="display:flex;"><span> containers: |
| </span></span><span style="display:flex;"><span> - name: server |
| </span></span><span style="display:flex;"><span> image: apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1 |
| </span></span><span style="display:flex;"><span> livenessProbe: |
| </span></span><span style="display:flex;"><span> httpGet: |
| </span></span><span style="display:flex;"><span> path: /live |
| </span></span><span style="display:flex;"><span> port: 22222 |
| </span></span><span style="display:flex;"><span> initialDelaySeconds: 5 |
| </span></span><span style="display:flex;"><span> periodSeconds: 5 |
| </span></span><span style="display:flex;"><span> readinessProbe: |
| </span></span><span style="display:flex;"><span> httpGet: |
| </span></span><span style="display:flex;"><span> path: /ready |
| </span></span><span style="display:flex;"><span> port: 22222 |
| </span></span><span style="display:flex;"><span> initialDelaySeconds: 5 |
| </span></span><span style="display:flex;"><span> periodSeconds: 5 |
| </span></span><span style="display:flex;"><span> startupProbe: |
| </span></span><span style="display:flex;"><span> httpGet: |
| </span></span><span style="display:flex;"><span> path: /startup |
| </span></span><span style="display:flex;"><span> port: 22222 |
| </span></span><span style="display:flex;"><span> failureThreshold: 30 |
| </span></span><span style="display:flex;"><span> periodSeconds: 10 |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>apiVersion: v1 |
| </span></span><span style="display:flex;"><span>kind: Service |
| </span></span><span style="display:flex;"><span>metadata: |
| </span></span><span style="display:flex;"><span> name: dubbo-samples-xds-consumer |
| </span></span><span style="display:flex;"><span> namespace: dubbo-demo |
| </span></span><span style="display:flex;"><span>spec: |
| </span></span><span style="display:flex;"><span> clusterIP: None |
| </span></span><span style="display:flex;"><span> selector: |
| </span></span><span style="display:flex;"><span> app: dubbo-samples-xds-consumer |
| </span></span><span style="display:flex;"><span> ports: |
| </span></span><span style="display:flex;"><span> - name: grpc |
| </span></span><span style="display:flex;"><span> protocol: TCP |
| </span></span><span style="display:flex;"><span> port: 50051 |
| </span></span><span style="display:flex;"><span> targetPort: 50051 |
| </span></span></code></pre></div><h4 id="编写dockerfile-1">编写Dockerfile</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>FROM openjdk:8-jdk |
| </span></span><span style="display:flex;"><span>ADD ./target/dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar |
| </span></span><span style="display:flex;"><span>CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar |
| </span></span></code></pre></div><p>✅ 到目前为止我们的环境和代码就全都准备完毕了!</p> |
| <h2 id="构建镜像">构建镜像</h2> |
| <h3 id="1启动docker">1、启动docker</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/5.png" alt="启动docker"></p> |
| <h3 id="2启动minikube">2、启动minikube</h3> |
| <p>因为minikube是一个本地的k8s,他启动需要一个虚拟引擎,这里我们用docker来管理。我们通过如下命令启动</p> |
| <p><code>minikube start</code></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/6.png" alt="启动minikube"></p> |
| <p>我们可以在docker里看到minikube</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/7.png" alt="minikube"></p> |
| <h3 id="3检查istio的状态">3、检查istio的状态</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/8.png" alt="istio的状态"></p> |
| <h3 id="4构建镜像">4、构建镜像</h3> |
| <p>在本地找到代码所在位置、依次执行以下命令</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span># 找到provider所在路径 |
| </span></span><span style="display:flex;"><span>cd ./dubbo-samples-xds-provider/ |
| </span></span><span style="display:flex;"><span># 构建provider的镜像 |
| </span></span><span style="display:flex;"><span>docker build -t apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1 . |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/20220905/9.png" alt="构建provider"></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span># 找到consumer所在路径 |
| </span></span><span style="display:flex;"><span>cd ../dubbo-samples-xds-consumer/ |
| </span></span><span style="display:flex;"><span># 构建consumer的镜像 |
| </span></span><span style="display:flex;"><span>docker build -t apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1 . |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/20220905/10.png" alt="构建consumer"></p> |
| <h3 id="5检查本地镜像">5、检查本地镜像</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/11.png" alt="检查本地镜像"></p> |
| <h3 id="6创建namespace">6、创建namespace</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span># 初始化命名空间 |
| </span></span><span style="display:flex;"><span>kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/dubbo-samples-xds/deploy/Namespace.yml |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span># 切换命名空间 |
| </span></span><span style="display:flex;"><span>kubens dubbo-demo |
| </span></span></code></pre></div><p>如果不创建namespace,那么会看到如下错误</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/12.png" alt="错误"></p> |
| <h2 id="部署容器">部署容器</h2> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span># 找到provider所在路径 |
| </span></span><span style="display:flex;"><span>cd ./dubbo-samples-xds-provider/src/main/resources/k8s |
| </span></span><span style="display:flex;"><span># dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Deployment.yml |
| </span></span><span style="display:flex;"><span># dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Service.yml |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span># 部署provider的Deployment和Service |
| </span></span><span style="display:flex;"><span>kubectl apply -f Deployment.yml |
| </span></span><span style="display:flex;"><span>kubectl apply -f Service.yml |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/20220905/13.png" alt="部署provider"></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span># 找到consumer所在路径 |
| </span></span><span style="display:flex;"><span>cd ../../../../../dubbo-samples-xds-consumer/src/main/resources/k8s |
| </span></span><span style="display:flex;"><span># dubbo-samples-xds/dubbo-samples-xds-consumer/src/main/resources/k8s/Deployment.yml |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span># 部署consumer的Deployment |
| </span></span><span style="display:flex;"><span>kubectl apply -f Deployment.yml |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/20220905/14.png" alt="部署provider"></p> |
| <p>在minikube dashboard看到我们已经部署的pod</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/15.png" alt="部署provider"></p> |
| <h2 id="观察consumer效果">观察consumer效果</h2> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>kubectl logs xxx |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>result: hello, xDS Consumer! from host: 172.17.0.5 |
| </span></span><span style="display:flex;"><span>result: hello, xDS Consumer! from host: 172.17.0.5 |
| </span></span><span style="display:flex;"><span>result: hello, xDS Consumer! from host: 172.17.0.6 |
| </span></span><span style="display:flex;"><span>result: hello, xDS Consumer! from host: 172.17.0.6 |
| </span></span></code></pre></div><h2 id="总结展望">总结&amp;展望</h2> |
| <p>本文主要剖析了 Dubbo Proxyless Mesh 的架构、服务发现以及证书管理等核心流程,最后通过示例给大家演示了如何使用 Dubbo Proxyless。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/20220905/16.png" alt="部署provider"></p> |
| <p>随着 Dubbo 3.1 的 release,Dubbo 在云原生的路上又迈出了重要的一步。在今年年底,Dubbo Mesh 将发布具有服务发现能力的版本, |
| 届时将面向所有 Dubbo 用户提供从低版本平滑迁移到 Mesh 架构的能力;在明年年初春季的时候将发布带有治理能力的版本;在明年年底前发布带热插件更新能力的版本, |
| 希望有兴趣见证 Dubbo 云原生之路的同学可以积极参与社区贡献!</p> |
| <p>更多关于 Dubbo Mesh 的动态可以关注 Apache Dubbo 社区官方公众号(ApacheDubbo),及时获取最新消息。</p></description></item><item><title>Blog: 22-Dubbo3消费者自动感应决策应用级服务发现原理</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/22/22-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E8%87%AA%E5%8A%A8%E6%84%9F%E5%BA%94%E5%86%B3%E7%AD%96%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%8E%9F%E7%90%86/</link><pubDate>Mon, 22 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/22/22-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E8%87%AA%E5%8A%A8%E6%84%9F%E5%BA%94%E5%86%B3%E7%AD%96%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%8E%9F%E7%90%86/</guid><description> |
| <h1 id="22-dubbo3消费者自动感应决策应用级服务发现原理">22-Dubbo3消费者自动感应决策应用级服务发现原理</h1> |
| <h2 id="221-简介">22.1 简介</h2> |
| <p>这里要说的内容对Dubbo2迁移到Dubbo3的应用比较有帮助,消费者应用级服务发现做了一些自动决策的逻辑来决定当前消费者是应用级发现还是接口级服务发现,这里与前面说的提供者双注册的原理是对等的,提供者默认同时进行应用级注册和接口级注册,消费者对提供者注册的数据来决定使用应用级发现或者接口级发现。这些都是默认的行为,当然对于消费者来说还可以自定义其他的迁移规则,具体的需要我们详细来看逻辑。</p> |
| <p>如果说对于迁移过程比较感兴趣可以直接去官网看文档相对来说还是比较清晰:<a href="https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/">https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/</a></p> |
| <p>这里再借官网的图来用用,迁移过程主要如下所示: |
| 第一个图是提供者双注册的图: |
| <img src="https://dubbo.apache.org/imgs/v3/migration/provider-registration.png" alt="在这里插入图片描述"></p> |
| <p>第二个图是消费者订阅决策的图: |
| <img src="https://dubbo.apache.org/imgs/v3/migration/consumer-subscription.png" alt="在这里插入图片描述"></p> |
| <p>第三个图就是精确到消费者订阅的代码层的逻辑了,消费者服务间调用通过一个Invoker类型对象来进行对象,如下图所示消费者代理对象通过创建一个迁移容错的调用器对象来对应用级或者接口级订阅进行适配如下所示 |
| <img src="https://dubbo.apache.org/imgs/v3/migration/migration-cluster-invoker.png" alt="在这里插入图片描述"></p> |
| <p>第二个图和第三个图是重点要关注的这一个文章的内容主要就是说这里的逻辑。</p> |
| <p>关于代码位置如果不知道是如何调用到这一块逻辑的可以查看博文<a href="https://blog.elastic.link/2022/07/10/dubbo/21-dubbo-xiao-fei-zhe-yin-yong-fu-wu-de-ru-kou/">《21-Dubbo3消费者引用服务入口》</a></p> |
| <p>这里直接将代码位置定位到:RegistryProtocol类型的interceptInvoker方法中: |
| 如下所示:</p> |
| <p>RegistryProtocol类型的interceptInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">interceptInvoker</span><span style="color:#719e07">(</span>ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">,</span> URL url<span style="color:#719e07">,</span> URL consumerUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目前存在的扩展类型为RegistryProtocolListener监听器的实现类型MigrationRuleListener |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryProtocolListener<span style="color:#719e07">&gt;</span> listeners <span style="color:#719e07">=</span> findRegistryProtocolListeners<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>listeners<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>RegistryProtocolListener listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> listener<span style="color:#719e07">.</span>onRefer<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> invoker<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>该方法尝试加载所有RegistryProtocolListener定义,这些定义通过与定义的交互来控制调用器的行为,然后使用这些侦听器更改MigrationInvoker的状态和行为。当前可用的监听器是MigrationRuleListener,用于通过动态变化的规则控制迁移行为。</p> |
| <h2 id="222-migrationrulelistener-类型的onrefer方法">22.2 MigrationRuleListener 类型的onRefer方法</h2> |
| <p>直接来看代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onRefer</span><span style="color:#719e07">(</span>RegistryProtocol registryProtocol<span style="color:#719e07">,</span> ClusterInvoker<span style="color:#719e07">&lt;?&gt;</span> invoker<span style="color:#719e07">,</span> URL consumerUrl<span style="color:#719e07">,</span> URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个对应invoker对象的MigrationRuleHandler类型对象 然后将其存放在缓存Map&lt;MigrationInvoker, MigrationRuleHandler&gt;类型对象handles中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MigrationRuleHandler<span style="color:#719e07">&lt;?&gt;</span> migrationRuleHandler <span style="color:#719e07">=</span> handlers<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">((</span>MigrationInvoker<span style="color:#719e07">&lt;?&gt;)</span> invoker<span style="color:#719e07">,</span> _key <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">((</span>MigrationInvoker<span style="color:#719e07">&lt;?&gt;)</span> invoker<span style="color:#719e07">).</span>setMigrationRuleListener<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> MigrationRuleHandler<span style="color:#719e07">&lt;&gt;((</span>MigrationInvoker<span style="color:#719e07">&lt;?&gt;)</span> invoker<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//迁移规则执行 rule是封装了迁移的配置规则的信息对应类型MigrationRule类型,在初始化对象的时候进行了配置初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> migrationRuleHandler<span style="color:#719e07">.</span>doMigrate<span style="color:#719e07">(</span>rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>关于这个igrationRule的文可以直接看官方的文档比较详细:<a href="https://dubbo.apache.org/zh-cn/docs/advanced/migration-invoker/#1-%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%B8%8B%E5%8F%91%E6%8E%A8%E8%8D%90">地址迁移规则说明</a></p> |
| <p>这个迁移规则是为了更细粒度的迁移决策: |
| 相关配置可以参考下面这个样例:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">key</span>: 消费者应用名(必填) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">step</span>: 状态名(必填) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">threshold</span>: 决策阈值(默认1.0) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">proportion</span>: 灰度比例(默认100) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">delay</span>: 延迟决策时间(默认0) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">force</span>: 强制切换(默认 false) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">interfaces</span>: 接口粒度配置(可选) |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">serviceKey: 接口名(接口 + </span>: + 版本号)(必填) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">threshold</span>: 决策阈值 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">proportion</span>: 灰度比例 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">delay</span>: 延迟决策时间 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">force</span>: 强制切换 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">step</span>: 状态名(必填) |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">serviceKey: 接口名(接口 + </span>: + 版本号) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">step</span>: 状态名 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">applications</span>: 应用粒度配置(可选) |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">serviceKey</span>: 应用名(消费的上游应用名)(必填) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">threshold</span>: 决策阈值 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">proportion</span>: 灰度比例 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">delay</span>: 延迟决策时间 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">force</span>: 强制切换 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">step</span>: 状态名(必填) |
| </span></span></code></pre></div><p>不过为了简单起见暂时先不详细说这个配置细节,我们继续往下看</p> |
| <h2 id="223-迁移规则处理器执行迁移规则migrationrulehandler类型的domigrate方法">22.3 迁移规则处理器执行迁移规则MigrationRuleHandler类型的doMigrate方法</h2> |
| <h3 id="2231-迁移规则的模版方法">22.3.1 迁移规则的模版方法:</h3> |
| <p>MigrationRuleHandler类型的doMigrate方法代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doMigrate</span><span style="color:#719e07">(</span>MigrationRule rule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认情况下这个类型是MigrationInvoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>migrationInvoker <span style="color:#719e07">instanceof</span> ServiceDiscoveryMigrationInvoker<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> refreshInvoker<span style="color:#719e07">(</span>MigrationStep<span style="color:#719e07">.</span>FORCE_APPLICATION<span style="color:#719e07">,</span> 1<span style="color:#719e07">.</span>0f<span style="color:#719e07">,</span> rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//迁移步骤,MigrationStep 一共有3种枚举情况:FORCE_INTERFACE, APPLICATION_FIRST, FORCE_APPLICATION |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// initial step : APPLICATION_FIRST |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MigrationStep step <span style="color:#719e07">=</span> MigrationStep<span style="color:#719e07">.</span>APPLICATION_FIRST<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">float</span> threshold <span style="color:#719e07">=</span> <span style="color:#719e07">-</span>1f<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置的类型 默认走APPLICATION_FIRST |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> step <span style="color:#719e07">=</span> rule<span style="color:#719e07">.</span>getStep<span style="color:#719e07">(</span>consumerURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//threshold: 决策阈值(默认-1.0)计算与获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> threshold <span style="color:#719e07">=</span> rule<span style="color:#719e07">.</span>getThreshold<span style="color:#719e07">(</span>consumerURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to get step and threshold info from rule: &#34;</span> <span style="color:#719e07">+</span> rule<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷洗调用器对象 来进行决策服务发现模式 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>refreshInvoker<span style="color:#719e07">(</span>step<span style="color:#719e07">,</span> threshold<span style="color:#719e07">,</span> rule<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// refresh success, update rule |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setMigrationRule<span style="color:#719e07">(</span>rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2232-服务发现调用器对象的选择决策服务发现策略">22.3.2 服务发现调用器对象的选择(决策服务发现策略)</h3> |
| <p>这里就是关键代码了:通过迁移配置和当前提供者注册信息来决定创建什么类型的调用器对象(Invoker)来为后续服务调用做准备</p> |
| <p>MigrationRuleHandler的refreshInvoker,注意默认情况下这个step参数为APPLICATION_FIRST</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">refreshInvoker</span><span style="color:#719e07">(</span>MigrationStep step<span style="color:#719e07">,</span> Float threshold<span style="color:#719e07">,</span> MigrationRule newRule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>step <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> threshold <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Step or threshold of migration rule cannot be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> MigrationStep originStep <span style="color:#719e07">=</span> currentStep<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>currentStep <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> currentStep <span style="color:#719e07">!=</span> step<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> <span style="color:#719e07">!</span>currentThreshold<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>threshold<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> success <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">switch</span> <span style="color:#719e07">(</span>step<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> APPLICATION_FIRST<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认和配置了应用级优先的服务发现则走这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> migrationInvoker<span style="color:#719e07">.</span>migrateToApplicationFirstInvoker<span style="color:#719e07">(</span>newRule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> FORCE_APPLICATION<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置了应用级服务发现则走这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> success <span style="color:#719e07">=</span> migrationInvoker<span style="color:#719e07">.</span>migrateToForceApplicationInvoker<span style="color:#719e07">(</span>newRule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> FORCE_INTERFACE<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置了接口级服务发现则走这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">default</span><span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> success <span style="color:#719e07">=</span> migrationInvoker<span style="color:#719e07">.</span>migrateToForceInterfaceInvoker<span style="color:#719e07">(</span>newRule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>success<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> setCurrentStepAndThreshold<span style="color:#719e07">(</span>step<span style="color:#719e07">,</span> threshold<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Succeed Migrated to &#34;</span> <span style="color:#719e07">+</span> step <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; mode. Service Name: &#34;</span> <span style="color:#719e07">+</span> consumerURL<span style="color:#719e07">.</span>getDisplayServiceKey<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> report<span style="color:#719e07">(</span>step<span style="color:#719e07">,</span> originStep<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// migrate failed, do not save new step and rule |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Migrate to &#34;</span> <span style="color:#719e07">+</span> step <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; mode failed. Probably not satisfy the threshold you set &#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> threshold <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;. Please try re-publish configuration if you still after check.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> report<span style="color:#719e07">(</span>step<span style="color:#719e07">,</span> originStep<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;false&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> success<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore if step is same with previous, will continue override rule for MigrationInvoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看到这个代码做了判断的逻辑分别对应了Dubbo3消费者迁移的一个状态逻辑: |
| 三种状态分别如下枚举类型: |
| 当前共存在三种状态,</p> |
| <ul> |
| <li>FORCE_INTERFACE(强制接口级)</li> |
| <li>APPLICATION_FIRST(应用级优先)</li> |
| <li>FORCE_APPLICATION(强制应用级)</li> |
| </ul> |
| <p>通过代码我们可以看到默认情况下都会走APPLICATION_FIRST(应用级优先)的策略,这里我们也重点来说 APPLICATION_FIRST(应用级优先)来看下Dubbo3是如何决策使用接口级还是应用级发现模型来兼容迁移的服务的。</p> |
| <h3 id="2233-应用级优先的服务发现规则逻辑">22.3.3 应用级优先的服务发现规则逻辑</h3> |
| <p>这个规则就是智能选择应用级还是接口级的代码了,对应类型为MigrationInvoker的migrateToApplicationFirstInvoker方法,接下来我们详细看下:</p> |
| <p>MigrationInvoker类型的migrateToApplicationFirstInvoker方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">migrateToApplicationFirstInvoker</span><span style="color:#719e07">(</span>MigrationRule newRule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> CountDownLatch latch <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CountDownLatch<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新接口级服务发现Invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> refreshInterfaceInvoker<span style="color:#719e07">(</span>latch<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新应用级服务发现Invoker类型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> refreshServiceDiscoveryInvoker<span style="color:#719e07">(</span>latch<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// directly calculate preferred invoker, will not wait until address notify |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// calculation will re-occurred when address notify later |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//计算当前使用应用级还是接口级服务发现的Invoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> calcPreferredInvoker<span style="color:#719e07">(</span>newRule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2234-刷新接口级服务发现invoker">22.3.4 刷新接口级服务发现Invoker</h3> |
| <p>MigrationInvoker类型的refreshInterfaceInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshInterfaceInvoker</span><span style="color:#719e07">(</span>CountDownLatch latch<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> clearListener<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>needRefresh<span style="color:#719e07">(</span>invoker<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isDebugEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>debug<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Re-subscribing interface addresses for interface &#34;</span> <span style="color:#719e07">+</span> type<span style="color:#719e07">.</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>invoker <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> invoker<span style="color:#719e07">.</span>destroy<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> registryProtocol<span style="color:#719e07">.</span>getInvoker<span style="color:#719e07">(</span>cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> setListener<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> <span style="color:#719e07">()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> latch<span style="color:#719e07">.</span>countDown<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>reportService<span style="color:#719e07">.</span>hasReporter<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>reportConsumptionStatus<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>createConsumptionReport<span style="color:#719e07">(</span>consumerUrl<span style="color:#719e07">.</span>getServiceInterface<span style="color:#719e07">(),</span> consumerUrl<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">(),</span> consumerUrl<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;interface&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>step <span style="color:#719e07">==</span> APPLICATION_FIRST<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> calcPreferredInvoker<span style="color:#719e07">(</span>rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2235-刷新应用级服务发现invoker类型对象">22.3.5 刷新应用级服务发现Invoker类型对象</h3> |
| <p>MigrationInvoker类型的refreshServiceDiscoveryInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshServiceDiscoveryInvoker</span><span style="color:#719e07">(</span>CountDownLatch latch<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> clearListener<span style="color:#719e07">(</span>serviceDiscoveryInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>needRefresh<span style="color:#719e07">(</span>serviceDiscoveryInvoker<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isDebugEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>debug<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Re-subscribing instance addresses, current interface &#34;</span> <span style="color:#719e07">+</span> type<span style="color:#719e07">.</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>serviceDiscoveryInvoker <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceDiscoveryInvoker<span style="color:#719e07">.</span>destroy<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> serviceDiscoveryInvoker <span style="color:#719e07">=</span> registryProtocol<span style="color:#719e07">.</span>getServiceDiscoveryInvoker<span style="color:#719e07">(</span>cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> setListener<span style="color:#719e07">(</span>serviceDiscoveryInvoker<span style="color:#719e07">,</span> <span style="color:#719e07">()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> latch<span style="color:#719e07">.</span>countDown<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>reportService<span style="color:#719e07">.</span>hasReporter<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>reportConsumptionStatus<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>createConsumptionReport<span style="color:#719e07">(</span>consumerUrl<span style="color:#719e07">.</span>getServiceInterface<span style="color:#719e07">(),</span> consumerUrl<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">(),</span> consumerUrl<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;app&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>step <span style="color:#719e07">==</span> APPLICATION_FIRST<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> calcPreferredInvoker<span style="color:#719e07">(</span>rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2236-计算当前使用应用级还是接口级服务发现的invoker对象">22.3.6 计算当前使用应用级还是接口级服务发现的Invoker对象</h3> |
| <p>MigrationInvoker类型的的calcPreferredInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">calcPreferredInvoker</span><span style="color:#719e07">(</span>MigrationRule migrationRule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>serviceDiscoveryInvoker <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> invoker <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>MigrationAddressComparator<span style="color:#719e07">&gt;</span> detectors <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">(</span>consumerUrl <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">:</span> consumerUrl<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>MigrationAddressComparator<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>detectors<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// pick preferred invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// the real invoker choice in invocation will be affected by promotion |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>detectors<span style="color:#719e07">.</span>stream<span style="color:#719e07">().</span>allMatch<span style="color:#719e07">(</span>comparator <span style="color:#719e07">-&gt;</span> comparator<span style="color:#719e07">.</span>shouldMigrate<span style="color:#719e07">(</span>serviceDiscoveryInvoker<span style="color:#719e07">,</span> invoker<span style="color:#719e07">,</span> migrationRule<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>currentAvailableInvoker <span style="color:#719e07">=</span> serviceDiscoveryInvoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>currentAvailableInvoker <span style="color:#719e07">=</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>currentAvailableInvoker是后期服务调用使用的Invoker对象</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/22-dubbo3-xiao-fei-zhe-zi-dong-gan-ying-jue-ce-ying-yong-ji-fu-wu-fa-xian-yuan-li/">22-Dubbo3消费者自动感应决策应用级服务发现原理</a></p></description></item><item><title>Blog: 21-Dubbo3消费者引用服务入口</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/21/21-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E5%BC%95%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%85%A5%E5%8F%A3/</link><pubDate>Sun, 21 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/21/21-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E5%BC%95%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%85%A5%E5%8F%A3/</guid><description> |
| <h1 id="21-dubbo3消费者引用服务入口">21-Dubbo3消费者引用服务入口</h1> |
| <h2 id="211-简介">21.1 简介</h2> |
| <p>前面我们通过Demo说了一个服务引用配置的创建。另外也在前面的文章说了服务提供者的启动完整过程,不过在说服务提供者启动的过程中并未提到服务消费者是如何发现服务,如果调用服务的,这里先就不再说关于服务消费者启动的一个细节了,直接来看前面未提到的服务消费者是如何引用到服务提供者提供的服务的。 |
| 先来回顾下样例代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ConsumerApplication</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> runWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">runWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ReferenceConfig<span style="color:#719e07">&lt;</span>DemoService<span style="color:#719e07">&gt;</span> reference <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ReferenceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setGeneric<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-consumer&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setQosEnable<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setQosPort<span style="color:#719e07">(-</span>1<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://8.131.79.126:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>reference<span style="color:#719e07">(</span>reference<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DemoService demoService <span style="color:#719e07">=</span> bootstrap<span style="color:#719e07">.</span>getCache<span style="color:#719e07">().</span>get<span style="color:#719e07">(</span>reference<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> String message <span style="color:#719e07">=</span> demoService<span style="color:#719e07">.</span>sayHello<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span>message<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generic invoke |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> GenericService genericService <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>GenericService<span style="color:#719e07">)</span> demoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> Object genericInvokeResult <span style="color:#719e07">=</span> genericService<span style="color:#719e07">.</span>$invoke<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;sayHello&#34;</span><span style="color:#719e07">,</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[]{</span>String<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">()},</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> Object<span style="color:#719e07">[]{</span><span style="color:#2aa198">&#34;dubbo generic invoke&#34;</span><span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span>genericInvokeResult<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这段代码我们前面详细说了服务引用的配置ReferenceConfig和Dubbo启动器启动应用的过程DubboBootstrap,后面我们直接定位到消费者引用服务的代码位置来看。</p> |
| <h2 id="212-入口代码">21.2 入口代码</h2> |
| <h3 id="2121-defaultmoduledeployer的start方法">21.2.1 DefaultModuleDeployer的start方法</h3> |
| <p>第一个要关注的就是模块发布器DefaultModuleDeployer的start方法,这个start方法包含了Dubbo应用启动的过程</p> |
| <p>DefaultModuleDeployer的start方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> Future <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span>省略掉若干代码 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> onModuleStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// initialize |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// prepare application instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// exclude internal module to avoid wait itself |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModel <span style="color:#719e07">!=</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getInternalModule<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer<span style="color:#719e07">.</span>prepareInternalModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// refer services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span>省略掉若干代码 |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法大部分代码已经省略,也不会详细去说了,感兴趣的可以看之前讲到的博客,这里主要来看引用服务方法referServices</p> |
| <h3 id="2122-defaultmoduledeployer的referservices方法">21.2.2 DefaultModuleDeployer的referServices方法</h3> |
| <p>下面就要来看消费者应用如何引用的服务的入口了,这个方法主要从大的方面做了一些服务引用生命周期的代码,看懂了这个方法我们就可以不依赖Dubbo负载的启动逻辑可以单独调用ReferenceConfigBase类型的对应方法来刷新,启动,销毁引用的服务了这里先来看下代码再详细介绍内容:</p> |
| <p>DefaultModuleDeployer的referServices方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">referServices</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个是获取配置的所有的ReferenceConfigBase类型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>getReferences<span style="color:#719e07">().</span>forEach<span style="color:#719e07">(</span>rc <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ReferenceConfig<span style="color:#719e07">&lt;?&gt;</span> referenceConfig <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ReferenceConfig<span style="color:#719e07">&lt;?&gt;)</span> rc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>referenceConfig<span style="color:#719e07">.</span>isRefreshed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新引用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referenceConfig<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>rc<span style="color:#719e07">.</span>shouldInit<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>referAsync <span style="color:#719e07">||</span> rc<span style="color:#719e07">.</span>shouldReferAsync<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ExecutorService executor <span style="color:#719e07">=</span> executorRepository<span style="color:#719e07">.</span>getServiceReferExecutor<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> CompletableFuture<span style="color:#719e07">&lt;</span>Void<span style="color:#719e07">&gt;</span> future <span style="color:#719e07">=</span> CompletableFuture<span style="color:#719e07">.</span>runAsync<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//间接的通过缓存对象来引用服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referenceCache<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; refer async catch error : &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> executor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> asyncReferringFutures<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>future<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//间接的通过缓存对象来引用服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referenceCache<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; refer catch error.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//出现异常销毁引用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referenceCache<span style="color:#719e07">.</span>destroy<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> t<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在这个代码中我们核心需要关心的就是SimpleReferenceCache类型的get方法了,在获取服务对象之外包装了一层缓存。</p> |
| <p>如果出现了异常则执行referenceCache的destroy方法进行销毁引用配置。</p> |
| <h2 id="213-开始引用服务">21.3 开始引用服务</h2> |
| <h3 id="2131-simplereferencecache是什么">21.3.1 SimpleReferenceCache是什么?</h3> |
| <p>一个用于缓存引用ReferenceConfigBase的util工具类。 |
| ReferenceConfigBase是一个重对象,对于频繁创建ReferenceConfigBase的框架来说,有必要缓存这些对象。 |
| 如果需要使用复杂的策略,可以实现并使用自己的ReferenceConfigBase缓存 |
| 这个Cache是引用服务的开始如果我们想在代码中自定义一些服务引用的逻辑,可以直接创建SimpleReferenceCache类型对象然后调用其get方法进行引用服务。那这个缓存对象是和缓存与引用服务的可以继续往下看。</p> |
| <h3 id="2132-引用服务之前的缓存处理逻辑">21.3.2 引用服务之前的缓存处理逻辑?</h3> |
| <p>关于逻辑的处理,看代码有时候比文字更清晰明了,这里可以直接来看 SimpleReferenceCache类型的get方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@SuppressWarnings</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;unchecked&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T <span style="color:#268bd2">get</span><span style="color:#719e07">(</span>ReferenceConfigBase<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> rc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个生成的key规则是这样的 服务分组/服务接口:版本号 详细的代码就不看了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//例如: group/link.elastic.dubbo.entity.DemoService:1.0 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> generator<span style="color:#719e07">.</span>generateKey<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务类型 如果是泛化调用则这个类型为GenericService |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Class<span style="color:#719e07">&lt;?&gt;</span> type <span style="color:#719e07">=</span> rc<span style="color:#719e07">.</span>getInterfaceClass<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务是否为单例的这里默认值都为空,为单例模式 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> singleton <span style="color:#719e07">=</span> rc<span style="color:#719e07">.</span>getSingleton<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> rc<span style="color:#719e07">.</span>getSingleton<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> T proxy <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Check existing proxy of the same &#39;key&#39; and &#39;type&#39; first. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>singleton<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一般为单例的 这个方法是从缓存中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> proxy <span style="color:#719e07">=</span> get<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//非单例容易造成内存泄露,无法从缓存中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Using non-singleton ReferenceConfig and ReferenceCache at the same time may cause memory leak. &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;Call ReferenceConfig#get() directly for non-singleton ReferenceConfig instead of using ReferenceCache#get(ReferenceConfig)&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//前面是从缓存中拿,如果缓存中获取不到则开始引用服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>proxy <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取或者创建值,为引用类型referencesOfType对象(类型为Map&lt;Class&lt;?&gt;, List&lt;ReferenceConfigBase&lt;?&gt;&gt;&gt;)缓存对象生成值(值不存咋时候会生成一个) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>ReferenceConfigBase<span style="color:#719e07">&lt;?&gt;&gt;</span> referencesOfType <span style="color:#719e07">=</span> referenceTypeMap<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> _t <span style="color:#719e07">-&gt;</span> Collections<span style="color:#719e07">.</span>synchronizedList<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//每次走到这里都会添加一个ReferenceConfigBase 引用配置对象(单例的从缓存中拿到就可以直接返回了) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referencesOfType<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//与前面一样 前面是类型映射,这里是key映射 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>ReferenceConfigBase<span style="color:#719e07">&lt;?&gt;&gt;</span> referenceConfigList <span style="color:#719e07">=</span> referenceKeyMap<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> _k <span style="color:#719e07">-&gt;</span> Collections<span style="color:#719e07">.</span>synchronizedList<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;()));</span> |
| </span></span><span style="display:flex;"><span> referenceConfigList<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//开始引用服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> proxy <span style="color:#719e07">=</span> rc<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> proxy<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看到这个逻辑使用了享元模式(其实就是先查缓存,缓存不存在则创建对象存入缓存)来进行引用对象的管理这样一个过程,这里一共有两个缓存对象referencesOfType和referenceConfigList |
| key分别为引用类型和引用的服务的key,值是引用服务的基础配置对象列表List&lt;ReferenceConfigBase<?>&gt;</p> |
| <p>后面可以详细看下如果借助ReferenceConfigBase类型对象来进行具体类型的引用。</p> |
| <h2 id="214-初始化引用服务的过程">21.4 初始化引用服务的过程</h2> |
| <h3 id="2141-初始化引用服务的调用入口">21.4.1 初始化引用服务的调用入口</h3> |
| <p>引用服务的逻辑其实是相对复杂一点的,包含了服务发现,引用对象的创建等等,接下来就让我们详细看下:</p> |
| <p>ReferenceConfig类型的get方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> T <span style="color:#268bd2">get</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>destroyed<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The invoker of ReferenceConfig(&#34;</span> <span style="color:#719e07">+</span> url <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) has already destroyed!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ref类型为 transient volatile T ref; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ref <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ensure start module, compatible with old api usage |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这个前面已经调用了模块发布器启动过了,这里有这么一行代码是有一定作用的,如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化下面执行逻辑的时候出现问题,这个代码其实就是启动模块进行一些基础配置的初始化操作 比如元数据中心默认配置选择,注册中心默认配置选择这些都是比较重要的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getScopeModel<span style="color:#719e07">().</span>getDeployer<span style="color:#719e07">().</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ref <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> init<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> ref<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里有一段代码是:getScopeModel().getDeployer().start(); |
| 这个前面已经调用了模块发布器启动过了,这里有这么一行代码是有一定作用的,如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化下面执行逻辑的时候出现问题,这个代码其实就是启动模块进行一些基础配置的初始化操作 比如元数据中心默认配置选择,注册中心默认配置选择这些都是比较重要的。</p> |
| <p>另外可以看到的是这里使用了双重校验锁来保证单例对象的创建,发现Dubbo种大量的使用了双重校验锁的逻辑。</p> |
| <h3 id="2142-初始化引用服务">21.4.2 初始化引用服务</h3> |
| <p>这个就直接看代码了这,初始化过程相对复杂一点,我们一点点来看 |
| ReferenceConfig类型init()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化标记变量保证只初始化一次,这里又是加锁🔐又是加标记变量的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>isRefreshed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init serviceMetadata |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//初始化ServiceMetadata类型对象serviceMetadata 为其设置服务基本属性比如版本号,分组,服务接口名 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initServiceMetadata<span style="color:#719e07">(</span>consumer<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//继续初始化元数据信息 服务接口类型和key |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>setServiceType<span style="color:#719e07">(</span>getServiceInterfaceClass<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// TODO, uncomment this line once service key is unified |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>setServiceKey<span style="color:#719e07">(</span>URL<span style="color:#719e07">.</span>buildKey<span style="color:#719e07">(</span>interfaceName<span style="color:#719e07">,</span> group<span style="color:#719e07">,</span> version<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置转Map类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> referenceParameters <span style="color:#719e07">=</span> appendConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init service-application mapping |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//来自本地存储和url参数的初始化映射。 参数转URL配置初始化 Dubbo中喜欢用url作为配置的一种处理方式 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initServiceAppsMapping<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地内存模块服务存储库 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ModuleServiceRepository repository <span style="color:#719e07">=</span> getScopeModel<span style="color:#719e07">().</span>getServiceRepository<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ServiceModel和ServiceMetadata在某种程度上是相互重复的。我们将来应该合并它们。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceDescriptor serviceDescriptor<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>NATIVE_STUB<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>getProxy<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceDescriptor <span style="color:#719e07">=</span> StubSuppliers<span style="color:#719e07">.</span>getServiceDescriptor<span style="color:#719e07">(</span>interfaceName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>serviceDescriptor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地存储库注册服务接口类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceDescriptor <span style="color:#719e07">=</span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>interfaceClass<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//消费者模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> consumerModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConsumerModel<span style="color:#719e07">(</span>serviceMetadata<span style="color:#719e07">.</span>getServiceKey<span style="color:#719e07">(),</span> proxy<span style="color:#719e07">,</span> serviceDescriptor<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> getScopeModel<span style="color:#719e07">(),</span> serviceMetadata<span style="color:#719e07">,</span> createAsyncMethodInfo<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地存储库注册消费者模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> repository<span style="color:#719e07">.</span>registerConsumer<span style="color:#719e07">(</span>consumerModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//与前面代码一样基础初始化服务元数据对象为其设置附加参数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>getAttachments<span style="color:#719e07">().</span>putAll<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务的代理对象 !!!核心代码在这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ref <span style="color:#719e07">=</span> createProxy<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为服务元数据对象设置代理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>setTarget<span style="color:#719e07">(</span>ref<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceMetadata<span style="color:#719e07">.</span>addAttribute<span style="color:#719e07">(</span>PROXY_CLASS_REF<span style="color:#719e07">,</span> ref<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> consumerModel<span style="color:#719e07">.</span>setProxyObject<span style="color:#719e07">(</span>ref<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> consumerModel<span style="color:#719e07">.</span>initMethodModels<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查invoker对象初始结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkInvokerAvailable<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="215-referenceconfig创建服务引用代理对象的原理">21.5 ReferenceConfig创建服务引用代理对象的原理</h2> |
| <h3 id="2151-代理对象的创建过程">21.5.1 代理对象的创建过程</h3> |
| <p>这里就要继续看 ReferenceConfig类型的创建代理方法createProxy了 |
| 直接贴一下源码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> T <span style="color:#268bd2">createProxy</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> referenceParameters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地引用 这里为false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>shouldJvmRefer<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> createInvokerForLocal<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> urls<span style="color:#719e07">.</span>clear<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//url存在则为点对点引用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// user specified URL, could be peer-to-peer address, or register center&#39;s address. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> parseUrl<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if protocols not in jvm checkRegistry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里不是local协议默认这里为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>LOCAL_PROTOCOL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从注册表中获取URL并将其聚合。这个其实就是初始化一下注册中心的url配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> aggregateUrlFromRegistry<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个代码非常重要 创建远程引用,创建远程引用调用器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> createInvokerForRemote<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Referred dubbo service: [&#34;</span> <span style="color:#719e07">+</span> referenceParameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;].&#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>Boolean<span style="color:#719e07">.</span>parseBoolean<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>GENERIC_KEY<span style="color:#719e07">))</span> <span style="color:#719e07">?</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34; it&#39;s GenericService reference&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34; it&#39;s not GenericService reference&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> URL consumerUrl <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfigURL<span style="color:#719e07">(</span>CONSUMER_PROTOCOL<span style="color:#719e07">,</span> referenceParameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>REGISTER_IP_KEY<span style="color:#719e07">),</span> 0<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> referenceParameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">),</span> referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> consumerUrl <span style="color:#719e07">=</span> consumerUrl<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>getScopeModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> consumerUrl <span style="color:#719e07">=</span> consumerUrl<span style="color:#719e07">.</span>setServiceModel<span style="color:#719e07">(</span>consumerModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> MetadataUtils<span style="color:#719e07">.</span>publishServiceDefinition<span style="color:#719e07">(</span>consumerUrl<span style="color:#719e07">,</span> consumerModel<span style="color:#719e07">.</span>getServiceModel<span style="color:#719e07">(),</span> getApplicationModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// create service proxy |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> proxyFactory<span style="color:#719e07">.</span>getProxy<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> ProtocolUtils<span style="color:#719e07">.</span>isGeneric<span style="color:#719e07">(</span>generic<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2152-创建远程引用创建远程引用调用器">21.5.2 创建远程引用,创建远程引用调用器</h3> |
| <p>ReferenceConfig类型的createInvokerForRemote方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">createInvokerForRemote</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个url 为注册协议如registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&amp;dubbo=2.0.2&amp;pid=6204&amp;qos.enable=false&amp;qos.port=-1&amp;registry=zookeeper&amp;release=3.0.9&amp;timestamp=1657439419495 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>urls<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> URL curUrl <span style="color:#719e07">=</span> urls<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个SPI对象是由字节码动态生成的自适应对象Protocol$Adaptie直接看看不到源码,后续可以解析一个字节码生成的类型,这里后续来调用链路即可 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invoker <span style="color:#719e07">=</span> protocolSPI<span style="color:#719e07">.</span>refer<span style="color:#719e07">(</span>interfaceClass<span style="color:#719e07">,</span> curUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>curUrl<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;?&gt;&gt;</span> invokers <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> invokers<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> Cluster<span style="color:#719e07">.</span>DEFAULT<span style="color:#719e07">).</span>join<span style="color:#719e07">(</span><span style="color:#719e07">new</span> StaticDirectory<span style="color:#719e07">(</span>curUrl<span style="color:#719e07">,</span> invokers<span style="color:#719e07">),</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;?&gt;&gt;</span> invokers <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> URL registryUrl <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>URL url <span style="color:#719e07">:</span> urls<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// For multi-registry scenarios, it is not checked whether each referInvoker is available. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// Because this invoker may become available later. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invokers<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>protocolSPI<span style="color:#719e07">.</span>refer<span style="color:#719e07">(</span>interfaceClass<span style="color:#719e07">,</span> url<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// use last registry url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryUrl <span style="color:#719e07">=</span> url<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registryUrl <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// registry url is available |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// for multi-subscription scenario, use &#39;zone-aware&#39; policy by default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String cluster <span style="color:#719e07">=</span> registryUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CLUSTER_KEY<span style="color:#719e07">,</span> ZoneAwareCluster<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -&gt; FailoverClusterInvoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// (RegistryDirectory, routing happens here) -&gt; Invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invoker <span style="color:#719e07">=</span> Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">(),</span> cluster<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">).</span>join<span style="color:#719e07">(</span><span style="color:#719e07">new</span> StaticDirectory<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">,</span> invokers<span style="color:#719e07">),</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// not a registry url, must be direct invoke. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>invokers<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;invokers == null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> URL curUrl <span style="color:#719e07">=</span> invokers<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">).</span>getUrl<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String cluster <span style="color:#719e07">=</span> curUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CLUSTER_KEY<span style="color:#719e07">,</span> Cluster<span style="color:#719e07">.</span>DEFAULT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> cluster<span style="color:#719e07">).</span>join<span style="color:#719e07">(</span><span style="color:#719e07">new</span> StaticDirectory<span style="color:#719e07">(</span>curUrl<span style="color:#719e07">,</span> invokers<span style="color:#719e07">),</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2153-invoker对象创建的全过程">21.5.3 Invoker对象创建的全过程</h3> |
| <p>为了更好理解Protocol$Adaptie内部的引用执行过程这里我把Debug的链路截图了过来 |
| 按照固定的顺序先执行AOP的逻辑再执行具体的逻辑:</p> |
| <ul> |
| <li>Protocol$Adaptie的refer方法</li> |
| <li>ProtocolSerializationWrapper AOP类型的协议序列化器refer方法</li> |
| <li>ProtocolFilterWrapper AOP类型的协议过滤器的refer方法</li> |
| <li>QosProtocolWrapper AOP类型的QOS协议包装器的refer方法</li> |
| <li>ProtocolListenerWrapper APO类型监听器包装器的refer方法</li> |
| <li>RegistryProtocol 注册协议的refer方法 (会添加容错逻辑)</li> |
| <li>RegistryProtocol 注册协议的doRefer方法(调用方法创建Invoker对象)</li> |
| </ul> |
| <p><a href="https://dubbo.apache.org/imgs/blog/source-blog/21-createInvokerRemote.png"></a></p> |
| <p>这里我们不再详细说这个引用链的具体过程直接定位到RegistryProtocol中创建Invoker类型的地方。 |
| 先来看RegistryProtocol类型的refer方法,如下代码所示:</p> |
| <p>RegistryProtocol类型的refer方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@SuppressWarnings</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;unchecked&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">refer</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个url已经被转换为具体的注册中心协议类型了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&amp;dubbo=2.0.2&amp;pid=7944&amp;qos.enable=false&amp;qos.port=-1&amp;release=3.0.9&amp;timestamp=1657440673100 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> getRegistryUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取用于操作Zookeeper的Registry类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>RegistryService<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>type<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> proxyFactory<span style="color:#719e07">.</span>getInvoker<span style="color:#719e07">((</span>T<span style="color:#719e07">)</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// group=&#34;a,b&#34; or group=&#34;*&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> qs <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;)</span> url<span style="color:#719e07">.</span>getAttribute<span style="color:#719e07">(</span>REFER_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> String group <span style="color:#719e07">=</span> qs<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>GROUP_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>group<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>COMMA_SPLIT_PATTERN<span style="color:#719e07">.</span>split<span style="color:#719e07">(</span>group<span style="color:#719e07">)).</span>length <span style="color:#719e07">&gt;</span> 1 <span style="color:#719e07">||</span> <span style="color:#2aa198">&#34;*&#34;</span><span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>group<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> doRefer<span style="color:#719e07">(</span>Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">(),</span> MergeableCluster<span style="color:#719e07">.</span>NAME<span style="color:#719e07">),</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> qs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//降级容错的逻辑处理对象 类型为Cluster 实际类型为MockClusterWrapper 内部包装的是FailoverCluster |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//后续调用服务失败时候会先失效转移再降级 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Cluster cluster <span style="color:#719e07">=</span> Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">(),</span> qs<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>CLUSTER_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里才是具体的Invoker对象的创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> doRefer<span style="color:#719e07">(</span>cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> qs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>RegistryProtocol类型的doRefer方法创建Invoker对象 |
| 直接来看代码了</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">doRefer</span><span style="color:#719e07">(</span>Cluster cluster<span style="color:#719e07">,</span> Registry registry<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> URL url<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> parameters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> consumerAttribute <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;(</span>url<span style="color:#719e07">.</span>getAttributes<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> consumerAttribute<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>REFER_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> String p <span style="color:#719e07">=</span> isEmpty<span style="color:#719e07">(</span>parameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>PROTOCOL_KEY<span style="color:#719e07">))</span> <span style="color:#719e07">?</span> CONSUMER <span style="color:#719e07">:</span> parameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>PROTOCOL_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> URL consumerUrl <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfigURL <span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> p<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> parameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>REGISTER_IP_KEY<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> 0<span style="color:#719e07">,</span> getPath<span style="color:#719e07">(</span>parameters<span style="color:#719e07">,</span> type<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> parameters<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> consumerAttribute |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>putAttribute<span style="color:#719e07">(</span>CONSUMER_URL_KEY<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重点看这一行 带迁移性质的Invoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> migrationInvoker <span style="color:#719e07">=</span> getMigrationInvoker<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一行回来执行迁移规则创建应用级优先的服务发现Invoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> interceptInvoker<span style="color:#719e07">(</span>migrationInvoker<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里代码比较重要的其实只有两行getMigrationInvoker和interceptInvoker方法 |
| 比较核心也是Dubbo3比较重要的消费者启动逻辑基本都在这个方法里面interceptInvoker,这个方法执行了消费者应用级发现和接口级发现迁移的逻辑,会自动帮忙决策一个Invoker类型对象,不过这个逻辑这里先简单看下,后续单独整个文章来说。</p> |
| <p>这里我们先来看 ClusterInvoker对象的创建,下面先看代码:</p> |
| <p>RegistryProtocol类型的getMigrationInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getMigrationInvoker</span><span style="color:#719e07">(</span>RegistryProtocol registryProtocol<span style="color:#719e07">,</span> Cluster cluster<span style="color:#719e07">,</span> Registry registry<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> URL url<span style="color:#719e07">,</span> URL consumerUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ServiceDiscoveryMigrationInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>registryProtocol<span style="color:#719e07">,</span> cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>详细的逻辑这里就不再看了,我们继续看RegistryProtocol类型的interceptInvoker方法:</p> |
| <p>具体代码如下: |
| RegistryProtocol类型的interceptInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">interceptInvoker</span><span style="color:#719e07">(</span>ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">,</span> URL url<span style="color:#719e07">,</span> URL consumerUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取激活的注册协议监听器扩展里面registry.protocol.listener,这里激活的类型为MigrationRuleListener |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryProtocolListener<span style="color:#719e07">&gt;</span> listeners <span style="color:#719e07">=</span> findRegistryProtocolListeners<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>listeners<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>RegistryProtocolListener listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里执行MigrationRuleListener类型的onRefer方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> listener<span style="color:#719e07">.</span>onRefer<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> invoker<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>该方法尝试加载所有RegistryProtocolListener定义,这些定义通过与定义的交互来控制调用器的行为,然后使用这些侦听器更改MigrationInvoker的状态和行为。 |
| 当前可用的监听器是MigrationRuleListener,用于通过动态变化的规则控制迁移行为。</p> |
| <p>可以看到核心的逻辑集中在了这个位置MigrationRuleListener类型的onRefer方法,这个这里就不深入往下说了,后续会有个文章专门来看Dubbo2迁移Dubbo3时候处理的逻辑。</p> |
| <p>Invoker对象的创建完成其实就代表了服务引用执行完成,不过这里核心的协议并没有来说</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/21-dubbo-xiao-fei-zhe-yin-yong-fu-wu-de-ru-kou/">21-Dubbo3消费者引用服务入口</a></p></description></item><item><title>Blog: 20-Dubbo3服务引用配置ReferenceConfig</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/20/20-dubbo3%E6%9C%8D%E5%8A%A1%E5%BC%95%E7%94%A8%E9%85%8D%E7%BD%AEreferenceconfig/</link><pubDate>Sat, 20 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/20/20-dubbo3%E6%9C%8D%E5%8A%A1%E5%BC%95%E7%94%A8%E9%85%8D%E7%BD%AEreferenceconfig/</guid><description> |
| <h1 id="20-dubbo3服务引用配置referenceconfig">20-Dubbo3服务引用配置ReferenceConfig</h1> |
| <h2 id="201-简介">20.1 简介</h2> |
| <p>前面简单介绍了一下消费者的例子,消费者创建的第一步就是先进行消费者信息的配置对应类型为ReferenceConfig,这里详细来看ReferenceConfig包含哪些信息?先简单了解下消费者配置的类型关系如下图所示:引用配置与服务配置类型都是通过继承接口配置来扩展的,在分析生产者的时候详细介绍过服务相关的配置,这里来详细看消费者引用者的相关配置信息. |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/20-refe.png" alt="在这里插入图片描述"></p> |
| <p>前面例子说了消费者配置对象的创建主要是通过如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ReferenceConfig<span style="color:#719e07">&lt;</span>DemoService<span style="color:#719e07">&gt;</span> reference <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ReferenceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span></code></pre></div><p>这个配置类型的对象创建过程并没有太多的逻辑这里主要来说下各种配置信息: |
| 服务消费者引用服务配置。对应的配置类: <code>org.apache.dubbo.config.ReferenceConfig</code></p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>id</td> |
| <td></td> |
| <td>string</td> |
| <td><strong>必填</strong></td> |
| <td></td> |
| <td>配置关联</td> |
| <td>服务引用BeanId</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>interface</td> |
| <td></td> |
| <td>class</td> |
| <td><strong>必填</strong></td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务接口名</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>version</td> |
| <td>version</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务版本,与服务提供者的版本一致</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>group</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务分组,当一个接口有多个实现,可以用分组区分,必需和服务提供方一致</td> |
| <td>1.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>timeout</td> |
| <td>long</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的timeout</td> |
| <td>性能调优</td> |
| <td>服务方法调用超时时间(毫秒)</td> |
| <td>1.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>retries</td> |
| <td>retries</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的retries</td> |
| <td>性能调优</td> |
| <td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>connections</td> |
| <td>connections</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的connections</td> |
| <td>性能调优</td> |
| <td>对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>loadbalance</td> |
| <td>loadbalance</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的loadbalance</td> |
| <td>性能调优</td> |
| <td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>async</td> |
| <td>async</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的async</td> |
| <td>性能调优</td> |
| <td>是否异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>generic</td> |
| <td>generic</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的generic</td> |
| <td>服务治理</td> |
| <td>是否缺省泛化接口,如果为泛化接口,将返回GenericService</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>check</td> |
| <td>check</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的check</td> |
| <td>服务治理</td> |
| <td>启动时检查提供者是否存在,true报错,false忽略</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>url</td> |
| <td>url</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>点对点直连服务提供者地址,将绕过注册中心</td> |
| <td>1.0.6以上版本</td> |
| </tr> |
| <tr> |
| <td>stub</td> |
| <td>stub</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>mock</td> |
| <td>mock</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务接口调用失败Mock实现类名,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。</td> |
| <td>Dubbo1.0.13及其以上版本支持</td> |
| </tr> |
| <tr> |
| <td>cache</td> |
| <td>cache</td> |
| <td>string/boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等</td> |
| <td>Dubbo2.1.0及其以上版本支持</td> |
| </tr> |
| <tr> |
| <td>validation</td> |
| <td>validation</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验</td> |
| <td>Dubbo2.1.0及其以上版本支持</td> |
| </tr> |
| <tr> |
| <td>proxy</td> |
| <td>proxy</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>javassist</td> |
| <td>性能调优</td> |
| <td>选择动态代理实现策略,可选:javassist, jdk</td> |
| <td>2.0.2以上版本</td> |
| </tr> |
| <tr> |
| <td>client</td> |
| <td>client</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>性能调优</td> |
| <td>客户端传输类型设置,如Dubbo协议的netty或mina。</td> |
| <td>Dubbo2.0.0以上版本支持</td> |
| </tr> |
| <tr> |
| <td>registry</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省将从所有注册中心获服务列表后合并结果</td> |
| <td>配置关联</td> |
| <td>从指定注册中心注册获取服务列表,在多个注册中心时使用,值为<a href="dubbo:registry">dubbo:registry</a>的id属性,多个注册中心ID用逗号分隔</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>owner</td> |
| <td>owner</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>调用服务负责人,用于服务治理,请填写负责人公司邮箱前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>actives</td> |
| <td>actives</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>每服务消费者每服务每方法最大并发调用数</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>cluster</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>failover</td> |
| <td>性能调优</td> |
| <td>集群方式,可选:failover/failfast/failsafe/failback/forking</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>filter</td> |
| <td>reference.filter</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>default</td> |
| <td>性能调优</td> |
| <td>服务消费方远程调用过程拦截器名称,多个名称用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>listener</td> |
| <td>invoker.listener</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>default</td> |
| <td>性能调优</td> |
| <td>服务消费方引用服务监听器名称,多个名称用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>layer</td> |
| <td>layer</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务调用者所在的分层。如:biz、dao、intl:web、china:acton。</td> |
| <td>2.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>init</td> |
| <td>init</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>性能调优</td> |
| <td>是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化。</td> |
| <td>2.0.10以上版本</td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td>protocol</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>只调用指定协议的服务提供方,其它协议忽略。</td> |
| <td></td> |
| </tr> |
| </tbody> |
| </table> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/20-dubbo3-fu-wu-yin-yong-pei-zhi-referenceconfig/">20-Dubbo3服务引用配置ReferenceConfig</a></p></description></item><item><title>Blog: 19 重新来过从一个服务消费者的Demo说起</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/19/19-%E9%87%8D%E6%96%B0%E6%9D%A5%E8%BF%87%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%B6%88%E8%B4%B9%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</link><pubDate>Fri, 19 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/19/19-%E9%87%8D%E6%96%B0%E6%9D%A5%E8%BF%87%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%B6%88%E8%B4%B9%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</guid><description> |
| <h1 id="19-重新来过从一个服务消费者的demo说起">19 重新来过从一个服务消费者的Demo说起</h1> |
| <p>为了更方便了解原理,我们先来编写一个Demo,从例子中来看源码实现:,前面说了提供者现在已经有服务注册上去了,那接下来我们编写一个消费者的例子来进行服务发现与服务RPC调用。</p> |
| <h2 id="191-启动zookeeper">19.1 启动Zookeeper</h2> |
| <p>为了Demo可以正常启动,需要我们先在本地启动一个Zookeeper如下图所示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/19-zk.png" alt="在这里插入图片描述"></p> |
| <h2 id="192-服务消费者">19.2 服务消费者</h2> |
| <p>接下来给大家贴一下示例源码,这个源码来源于Dubbo源码目录的 dubbo-demo/dubbo-demo-api 目录下面的dubbo-demo-api-consumer子项目,这里我做了删减,方便看核心代码: |
| 首先我们定义一个服务接口如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">import</span> java.util.concurrent.CompletableFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">DemoService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 同步处理的服务方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 用于异步处理的服务方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture<span style="color:#719e07">.</span>completedFuture<span style="color:#719e07">(</span>sayHello<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>服务实现类如下: |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.rpc.RpcContext<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.Logger<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.LoggerFactory<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> java.util.concurrent.CompletableFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceImpl</span> <span style="color:#268bd2">implements</span> DemoService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> Logger logger <span style="color:#719e07">=</span> LoggerFactory<span style="color:#719e07">.</span>getLogger<span style="color:#719e07">(</span>DemoServiceImpl<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, request from consumer: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getServiceContext<span style="color:#719e07">().</span>getRemoteAddress<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, response from provider: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getServiceContext<span style="color:#719e07">().</span>getLocalAddress<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="193-启用服务消费者">19.3 启用服务消费者</h2> |
| <p>有了服务接口之后我们来启用服务,启用服务的源码如下: |
| 这里如果要启动消费者,主要要修改QOS端口这里我已经配置可以直接复用</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">package</span> link.elastic.dubbo.consumer<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> link.elastic.dubbo.entity.DemoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.common.constants.CommonConstants<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ApplicationConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.MetadataReportConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ProtocolConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ReferenceConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.RegistryConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.bootstrap.DubboBootstrap<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.rpc.service.GenericService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ConsumerApplication</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> runWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">runWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ReferenceConfig<span style="color:#719e07">&lt;</span>DemoService<span style="color:#719e07">&gt;</span> reference <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ReferenceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setGeneric<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-consumer&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setQosEnable<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setQosPort<span style="color:#719e07">(-</span>1<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://8.131.79.126:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>reference<span style="color:#719e07">(</span>reference<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DemoService demoService <span style="color:#719e07">=</span> bootstrap<span style="color:#719e07">.</span>getCache<span style="color:#719e07">().</span>get<span style="color:#719e07">(</span>reference<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> String message <span style="color:#719e07">=</span> demoService<span style="color:#719e07">.</span>sayHello<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span>message<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generic invoke |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> GenericService genericService <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>GenericService<span style="color:#719e07">)</span> demoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> Object genericInvokeResult <span style="color:#719e07">=</span> genericService<span style="color:#719e07">.</span>$invoke<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;sayHello&#34;</span><span style="color:#719e07">,</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[]{</span>String<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">()},</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> Object<span style="color:#719e07">[]{</span><span style="color:#2aa198">&#34;dubbo generic invoke&#34;</span><span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span>genericInvokeResult<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="14-启用服务后写入zookeeper的节点数据">1.4 启用服务后写入Zookeeper的节点数据</h2> |
| <p>启动服务,这个时候我们打开Zookeeper图形化客户端来看看这个服务在Zookeeper上面写入来哪些数据,如下图: |
| 在这里插入图片描述 |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/19-zk2.png" alt="在这里插入图片描述"></p> |
| <p>写入Zookeper上的节点用于服务在分布式场景下的协调,这些节点是比较重要的。</p> |
| <p>如果了解过Dubbo的同学,应该会知道Dubbo在低版本的时候会向注册中心中写入服务接口,具体路径在上面的 <strong>dubbo目录下</strong> ,然后在 <strong>/dubbo/服务接口/</strong> 路径下写入如下信息:</p> |
| <ul> |
| <li><strong>服务提供者</strong>配置信息URL形式</li> |
| <li><strong>服务消费者</strong>的配置信息URL形式</li> |
| <li>服务<strong>路由信息</strong></li> |
| <li><strong>配置信息</strong></li> |
| </ul> |
| <p>上面这个图就是Dubbo3的注册信息了,后面我们也会围绕细节来说明下,这里可以看下新增了:</p> |
| <ul> |
| <li>/dubbo/metadata <strong>元数据信息</strong></li> |
| <li>/dubbo/mapping 服务和应用的<strong>映射信息</strong></li> |
| <li>/dubbo/config <strong>注册中心配置</strong></li> |
| <li>/services目录<strong>应用信息</strong></li> |
| </ul> |
| <p>在这里可以大致了解下,在后面会有更详细的源码解析这个示例代码.通过透析代码来看透Dubbo3服务注册原理,服务提供原理。</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/19-chong-xin-lai-guo-cong-yi-ge-fu-wu-xiao-fei-zhe-de-demo-shuo-qi/">19-重新来过从一个服务消费者的Demo说起</a></p></description></item><item><title>Blog: 18-Dubbo3元数据服务MetadataService的导出</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/18/18-dubbo3%E5%85%83%E6%95%B0%E6%8D%AE%E6%9C%8D%E5%8A%A1metadataservice%E7%9A%84%E5%AF%BC%E5%87%BA/</link><pubDate>Thu, 18 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/18/18-dubbo3%E5%85%83%E6%95%B0%E6%8D%AE%E6%9C%8D%E5%8A%A1metadataservice%E7%9A%84%E5%AF%BC%E5%87%BA/</guid><description> |
| <h1 id="18-dubbo3元数据服务metadataservice的导出">18-Dubbo3元数据服务MetadataService的导出</h1> |
| <h2 id="181-简介">18.1 简介</h2> |
| <p>MetadataService |
| 此服务用于公开Dubbo进程内的元数据信息。典型用途包括:</p> |
| <ul> |
| <li>使用者查询提供者的元数据信息,以列出接口和每个接口的配置</li> |
| <li>控制台(dubbo admin)查询特定进程的元数据,或聚合所有进程的数据。在Dubbo2.x的时候,所有的服务数据都是以接口的形式注册在注册中心.</li> |
| </ul> |
| <p>Dubbo3将部分数据抽象为元数据的形式来将数据存放在元数据中心,然后元数据由服务提供者提供给消费者而不是再由注册中心进行推送,如下图所示:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/18-metadata.png" alt="在这里插入图片描述"></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/18-metadata3.png" alt="在这里插入图片描述"> |
| 引入 MetadataService 元数据服务服务的好处 |
| • 由中心化推送转向点对点拉取(Consumer - Proroder) |
| • 易于扩展更多的参数 |
| • 更多的数据量 |
| • 对外暴露更多的治理数据</p> |
| <h2 id="182-metadataservice的导出过程">18.2 MetadataService的导出过程</h2> |
| <p>了解元数据的到处过程,这个就要继续前面博客往后的代码了前面博客说了一个服务发布之后的服务信息的双注册数据,这里继续看下导出服务之后的代码: |
| 先来简单回顾下模块发布的启动生命周期方法:</p> |
| <p>DefaultModuleDeployer类型的start方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> Future <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> onModuleStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// initialize |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// prepare application instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// exclude internal module to avoid wait itself |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModel <span style="color:#719e07">!=</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getInternalModule<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer<span style="color:#719e07">.</span>prepareInternalModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// refer services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if no async export/refer services, just set started |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>asyncExportingFutures<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> asyncReferringFutures<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> onModuleStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">....</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面的博客我们已经说了服务提供者导出服务的方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">// export services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportServices<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>在导出服务之后如果代码中配置了引用服务的代码将会执行引用服务的功能,调用代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>referServices<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>不过我们样例代码并没有介绍引用服务的功能,这里先不说,等服务提供者完全启动成功之后我们再来看消费者的逻辑。</p> |
| <p>接下来我们要看的是模块启动成功之后的方法 onModuleStarted();,在这个方法中会去发布服务元数据信息。</p> |
| <h2 id="183-模块启动成功时候的逻辑-onmodulestarted">18.3 模块启动成功时候的逻辑 onModuleStarted();</h2> |
| <p>这里我们直接先看代码再来分析下逻辑:</p> |
| <p>DefaultModuleDeployer类型的onModuleStarted方法如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onModuleStarted</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//状态判断是否为启动中如果是则将状态设置为STARTED |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStarting<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//先修改状态 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has started.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//状态修改成功之后开始通知应用程序发布器模块发布器启动成功了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>notifyModuleChanged<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">,</span> DeployState<span style="color:#719e07">.</span>STARTED<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// complete module start future after application state changed |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> completeStartFuture<span style="color:#719e07">(</span><span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>应用程序发布器处理启动成功的逻辑: |
| DefaultApplicationDeployer类型的notifyModuleChanged方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">notifyModuleChanged</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">,</span> DeployState state<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据所有模块的状态来判断应用发布器的状态 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkState<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">,</span> state<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// notify module state changed or module changed |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//通知所有模块状态更新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>stateLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> stateLock<span style="color:#719e07">.</span>notifyAll<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>应用发布器模型DefaultApplicationDeployer检查状态方法checkState代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">checkState</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">,</span> DeployState moduleState<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在写操作 先加个锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>stateLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//非内部模块,并且模块的状态是发布成功了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>moduleModel<span style="color:#719e07">.</span>isInternal<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> moduleState <span style="color:#719e07">==</span> DeployState<span style="color:#719e07">.</span>STARTED<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> prepareApplicationInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用下所有模块状态进行汇总计算 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DeployState newState <span style="color:#719e07">=</span> calculateState<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">switch</span> <span style="color:#719e07">(</span>newState<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STARTED<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> onStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STARTING<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> onStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STOPPING<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> onStopping<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STOPPED<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> onStopped<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> FAILED<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> Throwable error <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> ModuleModel errorModule <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ModuleModel module <span style="color:#719e07">:</span> applicationModel<span style="color:#719e07">.</span>getModuleModels<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ModuleDeployer deployer <span style="color:#719e07">=</span> module<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>deployer<span style="color:#719e07">.</span>isFailed<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> deployer<span style="color:#719e07">.</span>getError<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> error <span style="color:#719e07">=</span> deployer<span style="color:#719e07">.</span>getError<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> errorModule <span style="color:#719e07">=</span> module<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> onFailed<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; found failed module: &#34;</span> <span style="color:#719e07">+</span> errorModule<span style="color:#719e07">.</span>getDesc<span style="color:#719e07">(),</span> error<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> PENDING<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// cannot change to pending from other state |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// setPending(); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="184-准备发布元数据信息和应用实例信息">18.4 准备发布元数据信息和应用实例信息</h2> |
| <p>前面有个代码调用比较重要:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>prepareApplicationInstance<span style="color:#719e07">()</span> |
| </span></span></code></pre></div><p>DefaultApplicationDeployer类型的prepareApplicationInstance方法如下所示</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">prepareApplicationInstance</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//已经注册过应用实例数据了 直接返回 (下面CAS逻辑判断了) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>hasPreparedApplicationInstance<span style="color:#719e07">.</span>get<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册开关控制默认为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//通过将registerConsumer默认设置为“false”来关闭纯使用者进程实例的注册。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isRegisterConsumerInstance<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportMetadataService<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>hasPreparedApplicationInstance<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register the local ServiceInstance if required |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerServiceInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1841-导出元数据服务方法exportmetadataservice">18.4.1 导出元数据服务方法exportMetadataService</h3> |
| <p>这里我们就先直接来贴一下代码:</p> |
| <p>DefaultApplicationDeployer类型的exportMetadataService方法如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportMetadataService</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isStarting<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里监听器我们主要关注的类型是ExporterDeployListener类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>DeployListener<span style="color:#719e07">&lt;</span>ApplicationModel<span style="color:#719e07">&gt;</span> listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>listener <span style="color:#719e07">instanceof</span> ApplicationDeployListener<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 回调监听器的模块启动成功方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">((</span>ApplicationDeployListener<span style="color:#719e07">)</span> listener<span style="color:#719e07">).</span>onModuleStarted<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; an exception occurred when handle starting event&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面我们主要关注ExporterDeployListener类型的监听器的回调方法,这里我贴一下代码: |
| ExporterDeployListener类型的onModuleStarted方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onModuleStarted</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// start metadata service exporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//MetadataServiceDelegation类型为实现提供远程RPC服务以方便元数据信息的查询功能的类型。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataServiceDelegation metadataService <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getOrRegisterBean<span style="color:#719e07">(</span>MetadataServiceDelegation<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataServiceExporter <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metadataServiceExporter <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConfigurableMetadataServiceExporter<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> metadataService<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fixme, let&#39;s disable local metadata service export at this moment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//默认我们是没有配置这个元数据类型的这里元数据类型默认为local 条件是不是remote则开始导出,在前面的博客&lt;&lt;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&gt;&gt; 中有提到这个配置下面我再说下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>REMOTE_METADATA_STORAGE_TYPE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>getMetadataType<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metadataServiceExporter<span style="color:#719e07">.</span>export<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在前面的博客<a href="https://blog.elastic.link/2022/07/10/dubbo/9-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-ying-yong-cheng-xu-de-pei-zhi-xin-xi-applicationconfig/">&laquo;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&raquo;</a> 中有提到这个配置下面我再说下</p> |
| <p>metadata-type</p> |
| <p>metadata 传递方式,是以 Provider 视角而言的,Consumer 侧配置无效,可选值有:</p> |
| <ul> |
| <li>remote - Provider 把 metadata 放到远端<strong>注册中心</strong>,Consumer 从<strong>注册中心获取</strong>。</li> |
| <li>local - Provider <strong>把 metadata 放在本地</strong>,<strong>Consumer 从 Provider 处直接获取</strong> 。</li> |
| </ul> |
| <p>可以看到默认的local配置元数据信息的获取是由消费者从提供者拉的,那提供者怎么拉取对应服务的元数据信息那就要要用到这个博客说到的MetadataService服务,传递方式为remote的方式其实就要依赖注册中心了相对来说增加了注册中心的压力。</p> |
| <h3 id="1842-可配置元数据服务的导出configurablemetadataserviceexporter的export">18.4.2 可配置元数据服务的导出ConfigurableMetadataServiceExporter的export</h3> |
| <p>前面了解了导出服务的调用链路,这里详细看下ConfigurableMetadataServiceExporter的export过程源码如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> ConfigurableMetadataServiceExporter <span style="color:#268bd2">export</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据服务配置已经存在或者已经导出或者不可导出情况下是无需导出的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>serviceConfig <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> <span style="color:#719e07">!</span>isExported<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceConfig <span style="color:#719e07">=</span> buildServiceConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//导出服务 ,导出服务的具体过程这里就不再说了可以看上一个博客,这个导出服务的过程会绑定端口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>export<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> metadataService<span style="color:#719e07">.</span>setMetadataURL<span style="color:#719e07">(</span>serviceConfig<span style="color:#719e07">.</span>getExportedUrls<span style="color:#719e07">().</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The MetadataService exports urls : &#34;</span> <span style="color:#719e07">+</span> serviceConfig<span style="color:#719e07">.</span>getExportedUrls<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isWarnEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The MetadataService has been exported : &#34;</span> <span style="color:#719e07">+</span> serviceConfig<span style="color:#719e07">.</span>getExportedUrls<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1843-元数据服务配置对象的创建">18.4.3 元数据服务配置对象的创建</h3> |
| <p>前面我们看到了构建元数据服务对象的代码调用ServiceConfig<MetadataService>,接下来我们详细看下构建源码如下所示: |
| ConfigurableMetadataServiceExporter类型的buildServiceConfig构建元数据服务配置对象方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> ServiceConfig<span style="color:#719e07">&lt;</span>MetadataService<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">buildServiceConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//1 获取当前的应用配置 然后初始化应用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> getApplicationConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceConfig<span style="color:#719e07">&lt;</span>MetadataService<span style="color:#719e07">&gt;</span> serviceConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置域模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getInternalModule<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setApplication<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//2 创建注册中心配置对象 然后并初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> RegistryConfig registryConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;N/A&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setId<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;internal-metadata-registry&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//3 创建服务配置对象,并初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setRegistry<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setRegister<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//4 生成协议配置 ,这里会配置一下元数据使用的服务端口号默认使用其他服务的端口20880 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>generateMetadataProtocol<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>MetadataService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setDelay<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里也是需要注意的地方服务引用的类型为MetadataServiceDelegation |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span>metadataService<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setGroup<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">.</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setVersion<span style="color:#719e07">(</span>MetadataService<span style="color:#719e07">.</span>VERSION<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//5 生成方法配置 这里目前提供的服务方法为getAndListenInstanceMetadata方法 后续可以看下这个方法的视线 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setMethods<span style="color:#719e07">(</span>generateMethodConfig<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setConnections<span style="color:#719e07">(</span>1<span style="color:#719e07">);</span> <span style="color:#586e75">// separate connection |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setExecutes<span style="color:#719e07">(</span>100<span style="color:#719e07">);</span> <span style="color:#586e75">// max tasks running at the same time |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> serviceConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个服务配置对象的创建非常像我们第一个博客提到的服务配置过程,不过这个元数据服务对象有几个比较特殊的配置</p> |
| <ul> |
| <li>注册中心的配置register设置为了false 则为不向注册中心注册具体的服务配置信息</li> |
| <li>对每个提供者的最大连接数connections为1</li> |
| <li>服务提供者每服务每方法最大可并行执行请求数executes为100</li> |
| </ul> |
| <p>在使用过程中可以知道上面这几个配置值</p> |
| <h2 id="185-应用级数据注册---registerserviceinstance">18.5 应用级数据注册 registerServiceInstance()</h2> |
| <p>在前面导出元数据服务之后也会调用一行代码来注册应用级数据来保证应用上线</p> |
| <p>主要涉及到的代码为DefaultApplicationDeployer类型中的registerServiceInstance方法如下所示</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerServiceInstance</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//标记变量设置为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registered <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> ServiceInstanceMetadataUtils<span style="color:#719e07">.</span>registerMetadataAndInstance<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Register instance error&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registered<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// scheduled task for updating Metadata and ServiceInstance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> asyncMetadataFuture <span style="color:#719e07">=</span> frameworkExecutorRepository<span style="color:#719e07">.</span>getSharedScheduledExecutor<span style="color:#719e07">().</span>scheduleWithFixedDelay<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore refresh metadata on stopping |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>applicationModel<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> registered<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ServiceInstanceMetadataUtils<span style="color:#719e07">.</span>refreshMetadataAndInstance<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>applicationModel<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Refresh instance and metadata error&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> 0<span style="color:#719e07">,</span> ConfigurationUtils<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> METADATA_PUBLISH_DELAY_KEY<span style="color:#719e07">,</span> DEFAULT_METADATA_PUBLISH_DELAY<span style="color:#719e07">),</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法先将应用元数据注册到注册中心,然后开始开启定时器每隔30秒同步一次元数据向注册中心。</p> |
| <h3 id="1851-服务实例元数据工具类注册服务发现的元数据信息">18.5.1 服务实例元数据工具类注册服务发现的元数据信息</h3> |
| <p>前面通过调用类型ServiceInstanceMetadataUtils工具类的registerMetadataAndInstance方法来进行服务实例数据和元数据的注册这里我们详细看下代码如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerMetadataAndInstance</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Start registering instance address to registry.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> RegistryManager registryManager <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>RegistryManager<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register service instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">//注意这里服务发现的类型只有ServiceDiscoveryRegistry类型的注册协议才满足 registryManager.getServiceDiscoveries().forEach(ServiceDiscovery::register); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1852-abstractservicediscovery中的服务发现数据注册的模版方法">18.5.2 AbstractServiceDiscovery中的服务发现数据注册的模版方法</h3> |
| <p>AbstractServiceDiscovery类型的注册方法register()方法这个是一个模版方法,真正执行的注册逻辑封装在了doRegister方法中由扩展的服务发现子类来完成</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">register</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> RuntimeException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一步创建应用的实例信息等待下面注册到注册中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance <span style="color:#719e07">=</span> createServiceInstance<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isValidInstance<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No valid instance found, stop registering instance address to registry.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否需要更新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> revisionUpdated <span style="color:#719e07">=</span> calOrUpdateInstanceRevision<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>revisionUpdated<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportMetadata<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用的实例信息注册到注册中心之上 ,这个 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doRegister<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1853-应用级实例对象创建">18.5.3 应用级实例对象创建</h3> |
| <p>可以看到在AbstractServiceDiscovery服务发现的第一步创建应用的实例信息等待下面注册到注册中心</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance <span style="color:#719e07">=</span> createServiceInstance<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataInfo<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>最终创建的serviceInstance类型为ServiceInstance 这个是Dubbo封装的一个接口,具体实现类型为DefaultServiceInstance,我们可以看下应用级的元数据有哪些</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> ServiceInstance <span style="color:#268bd2">createServiceInstance</span><span style="color:#719e07">(</span>MetadataInfo metadataInfo<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里的服务名字为:dubbo-demo-api-provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DefaultServiceInstance instance <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DefaultServiceInstance<span style="color:#719e07">(</span>serviceName<span style="color:#719e07">,</span> applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用服务的元数据 ,可以看下面debug的数据信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance<span style="color:#719e07">.</span>setServiceMetadata<span style="color:#719e07">(</span>metadataInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//metadataType的值为local 这个方法是将元数据类型存储到英勇的元数据对象中 对应内容为dubbo.metadata.storage-type:local |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setMetadataStorageType<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> metadataType<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 这个是自定义元数据数据 我们也可以通过实现扩展ServiceInstanceCustomizer来自定义一些元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceInstanceMetadataUtils<span style="color:#719e07">.</span>customizeInstance<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法的主要目的就是将应用的元数据信息都封装到ServiceInstance类型中,不过额外提供了一个扩展性比较好的方法可以自定义元数据信息</p> |
| <p>前面的metadataInfo对象的信息如下图所示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/18-metadata2.png" alt="在这里插入图片描述"></p> |
| <p>自定义元数据类型Dubbo官方提供了一个默认的实现类型为:ServiceInstanceMetadataCustomizer</p> |
| <p>最终封装好的元数据信息如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>DefaultServiceInstance<span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span>serviceName<span style="color:#719e07">=</span>&#39;dubbo<span style="color:#719e07">-</span>demo<span style="color:#719e07">-</span>api<span style="color:#719e07">-</span>provider&#39;<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span>host<span style="color:#719e07">=</span>&#39;192<span style="color:#719e07">.</span>168<span style="color:#719e07">.</span>1<span style="color:#719e07">.</span>169&#39;<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span>port<span style="color:#719e07">=</span>20880<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span>enabled<span style="color:#719e07">=</span><span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span>healthy<span style="color:#719e07">=</span><span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> metadata<span style="color:#719e07">={</span> |
| </span></span><span style="display:flex;"><span> dubbo<span style="color:#719e07">.</span>metadata<span style="color:#719e07">-</span>service<span style="color:#719e07">.</span>url<span style="color:#719e07">-</span>params<span style="color:#719e07">={</span><span style="color:#2aa198">&#34;connections&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;1&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;version&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;1.0.0&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;2.0.2&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;release&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;3.0.9&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;side&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;provider&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;port&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;20880&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;protocol&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;dubbo&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> |
| </span></span><span style="display:flex;"><span> dubbo<span style="color:#719e07">.</span>endpoints<span style="color:#719e07">=[</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span><span style="color:#2aa198">&#34;port&#34;</span><span style="color:#719e07">:</span>20880<span style="color:#719e07">,</span><span style="color:#2aa198">&#34;protocol&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#719e07">}],</span> |
| </span></span><span style="display:flex;"><span> dubbo<span style="color:#719e07">.</span>metadata<span style="color:#719e07">.</span>storage<span style="color:#719e07">-</span>type<span style="color:#719e07">=</span>local<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> timestamp<span style="color:#719e07">=</span>1656227493387<span style="color:#719e07">}}</span> |
| </span></span></code></pre></div><h3 id="1854-应用级实例数据配置变更的的版本号获取">18.5.4 应用级实例数据配置变更的的版本号获取</h3> |
| <p>前面创建元应用的实例信息后开始创建版本号来判断是否需要更新,对应AbstractServiceDiscovery类型的calOrUpdateInstanceRevision</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">calOrUpdateInstanceRevision</span><span style="color:#719e07">(</span>ServiceInstance instance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取元数据版本号对应字段dubbo.metadata.revision |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String existingInstanceRevision <span style="color:#719e07">=</span> getExportedServicesRevision<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取实例的服务元数据信息:metadata{app=&#39;dubbo-demo-api-provider&#39;,revision=&#39;null&#39;,size=1,services=[link.elastic.dubbo.entity.DemoService:dubbo]} |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataInfo metadataInfo <span style="color:#719e07">=</span> instance<span style="color:#719e07">.</span>getServiceMetadata<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//必须在不同线程之间同步计算此实例的状态,如同一实例的修订和修改。此方法的使用仅限于某些点,例如在注册期间。始终尝试使用此选项。改为getRevision()。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String newRevision <span style="color:#719e07">=</span> metadataInfo<span style="color:#719e07">.</span>calAndGetRevision<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//版本号发生了变更(元数据发生了变更)版本号是md5元数据信息计算出来HASH验证 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>newRevision<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>existingInstanceRevision<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//版本号添加到dubbo.metadata.revision字段中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance<span style="color:#719e07">.</span>getMetadata<span style="color:#719e07">().</span>put<span style="color:#719e07">(</span>EXPORTED_SERVICES_REVISION_PROPERTY_NAME<span style="color:#719e07">,</span> metadataInfo<span style="color:#719e07">.</span>getRevision<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="18541-元数据版本号的计算与hash校验-calandgetrevision">18.5.4.1 元数据版本号的计算与HASH校验 calAndGetRevision</h4> |
| <p>这个方法其实比较重要,决定了什么时候会更新元数据,Dubbo使用了一种Hash验证的方式将元数据转MD5值与之前的存在的版本号(也是元数据转MD5得到的) 如果数据发生了变更则MD5值会发生变化 以此来更新元数据,不过发生了MD5冲突的话就会导致配置不更新这个冲突的概率非常小。 |
| 好了直接来看代码吧: |
| MetadataInfo类型的calAndGetRevision方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> String <span style="color:#268bd2">calAndGetRevision</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>revision <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>updated<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> revision<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> updated <span style="color:#719e07">=</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用下没有服务则使用一个空的版本号 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmptyMap<span style="color:#719e07">(</span>services<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>revision <span style="color:#719e07">=</span> EMPTY_REVISION<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> StringBuilder sb <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> StringBuilder<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//app是应用名 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> sb<span style="color:#719e07">.</span>append<span style="color:#719e07">(</span>app<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> ServiceInfo<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> <span style="color:#719e07">new</span> TreeMap<span style="color:#719e07">&lt;&gt;(</span>services<span style="color:#719e07">).</span>entrySet<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> sb<span style="color:#719e07">.</span>append<span style="color:#719e07">(</span>entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">().</span>toDescString<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> String tempRevision <span style="color:#719e07">=</span> RevisionResolver<span style="color:#719e07">.</span>calRevision<span style="color:#719e07">(</span>sb<span style="color:#719e07">.</span>toString<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>StringUtils<span style="color:#719e07">.</span>isEquals<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>revision<span style="color:#719e07">,</span> tempRevision<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据重新注册的话我们可以看看这个日志metadata revision change |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;metadata revision changed: %s -&gt; %s, app: %s, services: %d&#34;</span><span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>revision<span style="color:#719e07">,</span> tempRevision<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>app<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>services<span style="color:#719e07">.</span>size<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>revision <span style="color:#719e07">=</span> tempRevision<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>rawMetadataInfo <span style="color:#719e07">=</span> JsonUtils<span style="color:#719e07">.</span>getJson<span style="color:#719e07">().</span>toJson<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> revision<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>RevisionResolver类型的Md5运算计算版本号</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>md5Utils<span style="color:#719e07">.</span>getMd5<span style="color:#719e07">(</span>metadata<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><h3 id="1855-reportmetadata">18.5.5 reportMetadata</h3> |
| <p>回到18.5.2 AbstractServiceDiscovery中的模版方法register,这里我们来看下reportMetadata方法,不过这个方法目前并不会走到,因为我们默认的配置元数据是local不会直接把应用的元数据注册在元数据中心</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">reportMetadata</span><span style="color:#719e07">(</span>MetadataInfo metadataInfo<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//订阅元数据的标识符 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> SubscriberMetadataIdentifier identifier <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SubscriberMetadataIdentifier<span style="color:#719e07">(</span>serviceName<span style="color:#719e07">,</span> metadataInfo<span style="color:#719e07">.</span>getRevision<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否远程发布元数据,这里我们是本地注册这个就不会在元数据中心发布这个元数据信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>DEFAULT_METADATA_STORAGE_TYPE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>metadataType<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> metadataReport<span style="color:#719e07">.</span>shouldReportMetadata<span style="color:#719e07">())</span> <span style="color:#719e07">||</span> REMOTE_METADATA_STORAGE_TYPE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>metadataType<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metadataReport<span style="color:#719e07">.</span>publishAppMetadata<span style="color:#719e07">(</span>identifier<span style="color:#719e07">,</span> metadataInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1856-扩展的注册中心来注册应用级服务发现数据doregister方法">18.5.6 扩展的注册中心来注册应用级服务发现数据doRegister方法</h3> |
| <p>前面我们说了AbstractServiceDiscovery中的模版方法register,在register会调用一个doRegister方法来注册应用级数据,这个方法是需要扩展注册中心的服务发现来自行实现的,我们这里以官方实现的Zookeeper服务发现模型为例:</p> |
| <p>ZookeeperServiceDiscovery中的doRegister方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doRegister</span><span style="color:#719e07">(</span>ServiceInstance serviceInstance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceDiscovery<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>build<span style="color:#719e07">(</span>serviceInstance<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException<span style="color:#719e07">(</span>REGISTRY_EXCEPTION<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;Failed register instance &#34;</span> <span style="color:#719e07">+</span> serviceInstance<span style="color:#719e07">.</span>toString<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面我们介绍了ZookeeperServiceDiscovery发现的构造器连接注册中心,这里来看下服务注册, |
| 应用级实例数据注册一共分为两步 |
| 第一步是:Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance |
| 第二步是:执行registerService方法将数据注册到注册中心</p> |
| <p>先来看第一步:Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance |
| 关于Curator的服务发现原理可以参考官网的文章博客<a href="https://curator.apache.org/docs/service-discovery/index.html">curator-x-discovery</a></p> |
| <p><strong>什么是发现服务?</strong> |
| 在 SOA/分布式系统中,服务需要找到彼此。即,Web 服务可能需要找到缓存服务等。DNS 可以用于此,但对于不断变化的服务来说,它远不够灵活。服务发现系统提供了一种机制:</p> |
| <ul> |
| <li>注册其可用性的服务</li> |
| <li>定位特定服务的单个实例</li> |
| <li>在服务实例更改时通知</li> |
| </ul> |
| <p>服务实例由类表示:ServiceInstance。ServiceInstances 具有名称、id、地址、端口和/或 ssl 端口,以及可选的有效负载(用户定义)。ServiceInstances 通过以下方式序列化并存储在 ZooKeeper 中:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>base path |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>_______ service A name |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 1 id <span style="color:#719e07">--&gt;</span> <span style="color:#719e07">(</span>serialized ServiceInstance<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 2 id <span style="color:#719e07">--&gt;</span> <span style="color:#719e07">(</span>serialized ServiceInstance<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>_______ service B name |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 1 id <span style="color:#719e07">--&gt;</span> <span style="color:#719e07">(</span>serialized ServiceInstance<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 2 id <span style="color:#719e07">--&gt;</span> <span style="color:#719e07">(</span>serialized ServiceInstance<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>_______ <span style="color:#719e07">...</span> |
| </span></span></code></pre></div><p>这个应用最终注册应用级服务数据如下: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/18-metadata4.png" alt="在这里插入图片描述"> |
| 这里需要注意的是这个 应用的IP+端口的服务元数据信息是临时节点 |
| build方法内容对应着上图的JSON数据 可以看菜build方法封装的过程:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>curator<span style="color:#719e07">.</span>x<span style="color:#719e07">.</span>discovery<span style="color:#719e07">.</span>ServiceInstance<span style="color:#719e07">&lt;</span>ZookeeperInstance<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">build</span><span style="color:#719e07">(</span>ServiceInstance serviceInstance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ServiceInstanceBuilder builder<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String serviceName <span style="color:#719e07">=</span> serviceInstance<span style="color:#719e07">.</span>getServiceName<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String host <span style="color:#719e07">=</span> serviceInstance<span style="color:#719e07">.</span>getHost<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> port <span style="color:#719e07">=</span> serviceInstance<span style="color:#719e07">.</span>getPort<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> metadata <span style="color:#719e07">=</span> serviceInstance<span style="color:#719e07">.</span>getSortedMetadata<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String id <span style="color:#719e07">=</span> generateId<span style="color:#719e07">(</span>host<span style="color:#719e07">,</span> port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ZookeeperInstance是Dubbo封装的用于存放payload数据 包含服务id,服务名字和元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ZookeeperInstance zookeeperInstance <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ZookeeperInstance<span style="color:#719e07">(</span>id<span style="color:#719e07">,</span> serviceName<span style="color:#719e07">,</span> metadata<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> builder <span style="color:#719e07">=</span> builder<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>id<span style="color:#719e07">(</span>id<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>name<span style="color:#719e07">(</span>serviceName<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>address<span style="color:#719e07">(</span>host<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>port<span style="color:#719e07">(</span>port<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>payload<span style="color:#719e07">(</span>zookeeperInstance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RuntimeException<span style="color:#719e07">(</span>e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> builder<span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在《18.5 应用级数据注册 registerServiceInstance() 》 小节中介绍了应用元数据信息的注册调用代码,其实后面还有个update的逻辑定期30秒同步元数据到元数据中心,这里就不详细介绍了。</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/18-dubbo3-yuan-shu-ju-fu-wu-metadataservice-de-dao-chu/">18-Dubbo3元数据服务MetadataService的导出</a></p></description></item><item><title>Blog: 17-Dubbo服务提供者的双注册原理</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/17/17-dubbo%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E8%80%85%E7%9A%84%E5%8F%8C%E6%B3%A8%E5%86%8C%E5%8E%9F%E7%90%86/</link><pubDate>Wed, 17 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/17/17-dubbo%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E8%80%85%E7%9A%84%E5%8F%8C%E6%B3%A8%E5%86%8C%E5%8E%9F%E7%90%86/</guid><description> |
| <h1 id="17-dubbo服务提供者的双注册原理">17-Dubbo服务提供者的双注册原理</h1> |
| <h2 id="171-简介">17.1 简介</h2> |
| <p>上个博客<a href="https://blog.elastic.link/2022/07/10/dubbo/15-dubbo-de-san-da-zhong-xin-zhi-yuan-shu-ju-zhong-xin-yuan-ma-jie-xi/">《15-Dubbo的三大中心之元数据中心源码解析》</a>导出服务端的时候多次提到了元数据中心,注册信息的注册。 |
| Dubbo3出来时间不太长,对于现在的用户来说大部分使用的仍旧是Dubbo2.x, |
| Dubbo3 比较有特色也是会直接使用到的功能就是<strong>应用级服务发现</strong>:</p> |
| <ul> |
| <li>应用级服务发现 |
| <em>从服务/接口粒度到应用粒度的升级,使得 Dubbo 在集群可伸缩性、连接异构微服务体系上更具优势。应用粒度能以更低的资源消耗支持超百万实例规模集群程; 实现与 Spring Cloud、Kubernetes Service 等异构微服务体系的互联互通。</em></li> |
| </ul> |
| <p>对于直接使用Dubbo3的用户还好,可以仅仅开启应用级注册,但是对于Dubbo2.x的用户升级到Dubbo3的用户来说前期都是要开启双注册来慢慢迁移的,既注册传统的接口信息到注册中心,又注册应用信息到注册中心,同时注册应用与接口关系的元数据信息。 |
| 关于双注册与服务迁移的过程的使用可以参考官网: |
| <a href="https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/">应用级地址发现迁移指南</a></p> |
| <p>关于官网提供者双注册的图我这里贴一下,方便了解: |
| <img src="https://dubbo.apache.org/imgs/v3/migration/provider-registration.png" alt="在这里插入图片描述"></p> |
| <h2 id="172-双注册配置的读取">17.2 双注册配置的读取</h2> |
| <h3 id="1721-注册中心地址作为元数据中心">17.2.1 注册中心地址作为元数据中心</h3> |
| <p>这个配置的解析过程在前面的博客介绍元数据中心的时候很详细的说了相关链接:<a href="https://blog.elastic.link/2022/07/10/dubbo/15-dubbo-de-san-da-zhong-xin-zhi-yuan-shu-ju-zhong-xin-yuan-ma-jie-xi/">15-Dubbo的三大中心之元数据中心源码解析</a></p> |
| <p>对应代码位于:DefaultApplicationDeployer类型的startMetadataCenter()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startMetadataCenter</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> useRegistryAsMetadataCenterIfNecessary<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...省略掉其他代码防止受到干扰 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>具体逻辑是这个方法: |
| useRegistryAsMetadataCenterIfNecessary</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">useRegistryAsMetadataCenterIfNecessary</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置缓存中查询元数据配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataConfigs <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getMetadataConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...省略掉空判断 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> defaultRegistries <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getDefaultRegistries<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultRegistries<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多注册中心遍历 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultRegistries |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>stream<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>filter<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>isUsedRegistryAsMetadataCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心配置映射为元数据中心 映射就是获取需要的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>registryAsMetadataCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将元数据中心配置存储在配置缓存中方便后续使用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>metadataReportConfig <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...省略掉具体的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>关于元数据中心地址的获取,主要经过如下逻辑:</p> |
| <ul> |
| <li><strong>查询:</strong> 所有可用的默认注册中心列表</li> |
| <li><strong>遍历:</strong> 多注册中心遍历</li> |
| <li><strong>筛选:</strong> 选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)</li> |
| <li><strong>转化:</strong> 注册中心配置RegistryConfig映射转换为元数据中心配置类型MetadataReportConfig</li> |
| </ul> |
| <p>MetadataReportConfig 映射就是获取需要的配置。</p> |
| <p>最后会把查询到的元数据中心配置存储在配置缓存中方便后续使用。</p> |
| <h3 id="1722-双注册模式配置">17.2.2 双注册模式配置</h3> |
| <p>双注册配置类型是这个</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>dubbo<span style="color:#719e07">.</span>application<span style="color:#719e07">.</span>register<span style="color:#719e07">-</span>mode<span style="color:#719e07">=</span>all |
| </span></span></code></pre></div><p>默认值为all代表应用级注册和接口级注册,当前在完全迁移到应用级注册之后可以将服务直接迁移到应用级配置上去。 |
| 配置值解释:</p> |
| <ul> |
| <li>all 双注册</li> |
| <li>instance 应用级注册</li> |
| <li>interface 接口级注册</li> |
| </ul> |
| <p>后面的代码如果想要看更详细的代码可以看博客<a href="https://blog.elastic.link/2022/07/10/dubbo/16-mo-kuai-fa-bu-qi-fa-bu-fu-wu-quan-guo-cheng/">《16-模块发布器发布服务全过程》</a> |
| 关于这个配置的使用我们详细来看下,在Dubbo服务注册时候会先通过此配置查询需要注册服务地址,具体代码位于ServiceConfig的doExportUrls()方法中:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrls</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//省略掉前面的代码... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs <span style="color:#719e07">=</span> ConfigValidationUtils<span style="color:#719e07">.</span>loadRegistries<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//省略掉后面的代码... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>然后就是具体注册中心地址的获取过程我们看下: |
| ConfigValidationUtils的加载注册中心地址方法loadRegistries</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">loadRegistries</span><span style="color:#719e07">(</span>AbstractInterfaceConfig interfaceConfig<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// check &amp;&amp; override if necessary |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//省略掉前面的代码... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里会获取到一个接口配置注册地址例如:registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> registries <span style="color:#719e07">=</span> interfaceConfig<span style="color:#719e07">.</span>getRegistries<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//省略掉中间的的代码... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> genCompatibleRegistries<span style="color:#719e07">(</span>interfaceConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">(),</span> registryList<span style="color:#719e07">,</span> provider<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigValidationUtils的双注册地址的获取genCompatibleRegistries方法. |
| 前面代码获取到了一个注册中心地址列表例如:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><p>下面可以看下如果根据配置来转换为应用级注册地址+接口级注册地址</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">genCompatibleRegistries</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryList<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> result <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>registryList<span style="color:#719e07">.</span>size<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> registryList<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>registryURL <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// for registries enabled service discovery, automatically register interface compatible addresses. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String registerMode<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为了更好理解这里简化掉服务发现注册地址配置的逻辑判断过程仅仅看当前例子提供的值走的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双注册模式配置查询 对应参数为dubbo.application.register-mode 默认值为all |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerMode <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_MODE_KEY<span style="color:#719e07">,</span> ConfigurationUtils<span style="color:#719e07">.</span>getCachedDynamicProperty<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> DUBBO_REGISTER_MODE_DEFAULT_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果用户配置了一个错误的注册模式配置则只走接口级配置 这里默认值为interface |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isValidRegisterMode<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> DEFAULT_REGISTER_MODE_INTERFACE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个逻辑是满足应用级注册就添加一个应用级注册的地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>DEFAULT_REGISTER_MODE_INSTANCE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> registryNotExists<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">,</span> registryList<span style="color:#719e07">,</span> SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> URL serviceDiscoveryRegistryURL <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_TYPE_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>serviceDiscoveryRegistryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个逻辑是满足接口级注册配置就添加一个接口级注册地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>DEFAULT_REGISTER_MODE_INTERFACE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//省略掉若干代码和括号 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看到这里简化的配置比较容易理解了</p> |
| <ul> |
| <li>双注册模式配置查询 对应参数为dubbo.application.register-mode ,默认值为all</li> |
| <li>如果用户配置了一个错误的注册模式配置则只走接口级配置 这里默认值为interface</li> |
| <li>满足应用级注册就添加一个应用级注册的地址</li> |
| <li>满足接口级注册配置就添加一个接口级注册地址</li> |
| </ul> |
| <p>这个方法是根据服务注册模式来判断使用接口级注册地址还是应用级注册地址分别如下所示: |
| 配置信息: |
| dubbo.application.register-mode |
| 配置值:</p> |
| <ul> |
| <li>interface |
| <ul> |
| <li>接口级注册</li> |
| </ul> |
| </li> |
| <li>instance |
| <ul> |
| <li>应用级注册</li> |
| </ul> |
| </li> |
| <li>all |
| <ul> |
| <li>接口级别和应用级都注册</li> |
| </ul> |
| </li> |
| </ul> |
| <p>最终的注册地址配置如下: |
| 接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><p>应用级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><h2 id="173-双注册服务数据的注册">17.3 双注册服务数据的注册</h2> |
| <h3 id="1731-双注册代码逻辑调用简介">17.3.1 双注册代码逻辑调用简介</h3> |
| <p>前面说了这个注册服务的配置地址会由Dubbo内部进行判断如果判断是all的话会自动将一个配置的注册地址转变为两个一个是传统的接口级注册,一个是应用级注册使用的配置地址</p> |
| <p>然后我们先看注册中心,注册服务数据的源码 |
| 如果想要查看源码细节可以在RegistryProtocol类型的export(final Invoker<T> originInvoker) 方法的如下代码位置打断点:</p> |
| <p>RegistryProtocol的export方法的注册中心注册数据代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">// url to registry 注册服务对外的接口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果url为service-discovery-registry发现则这个实现类型为ServiceDiscoveryRegistry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务发现的提供者url: dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=19559&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1654938441023 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> URL registeredProviderUrl <span style="color:#719e07">=</span> getUrlToRegistry<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// decide if we need to delay publish (provider itself and registry should both need to register) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//register参数是否 注册数据到注册中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> register <span style="color:#719e07">=</span> providerUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> registryUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>register<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里有两种情况 接口级注册会将接口级服务提供者数据直接注册到Zookeper上面,服务发现(应用级注册)这里仅仅会将注册数据转换为服务元数据等后面来发布元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> register<span style="color:#719e07">(</span>registry<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register stated url on provider model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//向提供者模型注册提供者配置ProviderModel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerStatedUrl<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">,</span> register<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> exporter<span style="color:#719e07">.</span>setRegisterUrl<span style="color:#719e07">(</span>registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exporter<span style="color:#719e07">.</span>setSubscribeUrl<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>registry<span style="color:#719e07">.</span>isServiceDiscovery<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Deprecated! Subscribe to override rules in 2.6.x or before. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registry<span style="color:#719e07">.</span>subscribe<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">,</span> overrideSubscribeListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在上个博客中我们整体说了下服务注册时候的一个流程,关于数据向注册中心的注册细节这里可以详细看下</p> |
| <h3 id="1732--注册中心领域对象的初始化">17.3.2 注册中心领域对象的初始化</h3> |
| <p>前面的代码使用url来获取注册中心操作对象如下调用代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// url to registry 注册服务对外的接口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>对应代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> Registry <span style="color:#268bd2">getRegistry</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> URL registryUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为两步先获取注册中心工厂对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> RegistryFactory registryFactory <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>RegistryFactory<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用注册中心工厂对象获取注册中心操作对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> registryFactory<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>关于参数URL有两个在前面已经说过,url信息如下:</p> |
| <p>接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><p>应用级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><p>注册中心工厂对象与注册中心操作对象的获取与执行我们通过Debug来看比较麻烦,这里涉及到很多扩展机制动态生成的代码我们无法看到,这里我直接来贴一下比较关键的一些类型,以Zookeeper注册中心来举例子:</p> |
| <p>先来看下注册工厂相关的类型: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register.png" alt="在这里插入图片描述"></p> |
| <ul> |
| <li>RegistryFactory 注册中心对象获取</li> |
| <li>AbstractRegistryFactory 模板类型封装注册中心对象获取的基本逻辑,比如缓存和基本的逻辑判断</li> |
| <li>ServiceDiscoveryRegistryFactory 用于创建服务发现注册中心工厂对象 用于创建ServiceDiscoveryRegistry对象</li> |
| <li>ZookeeperRegistryFactory 用于创建ZookeeperRegistry类型对象</li> |
| <li>NacosRegistryFactory Nacos注册中心工厂对象 用于创建NacosRegistry</li> |
| </ul> |
| <p>接下来看封装了注册中心操作逻辑的注册中心领域对象:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register2.png" alt="在这里插入图片描述"></p> |
| <ul> |
| <li>Node 节点信息开放接口 比如节点 url的获取 ,销毁</li> |
| <li>RegistryService 注册服务接口,比如注册,订阅,查询等操作</li> |
| <li>Registry 注册中心接口,是否服务发现查询,注册,取消注册方法</li> |
| <li>AbstractRegistry 注册中心逻辑抽象模板类型,封装了注册,订阅,通知的基本逻辑,和本地缓存注册中心信息的基本逻辑</li> |
| <li>FailbackRegistry 封装了失败重试的逻辑</li> |
| <li>NacosRegistry 封装了以nacos作为注册中心的基本逻辑</li> |
| <li>ServiceDiscoveryRegistry 应用级服务发现注册中心逻辑,现在不需要这种网桥实现,协议可以直接与服务发现交互。ServiceDiscoveryRegistry是一种非常特殊的注册表实现,用于以兼容的方式将旧的接口级服务发现模型与3.0中引入的新服务发现模型连接起来。 |
| 它完全符合注册表SPI的扩展规范,但与zookeeper和Nacos的具体实现不同,因为它不与任何真正的第三方注册表交互,而只与过程中ServiceDiscovery的相关组件交互。简而言之,它架起了旧接口模型和新服务发现模型之间的桥梁:register()方法主要通过与MetadataService交互,将接口级数据聚合到MetadataInfo中subscribe() 触发应用程序级服务发现模型的整个订阅过程。-根据ServiceNameMapping将接口映射到应用程序。-启动新的服务发现侦听器(InstanceListener),并使NotifierListener成为InstanceListener的一部分。</li> |
| <li>CacheableFailbackRegistry 提供了一些本地内存缓存的逻辑 对注册中心有用,注册中心的sdk将原始字符串作为提供程序实例返回,例如zookeeper和etcd</li> |
| <li>ZookeeperRegistry Zookeeper作为注册中心的基本操作逻辑封装</li> |
| </ul> |
| <p>了解了这几个领域对象这里我们回到代码逻辑,这里直接看将会执行的一些核心逻辑:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> Registry <span style="color:#268bd2">getRegistry</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> URL registryUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为两步先获取注册中心工厂对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> RegistryFactory registryFactory <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>RegistryFactory<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用注册中心工厂对象获取注册中心操作对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> registryFactory<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面注册中心工厂不论那种协议的地址信息获取到的都是一个RegistryFactory$Adaptive类型(由扩展机制的字节码工具自动生成的代码)</p> |
| <p>如果getRegistry参数为应用级注册地址。如下所示将获取到的类型为ServiceDiscoveryRegistryFactory逻辑来获取注册中心: |
| (这个逻辑是@Adaptive注解产生的了逻辑具体原理可以看扩展机制中@Adaptive的实现)</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><p>getRegistry方法优先走的逻辑是这里:AbstractRegistryFactory模板类型中的getRegistry方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Registry <span style="color:#268bd2">getRegistry</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registryManager <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unable to fetch RegistryManager from ApplicationModel BeanFactory. &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;Please check if `setApplicationModel` has been override.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//销毁状态直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Registry defaultNopRegistry <span style="color:#719e07">=</span> registryManager<span style="color:#719e07">.</span>getDefaultNopRegistryIfDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#cb4b16">null</span> <span style="color:#719e07">!=</span> defaultNopRegistry<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> defaultNopRegistry<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setPath<span style="color:#719e07">(</span>RegistryService<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>addParameter<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">,</span> RegistryService<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>TIMESTAMP_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeAttribute<span style="color:#719e07">(</span>EXPORT_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeAttribute<span style="color:#719e07">(</span>REFER_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个key为 service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> createRegistryCacheKey<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Registry registry <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check配置 是否检查注册中心连通 默认为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> check <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CHECK_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> url<span style="color:#719e07">.</span>getPort<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> 0<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Lock the registry access process to ensure a single instance of the registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//给写操作加锁方式并发写问题 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryManager<span style="color:#719e07">.</span>getRegistryLock<span style="color:#719e07">().</span>lock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//锁内检查是否销毁的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// double check |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// fix https://github.com/apache/dubbo/issues/7265. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultNopRegistry <span style="color:#719e07">=</span> registryManager<span style="color:#719e07">.</span>getDefaultNopRegistryIfDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#cb4b16">null</span> <span style="color:#719e07">!=</span> defaultNopRegistry<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> defaultNopRegistry<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//锁内检查是否缓存中存在存在则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registry <span style="color:#719e07">=</span> registryManager<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registry <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> registry<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//create registry by spi/ioc |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//使用url创建注册中心操作的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registry <span style="color:#719e07">=</span> createRegistry<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check配置检查 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>check<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RuntimeException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can not create registry &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to obtain or create registry &#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Release the lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryManager<span style="color:#719e07">.</span>getRegistryLock<span style="color:#719e07">().</span>unlock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>check <span style="color:#719e07">&amp;&amp;</span> registry <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can not create registry &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registry <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registryManager<span style="color:#719e07">.</span>putRegistry<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> registry<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> registry<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>逻辑其实吧比较简单,概括下上面的逻辑:</p> |
| <ul> |
| <li>销毁逻辑判断</li> |
| <li>缓存获取,存在则直接返回</li> |
| <li>根据注册中心url配置,创建注册中心操作对象</li> |
| <li>注册中心连接失败的check配置逻辑处理</li> |
| <li>将注册中心操作对象存入缓存</li> |
| </ul> |
| <p>上面比较重要的逻辑是createRegistry这个 |
| 整个调用过程我给大家看下Debug的详情,这里很多逻辑由扩展机制产生的这里直接看下逻辑调用栈,有几个需要关注的地方我圈了起来: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register3.png" alt="在这里插入图片描述"> |
| 我们继续看服务发现的注册中心工厂对象的获取,代码如下: |
| ServiceDiscoveryRegistryFactory类型的createRegistry方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> Registry <span style="color:#268bd2">createRegistry</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//判断url是否是这个前缀:service-discovery-registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>hasServiceDiscoveryRegistryProtocol<span style="color:#719e07">(</span>url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//切换下协议:将服务发现协议切换为配置的注册中心协议这里是Zookeeper如下: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.RegistryService&amp;pid=39884&amp;release=3.0.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String protocol <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTRY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>protocol<span style="color:#719e07">).</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务发现注册中心对象对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ServiceDiscoveryRegistry<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>通过以上代码可以看到其实最终创建的是一个ServiceDiscoveryRegistry注册中心对象,这个url协议被转换为了对应注册中心的协议,也就是说双注册会有两个协议一个是原先的接口级注册注册中心对象(这个还未说到)和这里对应注册中心协议的服务发现注册中心对象ServiceDiscoveryRegistry</p> |
| <h3 id="1733-servicediscoveryregistry">17.3.3 ServiceDiscoveryRegistry</h3> |
| <p>ServiceDiscoveryRegistry服务发现注册中心对象的初始化过程:</p> |
| <h4 id="17331-servicediscoveryregistry的构造器">17.3.3.1 ServiceDiscoveryRegistry的构造器:</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ServiceDiscoveryRegistry</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">,</span> ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据url创建一个服务发现对象类型为ServiceDiscovery |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceDiscovery <span style="color:#719e07">=</span> createServiceDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个类型为是serviceNameMapping类型是MetadataServiceNameMapping类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceNameMapping <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>AbstractServiceNameMapping<span style="color:#719e07">)</span> ServiceNameMapping<span style="color:#719e07">.</span>getDefaultExtension<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ServiceDiscoveryRegistry中创建服务发现对象createServiceDiscovery方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> ServiceDiscovery <span style="color:#268bd2">createServiceDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getServiceDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>addParameter<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">,</span> ServiceDiscovery<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_TYPE_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ServiceDiscoveryRegistry中创建服务发现对象getServiceDiscovery方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> ServiceDiscovery <span style="color:#268bd2">getServiceDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//服务发现工厂对象的获取这里是ServiceDiscoveryFactory类型, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceDiscoveryFactory factory <span style="color:#719e07">=</span> getExtension<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务发现工厂对象获取服务发现对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> factory<span style="color:#719e07">.</span>getServiceDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ServiceDiscoveryFactory和ServiceDiscovery类型可以往后看</p> |
| <h4 id="17332-父类型failbackregistry的构造器">17.3.3.2 父类型FailbackRegistry的构造器</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">FailbackRegistry</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重试间隔配置retry.period ,默认为5秒 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>retryPeriod <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_RETRY_PERIOD_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTRY_RETRY_PERIOD<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// since the retry task will not be very much. 128 ticks is enough. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//因为重试任务不会太多。128个刻度就足够了。Dubbo封装的时间轮用于高效率的重试,这个在Kafka也自定义实现了后续可以单独来看看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> retryTimer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashedWheelTimer<span style="color:#719e07">(</span><span style="color:#719e07">new</span> NamedThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;DubboRegistryRetryTimer&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">),</span> retryPeriod<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">,</span> 128<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="17333-abstractregistry的构造器">17.3.3.3 AbstractRegistry的构造器</h4> |
| <p>参数url如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>zookeeper:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.RegistryService&amp;pid=39884&amp;release=3.0.8 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractRegistry</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> setUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryManager <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getOrDefaultApplicationModel<span style="color:#719e07">().</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>RegistryManager<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否本地缓存默认为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> localCacheEnabled <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_LOCAL_FILE_CACHE_ENABLED<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryCacheExecutor <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getOrDefaultFrameworkModel<span style="color:#719e07">().</span>getBeanFactory<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span>FrameworkExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSharedExecutor<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>localCacheEnabled<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Start file save timer 是否同步缓存默认为false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> syncSaveFile <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_FILESAVE_SYNC_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认缓存的文件路径与文件名字为:/Users/song/.dubbo/dubbo-registry-dubbo-demo-api-provider-127.0.0.1-2181.cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String defaultFilename <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>USER_HOME<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> DUBBO_REGISTRY <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getApplication<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">().</span>replaceAll<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;:&#34;</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;-&#34;</span><span style="color:#719e07">)</span> <span style="color:#719e07">+</span> CACHE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//未指定缓存的文件名字则用默认的文件名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String filename <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>FILE_KEY<span style="color:#719e07">,</span> defaultFilename<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> File file <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//父目录创建,保证目录存在 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ConfigUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>filename<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> file <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File<span style="color:#719e07">(</span>filename<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>exists<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">().</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">().</span>mkdirs<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Invalid registry cache file &#34;</span> <span style="color:#719e07">+</span> file <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: Failed to create directory &#34;</span> <span style="color:#719e07">+</span> file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>file <span style="color:#719e07">=</span> file<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// When starting the subscription center, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// we need to read the local cache file for future Registry fault tolerance processing. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载本地磁盘文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadProperties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//变更推送 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> notify<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getBackupUrls<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1734-将服务提供者数据转换到本地内存的元数据信息中">17.3.4 将服务提供者数据转换到本地内存的元数据信息中</h3> |
| <p>在前面我们看到了RegistryProtocol中调用register来注册服务提供者的数据到注册的中心,接下来详细看下实现原理: |
| 下面参数为ServiceDiscoveryRegistry为情况下举例子:ServiceDiscoveryRegistry类型的register方法与ZookeeperRegister注册不一样传统的接口级注册在这个方法里面就将服务数据注册到注册中心了,服务发现的数据注册分为了两步,这里仅仅将数据封装到内存中如下: |
| url例子为:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>dubbo:<span style="color:#586e75">//192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=19559&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1654938441023 |
| </span></span></span></code></pre></div><p>RegistryProtocol中的register方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">register</span><span style="color:#719e07">(</span>Registry registry<span style="color:#719e07">,</span> URL registeredProviderUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registry<span style="color:#719e07">.</span>register<span style="color:#719e07">(</span>registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面这个代码会优先走ListenerRegistryWrapper的一些逻辑来执行register方法来触发一些监听器的逻辑,我们直接跳到ServiceDiscoveryRegistry中的register方法来看</p> |
| <p>ServiceDiscoveryRegistry的register方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">register</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//逻辑判断比如只有side为提供者时候才能注册 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>shouldRegister<span style="color:#719e07">(</span>url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> <span style="color:#586e75">// Should Not Register |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> doRegister<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ServiceDiscoveryRegistry的doRegister方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doRegister</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fixme, add registry-cluster is not necessary anymore |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> addRegistryClusterKey<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceDiscovery<span style="color:#719e07">.</span>register<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractServiceDiscovery的register方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">register</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//metadaInfo类型为MetadataInfo类型,用来操作元数据的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataInfo<span style="color:#719e07">.</span>addService<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>MetadataInfo 类型的addService方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addService</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fixme, pass in application mode context during initialization of MetadataInfo. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//元数据参数过滤器扩展获取:MetadataParamsFilter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>loader <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getOrDefaultApplicationModel<span style="color:#719e07">().</span>getExtensionLoader<span style="color:#719e07">(</span>MetadataParamsFilter<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据参数过滤器获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>MetadataParamsFilter<span style="color:#719e07">&gt;</span> filters <span style="color:#719e07">=</span> loader<span style="color:#719e07">.</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;params-filter&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generate service level metadata |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//生成服务级别的元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceInfo serviceInfo <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceInfo<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> filters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>services<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>serviceInfo<span style="color:#719e07">.</span>getMatchKey<span style="color:#719e07">(),</span> serviceInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// extract common instance level params |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extractInstanceParams<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> filters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>exportedServiceURLs <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportedServiceURLs <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentSkipListMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> addURL<span style="color:#719e07">(</span>exportedServiceURLs<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> updated <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1735-接口级服务提供者配置的注册">17.3.5 接口级服务提供者配置的注册</h3> |
| <p>前面我们通过服务发现的的url进行了举例子,其实在RegistryProtocol协议的export方法中还会注册接口级信息: |
| 例如如下关键代码: |
| 当registryUrl参数不是服务发现协议service-discovery-registry配置而是zookeeper如下时候获取到的扩展类型将是与Zookeeper相关的扩展对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>zookeeper:<span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=29386&amp;release=3.0.8&amp;timestamp=1655023329438 |
| </span></span></span></code></pre></div><p>RegistryProtocol协议的export方法中接口级数据注册的核心代码如下: |
| 如下代码的操作类型可以看注释</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// url to registry 这里registry对象的类型为ZookeeperRegistry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> URL registeredProviderUrl <span style="color:#719e07">=</span> getUrlToRegistry<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// decide if we need to delay publish (provider itself and registry should both need to register) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> register <span style="color:#719e07">=</span> providerUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> registryUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一个方法里面会将提供者的url配置写入Zookeeper的provider节点下面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>register<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> register<span style="color:#719e07">(</span>registry<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>如上代码是获取Zookeeper操作对象和向Zookeeper中写入服务提供者信息的代码,关于与Zookeeper连接和注册数据本地缓存的代码可以看ZookeeperRegistry类型和它的几个父类型比如:CacheableFailbackRegistry类型,关于接口级数据的注册可以看register方法,这个就不详细说了,下面我贴一下接口级数据注册的Zookeeper信息可以了解下就行: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register4.png" alt="在这里插入图片描述"> |
| 接口信息如下,上面我们需要注意的是这个 url配置为临时节点,当与Zookeeper断开连接或者Session超时的时候这个信息会被移除:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">/</span>dubbo<span style="color:#719e07">/</span>link<span style="color:#719e07">.</span>elastic<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>entity<span style="color:#719e07">.</span>DemoService<span style="color:#719e07">/</span>providers<span style="color:#719e07">/</span>dubbo<span style="color:#719e07">%</span>3A<span style="color:#719e07">%</span>2F<span style="color:#719e07">%</span>2F192<span style="color:#719e07">.</span>168<span style="color:#719e07">.</span>1<span style="color:#719e07">.</span>9<span style="color:#719e07">%</span>3A20880<span style="color:#719e07">%</span>2Flink<span style="color:#719e07">.</span>elastic<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>entity<span style="color:#719e07">.</span>DemoService<span style="color:#719e07">%</span>3Fanyhost<span style="color:#719e07">%</span>3Dtrue<span style="color:#719e07">%</span>26application<span style="color:#719e07">%</span>3Ddubbo<span style="color:#719e07">-</span>demo<span style="color:#719e07">-</span>api<span style="color:#719e07">-</span>provider<span style="color:#719e07">%</span>26background<span style="color:#719e07">%</span>3Dfalse<span style="color:#719e07">%</span>26deprecated<span style="color:#719e07">%</span>3Dfalse<span style="color:#719e07">%</span>26dubbo<span style="color:#719e07">%</span>3D2<span style="color:#719e07">.</span>0<span style="color:#719e07">.</span>2<span style="color:#719e07">%</span>26dynamic<span style="color:#719e07">%</span>3Dtrue<span style="color:#719e07">%</span>26generic<span style="color:#719e07">%</span>3Dfalse<span style="color:#719e07">%</span>26interface<span style="color:#719e07">%</span>3Dlink<span style="color:#719e07">.</span>elastic<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>entity<span style="color:#719e07">.</span>DemoService<span style="color:#719e07">%</span>26methods<span style="color:#719e07">%</span>3DsayHello<span style="color:#719e07">%</span>2CsayHelloAsync<span style="color:#719e07">%</span>26pid<span style="color:#719e07">%</span>3D29386<span style="color:#719e07">%</span>26release<span style="color:#719e07">%</span>3D3<span style="color:#719e07">.</span>0<span style="color:#719e07">.</span>8<span style="color:#719e07">%</span>26service<span style="color:#719e07">-</span>name<span style="color:#719e07">-</span>mapping<span style="color:#719e07">%</span>3Dtrue<span style="color:#719e07">%</span>26side<span style="color:#719e07">%</span>3Dprovider<span style="color:#719e07">%</span>26timestamp<span style="color:#719e07">%</span>3D1655023329514 |
| </span></span></code></pre></div><h2 id="174-应用级服务发现功能的实现servicediscovery">17.4 应用级服务发现功能的实现ServiceDiscovery</h2> |
| <p>在说这个实现之前我们先看看相关类型,这个服务发现相关的类型与注册中心相关的类型有点类似:</p> |
| <p>服务发现工厂类型: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register5.png" alt="在这里插入图片描述"> |
| 服务发现类型: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register6.png" alt="在这里插入图片描述"></p> |
| <p>刚刚在 ServiceDiscoveryRegistry中创建服务发现对象getServiceDiscovery方法看到了两个类型一个是服务发现工厂类型ServiceDiscoveryFactory,一个是服务发现类型ServiceDiscovery</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> ServiceDiscovery <span style="color:#268bd2">getServiceDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//服务发现工厂对象的获取这里是ServiceDiscoveryFactory类型,这里对应ZookeeperServiceDiscoveryFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceDiscoveryFactory factory <span style="color:#719e07">=</span> getExtension<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务发现工厂对象获取服务发现对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> factory<span style="color:#719e07">.</span>getServiceDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractServiceDiscoveryFactory类型的getServiceDiscovery方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> ServiceDiscovery <span style="color:#268bd2">getServiceDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个key是 zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.client.ServiceDiscovery |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//一个地址需要创建一个服务发现对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>toServiceStringWithoutResolving<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> discoveries<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> k <span style="color:#719e07">-&gt;</span> createDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>createDiscovery方法对应ZookeeperServiceDiscoveryFactory类型中的createDiscovery方法</p> |
| <p>如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> ServiceDiscovery <span style="color:#268bd2">createDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ZookeeperServiceDiscovery<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><h3 id="1741-zookeeperservicediscovery">17.4.1 ZookeeperServiceDiscovery</h3> |
| <p>ZookeeperServiceDiscovery的构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ZookeeperServiceDiscovery</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">,</span> URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//先调用父类AbstractServiceDiscovery 模板类构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建 创建CuratorFramework 类型对象用于操作Zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>curatorFramework <span style="color:#719e07">=</span> buildCuratorFramework<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用级服务发现的根路径 值为/services 这个可以在Zookeeper上面看到 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>rootPath <span style="color:#719e07">=</span> ROOT_PATH<span style="color:#719e07">.</span>getParameterValue<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务发现对象 实现类型为ServiceDiscoveryImpl 这个实现来源于Curator框架中的discovery模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceDiscovery <span style="color:#719e07">=</span> buildServiceDiscovery<span style="color:#719e07">(</span>curatorFramework<span style="color:#719e07">,</span> rootPath<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动服务发现 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceDiscovery<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Create zookeeper service discovery failed.&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法比较重要是应用级服务发现的实现,这里主要关注下serviceDiscovery类型的创建与启动,这个应用级服务发现的实现其实是Dubbo使用了Curator来做的,Dubbo只是在这里封装了一些方法来进行调用Curator的实现: |
| 关于Curator的官方文档可以看<a href="https://curator.apache.org/">curator官网</a></p> |
| <p>关于Zookeeper上面注册服务应用级服务注册信息可以看如下图所示(后面会具体讲到数据注册时的调用): |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register7.png" alt="在这里插入图片描述"> |
| 我这个服务提供者注册的应用数据如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;name&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;id&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;192.168.1.9:20880&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;address&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;192.168.1.9&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;port&#34;</span> <span style="color:#719e07">:</span> 20880<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;sslPort&#34;</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;payload&#34;</span> <span style="color:#719e07">:</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;@class&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;org.apache.dubbo.registry.zookeeper.ZookeeperInstance&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;id&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;192.168.1.9:20880&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;name&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;metadata&#34;</span> <span style="color:#719e07">:</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.endpoints&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;[{\&#34;port\&#34;:20880,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}]&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.metadata-service.url-params&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;{\&#34;connections\&#34;:\&#34;1\&#34;,\&#34;version\&#34;:\&#34;1.0.0\&#34;,\&#34;dubbo\&#34;:\&#34;2.0.2\&#34;,\&#34;release\&#34;:\&#34;3.0.8\&#34;,\&#34;side\&#34;:\&#34;provider\&#34;,\&#34;port\&#34;:\&#34;20880\&#34;,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.metadata.revision&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;a662fd2213a8a49dc6ff43a4c2ae7b9e&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.metadata.storage-type&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;local&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;timestamp&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;1654916298616&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;registrationTimeUTC&#34;</span> <span style="color:#719e07">:</span> 1654917265499<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;serviceType&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;DYNAMIC&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;uriSpec&#34;</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>如果感兴趣的话可以看更详细的curator服务发现文档<a href="https://curator.apache.org/docs/service-discovery/index.html">curator-x-discovery</a></p> |
| <h3 id="1742-abstractservicediscovery的构造器">17.4.2 AbstractServiceDiscovery的构造器</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractServiceDiscovery</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">,</span> URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用重载的构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">(),</span> registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> MetadataReportInstance metadataReportInstance <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>MetadataReportInstance<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> metadataType <span style="color:#719e07">=</span> metadataReportInstance<span style="color:#719e07">.</span>getMetadataType<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReport <span style="color:#719e07">=</span> metadataReportInstance<span style="color:#719e07">.</span>getMetadataReport<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_CLUSTER_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataReportInstance.getMetadataType())) { |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// this.metadataReport = metadataReportInstance.getMetadataReport(registryURL.getParameter(REGISTRY_CLUSTER_KEY)); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// } else { |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// this.metadataReport = metadataReportInstance.getNopMetadataReport(); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// } |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>重载的构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractServiceDiscovery</span><span style="color:#719e07">(</span>String serviceName<span style="color:#719e07">,</span> URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个url参考:zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.client.ServiceDiscovery&amp;pid=4570&amp;release=3.0.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>registryURL <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个serviceName参考dubbo-demo-api-provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceName <span style="color:#719e07">=</span> serviceName<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//MetadataInfo 用来封装元数据信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataInfo <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetadataInfo<span style="color:#719e07">(</span>serviceName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个是元数据缓存信息管理的类型 缓存文件使用LRU策略 感兴趣的可以详细看看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//对应缓存路径为:/Users/song/.dubbo/.metadata.zookeeper127.0.0.1%003a2181.dubbo.cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metaCacheManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetaCacheManager<span style="color:#719e07">(</span>getCacheNameSuffix<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getFrameworkModel<span style="color:#719e07">().</span>getBeanFactory<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span>FrameworkExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getCacheRefreshingScheduledExecutor<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="175-服务映射类型abstractservicenamemapping">17.5 服务映射类型AbstractServiceNameMapping</h2> |
| <p>服务映射主要是通过服务名字来反查应用信息的应用名字如下图所示 |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register8.png" alt="在这里插入图片描述"> |
| 这里我们来看下服务映射相关的类型主要通过如下代码来获取扩展对象:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceNameMapping <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>AbstractServiceNameMapping<span style="color:#719e07">)</span> ServiceNameMapping<span style="color:#719e07">.</span>getDefaultExtension<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">());</span> |
| </span></span></code></pre></div><p>对应类型如下: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register9.png" alt="在这里插入图片描述"></p> |
| <p>最终获取的扩展实现类型为:MetadataServiceNameMapping |
| 构造器如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetadataServiceNameMapping</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> metadataReportInstance <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>MetadataReportInstance<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>服务映射元数据父类型AbstractServiceNameMapping如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractServiceNameMapping</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//LRU缓存保存服务映射数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>mappingCacheManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MappingCacheManager<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getFrameworkModel<span style="color:#719e07">().</span>getBeanFactory<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span>FrameworkExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getCacheRefreshingScheduledExecutor<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="174-双注册元数据信息发布到注册中心">17.4 双注册元数据信息发布到注册中心</h2> |
| <h3 id="1741-回顾简介">17.4.1 回顾简介</h3> |
| <p>前面注册数据的时候并没有把服务配置的元数据直接注册在注册中心而是需要在导出服务之后在ServiceConfig中来发布元数据,这个就需要我们回到ServiceConfig的exportUrl方法来看了如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportUrl</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String scope <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>SCOPE_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// don&#39;t export when none is configured |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">...</span>省略到若干代码 |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>SCOPE_LOCAL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>scope<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> exportRemote<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isGeneric<span style="color:#719e07">(</span>generic<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>getScopeModel<span style="color:#719e07">().</span>isInternal<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetadataUtils<span style="color:#719e07">.</span>publishServiceDefinition<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> providerModel<span style="color:#719e07">.</span>getServiceModel<span style="color:#719e07">(),</span> getApplicationModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>urls<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1742-元数据服务定义数据的发布">17.4.2 元数据服务定义数据的发布</h3> |
| <p>在exportRemote之后单独调用发布元数据的方法来发布,通过调用元数据工具类来发布元数据信息接下来我们详细看下: |
| MetadataUtils类型的publishServiceDefinition方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishServiceDefinition</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> ServiceDescriptor serviceDescriptor<span style="color:#719e07">,</span> ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//查询是否存在元数据存储对象 对应接口MetadataReport 这里对应实现类 ZookeeperMetadataReport |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>getMetadataReports<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">).</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String msg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Remote Metadata Report Server is not provided or unavailable, will stop registering service definition to remote center!&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>msg<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String side <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getSide<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务提供者走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>PROVIDER_SIDE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>side<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String serviceKey <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getServiceKey<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取当前服务元数据信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FullServiceDefinition serviceDefinition <span style="color:#719e07">=</span> serviceDescriptor<span style="color:#719e07">.</span>getFullServiceDefinition<span style="color:#719e07">(</span>serviceKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>serviceKey<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> serviceDefinition <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceDefinition<span style="color:#719e07">.</span>setParameters<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> MetadataReport<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> getMetadataReports<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">).</span>entrySet<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetadataReport metadataReport <span style="color:#719e07">=</span> entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>metadataReport<span style="color:#719e07">.</span>shouldReportDefinition<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Report of service definition is disabled for &#34;</span> <span style="color:#719e07">+</span> entry<span style="color:#719e07">.</span>getKey<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存储服务提供者的元数据 metadataReport类型为ZookeeperMetadataReport 方法来源于父类模板方法: AbstractMetadataReport类型的storeProviderMetadata模板方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReport<span style="color:#719e07">.</span>storeProviderMetadata<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> MetadataIdentifier<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getServiceInterface<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">:</span> url<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">:</span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> PROVIDER_SIDE<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务消费者走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> MetadataReport<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> getMetadataReports<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">).</span>entrySet<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetadataReport metadataReport <span style="color:#719e07">=</span> entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>metadataReport<span style="color:#719e07">.</span>shouldReportDefinition<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Report of service definition is disabled for &#34;</span> <span style="color:#719e07">+</span> entry<span style="color:#719e07">.</span>getKey<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> metadataReport<span style="color:#719e07">.</span>storeConsumerMetadata<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> MetadataIdentifier<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getServiceInterface<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">:</span> url<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">:</span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> CONSUMER_SIDE<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">()),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ignore error |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;publish service definition metadata error.&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractMetadataReport的storeProviderMetadata方法如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否同步配置对应sync-report 默认为异步 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>syncReport<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> storeProviderMetadataTask<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportCacheExecutor<span style="color:#719e07">.</span>execute<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> storeProviderMetadataTask<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractMetadataReport的存储元数据方法storeProviderMetadataTask</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadataTask</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;store provider metadata. Identifier : &#34;</span> <span style="color:#719e07">+</span> providerMetadataIdentifier <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;; definition: &#34;</span> <span style="color:#719e07">+</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> allMetadataReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> failedReports<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Gson gson <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Gson<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String data <span style="color:#719e07">=</span> gson<span style="color:#719e07">.</span>toJson<span style="color:#719e07">(</span>serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> doStoreProviderMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> saveProperties<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> <span style="color:#719e07">!</span>syncReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// retry again. If failed again, throw exception. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> failedReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> metadataReportRetry<span style="color:#719e07">.</span>startRetryTask<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to put provider metadata &#34;</span> <span style="color:#719e07">+</span> providerMetadataIdentifier <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in &#34;</span> <span style="color:#719e07">+</span> serviceDefinition <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register10.png" alt="在这里插入图片描述"></p> |
| <p>元数据信息如下:可以分为两类 应用元数据,服务元数据</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameters&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;side&#34;</span>: <span style="color:#2aa198">&#34;provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;interface&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;pid&#34;</span>: <span style="color:#2aa198">&#34;22099&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;application&#34;</span>: <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo&#34;</span>: <span style="color:#2aa198">&#34;2.0.2&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;release&#34;</span>: <span style="color:#2aa198">&#34;3.0.8&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;anyhost&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.ip&#34;</span>: <span style="color:#2aa198">&#34;192.168.1.9&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: <span style="color:#2aa198">&#34;sayHello,sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;background&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;deprecated&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dynamic&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;service-name-mapping&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;generic&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.port&#34;</span>: <span style="color:#2aa198">&#34;20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;timestamp&#34;</span>: <span style="color:#2aa198">&#34;1654942353902&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;canonicalName&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;codeSource&#34;</span>: <span style="color:#2aa198">&#34;file:/Users/song/Desktop/dubbo-test/target/classes/&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: [{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [<span style="color:#2aa198">&#34;java.lang.String&#34;</span>], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [] |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHello&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [<span style="color:#2aa198">&#34;java.lang.String&#34;</span>], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [] |
| </span></span><span style="display:flex;"><span> }], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;types&#34;</span>: [{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;result&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;stack&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span> |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;next&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;status&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> }], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [] |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>Zookeeper扩展类型ZookeeperMetadataReport实现的存储方法如下所示doStoreProviderMetadata:</p> |
| <p>如果我们自己实现一套元数据就可以重写这个方法来进行元数据的额存储</p> |
| <p>ZookeeperMetadataReport的doStoreProviderMetadata</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doStoreProviderMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> String serviceDefinitions<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> storeMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinitions<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ZookeeperMetadataReport的storeMetadata</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier metadataIdentifier<span style="color:#719e07">,</span> String v<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//参数false为非临时节点,这个元数据为持久节点,这个细节就暂时不看了就是将刚刚的json元数据存储到对应路径上面:路径为:/dubbo/metadata/link.elastic.dubbo.entity.DemoService/provider/dubbo-demo-api-provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> zkClient<span style="color:#719e07">.</span>create<span style="color:#719e07">(</span>getNodePath<span style="color:#719e07">(</span>metadataIdentifier<span style="color:#719e07">),</span> v<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/17-dubbo3-ying-yong-ji-zhu-ce-zhi-fu-wu-ti-gong-zhe-de-shuang-zhu-ce-yuan-li/">17-Dubbo服务提供者的双注册原理</a></p></description></item><item><title>Blog: 16-模块发布器发布服务全过程</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/16/16-%E6%A8%A1%E5%9D%97%E5%8F%91%E5%B8%83%E5%99%A8%E5%8F%91%E5%B8%83%E6%9C%8D%E5%8A%A1%E5%85%A8%E8%BF%87%E7%A8%8B/</link><pubDate>Tue, 16 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/16/16-%E6%A8%A1%E5%9D%97%E5%8F%91%E5%B8%83%E5%99%A8%E5%8F%91%E5%B8%83%E6%9C%8D%E5%8A%A1%E5%85%A8%E8%BF%87%E7%A8%8B/</guid><description> |
| <h1 id="16-模块发布器发布服务全过程">16-模块发布器发布服务全过程</h1> |
| <h2 id="161-简介">16.1 简介</h2> |
| <p>Dubbo做为服务治理框架,比较核心的就是服务相关的概念,这里我先贴个找到的关于Dubbo工作原理的架构图: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/16-deploy.png" alt="在这里插入图片描述"> |
| 如果按完整服务启动与订阅的顺序我们可以归结为以下6点:</p> |
| <ul> |
| <li>导出服务(提供者) |
| <ul> |
| <li>服务提供方通过指定端口对外暴露服务</li> |
| </ul> |
| </li> |
| <li>注册服务(提供者) |
| <ul> |
| <li>提供方向注册中心注册自己的信息</li> |
| </ul> |
| </li> |
| <li>(服务发现)-订阅服务(消费者) |
| <ul> |
| <li>服务调用方通过注册中心订阅自己感兴趣的服务</li> |
| </ul> |
| </li> |
| <li>(服务发现)-服务推送(消费者) |
| <ul> |
| <li>注册中心向调用方推送地址列表</li> |
| </ul> |
| </li> |
| <li>调用服务(消费者调用提供者) |
| <ul> |
| <li>调用方选择一个地址发起RPC调用</li> |
| </ul> |
| </li> |
| <li>监控服务 |
| <ul> |
| <li>服务提供方和调用方的统计数据由监控模块收集展示</li> |
| </ul> |
| </li> |
| </ul> |
| <p>上面的完整的服务启动订阅与调用流程不仅仅适用于Dubbo 同样也适用于其他服务治理与发现的模型, 一般服务发现与服务调用的思路就是这样的,我们将以上内容扩展,暴漏服务可以使用http,tcp,udp等各种协议,注册服务可以注册到Redis,Dns,Etcd,Zookeeper等注册中心中,订阅服务可以主动去注册中心查询服务列表,服务发现可以让注册中心将服务数据动态推送给消费者.Dubbo其实就是基于这种简单的服务模型来扩展出各种功能的支持,来满足服务治理的各种场景,了解了这里可能各位同学就想着自行开发一个简单的微服务框架了。</p> |
| <p>回到主题,从以上的服务完整发布调用流程可以看到,所有的功能都是由导出服务(提供者)开始的,只有提供者先提供了服务才可以有真正的服务让消费者调用。</p> |
| <p>之前的博客内容 链接:<a href="https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/">&laquo;12-全局视野来看Dubbo3.0.8的服务启动生命周期&raquo;</a> 我们了解了 DefaultModuleDeployer模块器启动的流程,其中在start代码的模版方法中开始了导出服务的功能,这里我们来详细看下服务发布的全过程:</p> |
| <p>入口代码: DefaultModuleDeployer的发布服务方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportServices</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ServiceConfigBase sc <span style="color:#719e07">:</span> configManager<span style="color:#719e07">.</span>getServices<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportServiceInternal<span style="color:#719e07">(</span>sc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="162-导出服务的入口">16.2 导出服务的入口</h2> |
| <p>入口代码: DefaultModuleDeployer的发布服务方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportServices</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ServiceConfigBase sc <span style="color:#719e07">:</span> configManager<span style="color:#719e07">.</span>getServices<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportServiceInternal<span style="color:#719e07">(</span>sc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>主要流程为遍历初始化的服务配置列表然后逐个服务开始到处 |
| 内部导出服务代码: |
| exportServiceInternal方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportServiceInternal</span><span style="color:#719e07">(</span>ServiceConfigBase sc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ServiceConfig<span style="color:#719e07">&lt;?&gt;</span> serviceConfig <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ServiceConfig<span style="color:#719e07">&lt;?&gt;)</span> sc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务配置刷新 配置优先级覆盖 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>serviceConfig<span style="color:#719e07">.</span>isRefreshed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务已经导出过了就直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>sc<span style="color:#719e07">.</span>isExported<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否异步方式导出 全局配置或者服务级其中一个配置了异步则异步处理 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>exportAsync <span style="color:#719e07">||</span> sc<span style="color:#719e07">.</span>shouldExportAsync<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//异步其实就是使用线程来导出服务serviceExportExecutor |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExecutorService executor <span style="color:#719e07">=</span> executorRepository<span style="color:#719e07">.</span>getServiceExportExecutor<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> CompletableFuture<span style="color:#719e07">&lt;</span>Void<span style="color:#719e07">&gt;</span> future <span style="color:#719e07">=</span> CompletableFuture<span style="color:#719e07">.</span>runAsync<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>sc<span style="color:#719e07">.</span>isExported<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> sc<span style="color:#719e07">.</span>export<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> exportedServices<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>sc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; export async catch error : &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> executor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> asyncExportingFutures<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>future<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//同步导出服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>sc<span style="color:#719e07">.</span>isExported<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> sc<span style="color:#719e07">.</span>export<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> exportedServices<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>sc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个逻辑里面做了一些基本的操作,可以直接看注释然后调用ServiceConfig的export的来导出服务,继续往后看服务配置的导出服务方法。</p> |
| <h2 id="163-服务配置导出服务模板方法">16.3 服务配置导出服务模板方法</h2> |
| <p>核心的服务导出代码是在服务配置中来做的ServiceConfig的 export() 方法 |
| ServiceConfig的 export() 方法代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">export</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//已经导出过服务直接放那会 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>exported<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ensure start module, compatible with old api usage |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//确保模块启动了(基本的初始化操作执行了) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getScopeModel<span style="color:#719e07">().</span>getDeployer<span style="color:#719e07">().</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//悲观锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>exported<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置是否刷新 前面初始化时候已经刷新过配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>isRefreshed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务导出配置配置为false则不导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>shouldExport<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务发布前初始化一下元数据对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>init<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>shouldDelay<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置了服务的延迟发布配置则走延迟发布逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doDelayExport<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//导出服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doExport<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1631-服务配置导出服务前的初始化方法">16.3.1 服务配置导出服务前的初始化方法</h3> |
| <p>ServiceConfig 导出服务之前的初始化方法init</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载服务监听器 这里暂时没有服务监听器扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// load ServiceListeners from extension |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ServiceListener<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ServiceListener<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceListeners<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>extensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务提供者配置传递给元数据配置对象 一个服务提供者配置会有一个元数据配置,服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initServiceMetadata<span style="color:#719e07">(</span>provider<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>setServiceType<span style="color:#719e07">(</span>getInterfaceClass<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> serviceMetadata<span style="color:#719e07">.</span>setTarget<span style="color:#719e07">(</span>getRef<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据的key格式为 group/服务接口:版本号 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>generateServiceKey<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="164-服务配置导出服务模板方法2">16.4 服务配置导出服务模板方法2</h2> |
| <p>ServiceConfig 导出服务核心逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExport</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//取消发布 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>unexported<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has already unexported!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//已经发布 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>exported<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务路径 为空则设置为接口名,本例子中为link.elastic.dubbo.entity.DemoService |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> interfaceName<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//导出URL |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doExportUrls<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exported<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1641-导出服务的url配置逻辑">16.4.1 导出服务的URL配置逻辑</h3> |
| <p>ServiceConfig 导出URL核心逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrls</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块服务存储库 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ModuleServiceRepository repository <span style="color:#719e07">=</span> getScopeModel<span style="color:#719e07">().</span>getServiceRepository<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ServiceDescriptor serviceDescriptor<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ref为服务实现类型 这里对应我们例子的DemoServiceImpl |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">boolean</span> serverService <span style="color:#719e07">=</span> ref <span style="color:#719e07">instanceof</span> ServerService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span><span style="color:#719e07">(</span>serverService<span style="color:#719e07">){</span> |
| </span></span><span style="display:flex;"><span> serviceDescriptor<span style="color:#719e07">=((</span>ServerService<span style="color:#719e07">)</span> ref<span style="color:#719e07">).</span>getServiceDescriptor<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>serviceDescriptor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span><span style="color:#719e07">else</span><span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//我们代码走这个逻辑 注册服务 这个注册不是向注册中心注册 这个是解析服务接口将服务方法等描述信息存放在了服务存储ModuleServiceRepository类型对象的成员变量services中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceDescriptor <span style="color:#719e07">=</span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>getInterfaceClass<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供者领域模型, 提供者领域模型 封装了一些提供者需要的就基本属性同时内部解析封装方法信息 ProviderMethodModel 列表 , 服务标识符 格式group/服务接:版本号 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> providerModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ProviderModel<span style="color:#719e07">(</span>getUniqueServiceName<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务实现类DemoServiceImpl |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ref<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务描述符 描述符里面包含了服务接口的方法信息,不过服务接口通过反射也可以拿到方法信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceDescriptor<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前所处模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getScopeModel<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前服务接口的元数据对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块服务存储库存储提供者模型对象ModuleServiceRepository |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> repository<span style="color:#719e07">.</span>registerProvider<span style="color:#719e07">(</span>providerModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置的注册中心列表 ,同时将注册中心配置转URL (在Dubbo中URL就是配置信息的一种形式) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里会获取到两个 由dubbo.application.register-mode 双注册配置决定 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//注册中心 registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//service-discovery-registry://8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//参数dubbo是dubbo协议的版本不是Dubbo版本 Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里后面详细说下 服务双注册 dubbo.application.register-mode |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs <span style="color:#719e07">=</span> ConfigValidationUtils<span style="color:#719e07">.</span>loadRegistries<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ProtocolConfig protocolConfig <span style="color:#719e07">:</span> protocols<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String pathKey <span style="color:#719e07">=</span> URL<span style="color:#719e07">.</span>buildKey<span style="color:#719e07">(</span>getContextPath<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span>p <span style="color:#719e07">-&gt;</span> p <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;/&#34;</span> <span style="color:#719e07">+</span> path<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>orElse<span style="color:#719e07">(</span>path<span style="color:#719e07">),</span> group<span style="color:#719e07">,</span> version<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// stub service will use generated service name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span><span style="color:#719e07">(!</span>serverService<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// In case user specified path, register service one more time to map it to path. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//模块服务存储库ModuleServiceRepository存储服务接口信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>pathKey<span style="color:#719e07">,</span> interfaceClass<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//导出根据协议导出配置到注册中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doExportUrlsFor1Protocol<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1642-应用级和接口级服务注册地址获取">16.4.2 应用级和接口级服务注册地址获取</h3> |
| <p>这里主要看下注册中心的获取,这里涉及到服务的双注册配置</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs <span style="color:#719e07">=</span> ConfigValidationUtils<span style="color:#719e07">.</span>loadRegistries<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>关于loadRegistries方法的详情我们就不看了主要看loadRegistries方法中调用的genCompatibleRegistries添加服务发现注册中心</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param scopeModel 域模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param registryList 配置的注册中心列表 例如:registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param provider 是否为服务提供者 这里Demo为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">genCompatibleRegistries</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryList<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> result <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>registryList<span style="color:#719e07">.</span>size<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的注册中心 为每个注册中心增加兼容的服务发现注册中心地址配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryList<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>registryURL <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否为提供者 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// for registries enabled service discovery, automatically register interface compatible addresses. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> String registerMode<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册协议配置了service-discovery-registry 走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//前面这个逻辑是直接接给result结果中添加应用级注册,如果是all配置则增加接口级注册信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_MODE_KEY<span style="color:#719e07">,</span> ConfigurationUtils<span style="color:#719e07">.</span>getCachedDynamicProperty<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> DUBBO_REGISTER_MODE_DEFAULT_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTER_MODE_INSTANCE<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isValidRegisterMode<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> DEFAULT_REGISTER_MODE_INSTANCE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里配置的就是应用级配置 则先添加应用级地址,再根据条件判断是否添加接口级注册中心地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> registryNotExists<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">,</span> registryList<span style="color:#719e07">,</span> REGISTRY_PROTOCOL<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> URL interfaceCompatibleRegistryURL <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>REGISTRY_PROTOCOL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_TYPE_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>interfaceCompatibleRegistryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//正常情况下我们的配置会走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 获取服务注册的注册模式 配置为dubbo.application.register-mode 默认值为all 既注册接口数据又注册应用级信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerMode <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_MODE_KEY<span style="color:#719e07">,</span> ConfigurationUtils<span style="color:#719e07">.</span>getCachedDynamicProperty<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> DUBBO_REGISTER_MODE_DEFAULT_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isValidRegisterMode<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> DEFAULT_REGISTER_MODE_INTERFACE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据逻辑条件判断是否添加应用级注册中心地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>DEFAULT_REGISTER_MODE_INSTANCE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> registryNotExists<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">,</span> registryList<span style="color:#719e07">,</span> SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> URL serviceDiscoveryRegistryURL <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_TYPE_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>serviceDiscoveryRegistryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据逻辑条件判断是否添加接口级注册中心地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>DEFAULT_REGISTER_MODE_INTERFACE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> FrameworkStatusReportService reportService <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">).</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>FrameworkStatusReportService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>reportRegistrationStatus<span style="color:#719e07">(</span>reportService<span style="color:#719e07">.</span>createRegistrationReport<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法是根据服务注册模式来判断使用接口级注册地址还是应用级注册地址分别如下所示: |
| 配置信息: |
| dubbo.application.register-mode |
| 配置值:</p> |
| <ul> |
| <li>interface |
| <ul> |
| <li>接口级注册</li> |
| </ul> |
| </li> |
| <li>instance |
| <ul> |
| <li>应用级注册</li> |
| </ul> |
| </li> |
| <li>all |
| <ul> |
| <li>接口级别和应用级都注册</li> |
| </ul> |
| </li> |
| </ul> |
| <p>接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><p>应用级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><h2 id="165-导出服务配置到本地和注册中心">16.5 导出服务配置到本地和注册中心</h2> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> doExportUrlsFor1Protocol<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>protocolConfig为:dubbo协议的配置 |
| &lt;dubbo:protocol port=&quot;-1&quot; name=&ldquo;dubbo&rdquo; /&gt;</p> |
| <p>registryURLs目前有两个 应用级注册地址和接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><h3 id="1651-导出服务配置的doexporturlsfor1protocol方法">16.5.1 导出服务配置的doExportUrlsFor1Protocol方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrlsFor1Protocol</span><span style="color:#719e07">(</span>ProtocolConfig protocolConfig<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成协议配置具体可见下图中的元数据配置中的attachments |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> map <span style="color:#719e07">=</span> buildAttributes<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// remove null key and null value |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//移除空值 简化配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> map<span style="color:#719e07">.</span>keySet<span style="color:#719e07">().</span>removeIf<span style="color:#719e07">(</span>key <span style="color:#719e07">-&gt;</span> key <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> map<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">)</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init serviceMetadata attachments |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//协议配置放到元数据对象中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>getAttachments<span style="color:#719e07">().</span>putAll<span style="color:#719e07">(</span>map<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//协议配置 + 默认协议配置转URL类型的配置存储 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> buildUrl<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">,</span> map<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//导出url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/16-deploy2.png" alt="在这里插入图片描述"></p> |
| <h3 id="1652-导出服务配置模板方法">16.5.2 导出服务配置模板方法</h3> |
| <p>继续看导出服务的模板方法,分为本地导出和注册中心导出 |
| //参数url为协议配置url可以参考:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>dubbo:<span style="color:#586e75">//192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=10953&amp;release=3.0.8&amp;side=provider&amp;timestamp=1653705630518 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportUrl</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String scope <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>SCOPE_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// don&#39;t export when none is configured |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>SCOPE_NONE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>scope<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export to local if the config is not remote (export to remote only when config is remote) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//未明确指定远程导出 则开启本地导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>SCOPE_REMOTE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>scope<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportLocal<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//未明确指定本地导出 则开启远程导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// export to remote if the config is not local (export to local only when config is local) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>SCOPE_LOCAL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>scope<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> exportRemote<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isGeneric<span style="color:#719e07">(</span>generic<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>getScopeModel<span style="color:#719e07">().</span>isInternal<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetadataUtils<span style="color:#719e07">.</span>publishServiceDefinition<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> providerModel<span style="color:#719e07">.</span>getServiceModel<span style="color:#719e07">(),</span> getApplicationModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>urls<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="166-导出服务到本地">16.6 导出服务到本地</h2> |
| <p>本地调用使用了 injvm 协议,是一个伪协议,它不开启端口,不发起远程调用,只在 JVM 内直接关联,但执行 Dubbo 的 Filter 链。</p> |
| <p>直接通过代码来看吧</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportLocal</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//协议转为injvm 代表本地导出 host为127.0.0.1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL local <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>LOCAL_PROTOCOL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setHost<span style="color:#719e07">(</span>LOCALHOST_VALUE<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setPort<span style="color:#719e07">(</span>0<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> local <span style="color:#719e07">=</span> local<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setServiceModel<span style="color:#719e07">(</span>providerModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> doExportUrl<span style="color:#719e07">(</span>local<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Export dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to local registry url : &#34;</span> <span style="color:#719e07">+</span> local<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1661-doexporturl方法">16.6.1 doExportUrl方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrl</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> withMetaData<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里是由adaptor扩展类型处理过的 我们直接看默认的类型javassist 对应JavassistProxyFactory代理工厂 获取调用对象 ( |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Invoker<span style="color:#719e07">&lt;?&gt;</span> invoker <span style="color:#719e07">=</span> proxyFactory<span style="color:#719e07">.</span>getInvoker<span style="color:#719e07">(</span>ref<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>Class<span style="color:#719e07">)</span> interfaceClass<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>withMetaData<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DelegateProviderMetaDataInvoker<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Exporter<span style="color:#719e07">&lt;?&gt;</span> exporter <span style="color:#719e07">=</span> protocolSPI<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exporters<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>exporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1662-javassistproxyfactory类型的getinvoker方法">16.6.2 JavassistProxyFactory类型的getInvoker方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getInvoker</span><span style="color:#719e07">(</span>T proxy<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// TODO Wrapper cannot handle this scenario correctly: the classname contains &#39;$&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 创建实际服务提供者的代理类型,代理类型后缀为DubboWrap在这里类型为 link.elastic.dubbo.entity.DemoServiceImplDubboWrap0 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Wrapper wrapper <span style="color:#719e07">=</span> Wrapper<span style="color:#719e07">.</span>getWrapper<span style="color:#719e07">(</span>proxy<span style="color:#719e07">.</span>getClass<span style="color:#719e07">().</span>getName<span style="color:#719e07">().</span>indexOf<span style="color:#719e07">(</span><span style="color:#2aa198">&#39;$&#39;</span><span style="color:#719e07">)</span> <span style="color:#719e07">&lt;</span> 0 <span style="color:#719e07">?</span> proxy<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个匿名内部类对象 继承自AbstractProxyInvoker的Invoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> AbstractProxyInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>proxy<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> Object <span style="color:#268bd2">doInvoke</span><span style="color:#719e07">(</span>T proxy<span style="color:#719e07">,</span> String methodName<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;[]</span> parameterTypes<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> Object<span style="color:#719e07">[]</span> arguments<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Throwable <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> wrapper<span style="color:#719e07">.</span>invokeMethod<span style="color:#719e07">(</span>proxy<span style="color:#719e07">,</span> methodName<span style="color:#719e07">,</span> parameterTypes<span style="color:#719e07">,</span> arguments<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">};</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable fromJavassist<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// try fall back to JDK proxy factory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1663-使用协议导出调用对象-export">16.6.3 使用协议导出调用对象 export</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> Exporter<span style="color:#719e07">&lt;?&gt;</span> exporter <span style="color:#719e07">=</span> protocolSPI<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这个使用了Adaptor扩展和Wrapper机制Debug起来不太方便这里贴一下调用堆栈<img src="https://dubbo.apache.org/imgs/blog/source-blog/16-deploy3.png" alt="在这里插入图片描述"></p> |
| <h3 id="16631-协议序列化机制protocolserializationwrapper">16.6.3.1 协议序列化机制ProtocolSerializationWrapper</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里主要逻辑是将服务提供者url添加到服务存储仓库中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getFrameworkModel<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">().</span>getScopeModel<span style="color:#719e07">()).</span>getServiceRepository<span style="color:#719e07">().</span>registerProviderUrl<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="16632-协议过滤器wrapper-protocolfilterwrapper">16.6.3.2 协议过滤器Wrapper ProtocolFilterWrapper</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心的协议导出直接执行 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//过滤器调用链FilterChainBuilder的扩展对象查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FilterChainBuilder builder <span style="color:#719e07">=</span> getFilterChainBuilder<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为2步 生成过滤器调用链 然后使用链表中的节点调用 这里值查询provider类型的过滤器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>builder<span style="color:#719e07">.</span>buildInvokerChain<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> SERVICE_FILTER_KEY<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>PROVIDER<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>过滤器调用链的生成 对用DefaultFilterChainBuilder类型的buildInvokerChain方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">buildInvokerChain</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> originalInvoker<span style="color:#719e07">,</span> String key<span style="color:#719e07">,</span> String group<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//originalInvoker代表真正的服务调用器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> last <span style="color:#719e07">=</span> originalInvoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> URL url <span style="color:#719e07">=</span> originalInvoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ModuleModel<span style="color:#719e07">&gt;</span> moduleModels <span style="color:#719e07">=</span> getModuleModelsFromUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Filter<span style="color:#719e07">&gt;</span> filters<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModels <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> moduleModels<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//类型Filter key为service.filter 分组为provider 所有提供者过滤器拉取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> filters <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Filter<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> moduleModels<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">)).</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> group<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModels <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> moduleModels<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ExtensionDirector<span style="color:#719e07">&gt;</span> directors <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ModuleModel moduleModel <span style="color:#719e07">:</span> moduleModels<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Filter<span style="color:#719e07">&gt;</span> tempFilters <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Filter<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> moduleModel<span style="color:#719e07">).</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> group<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> filters<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>tempFilters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> directors<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> sortingAndDeduplication<span style="color:#719e07">(</span>filters<span style="color:#719e07">,</span> directors<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Filter<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">).</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> group<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//倒序拼接,将过滤器的调用对象添加到链表中 最后倒序遍历之后 last节点指向了调用链路链表头节点的对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>filters<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> filters<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">-</span> 1<span style="color:#719e07">;</span> i <span style="color:#719e07">&gt;=</span> 0<span style="color:#719e07">;</span> i<span style="color:#719e07">--)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> Filter filter <span style="color:#719e07">=</span> filters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>i<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> next <span style="color:#719e07">=</span> last<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//每个invoker对象中都有originalInvoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> last <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CopyOfFilterChainNode<span style="color:#719e07">&lt;&gt;(</span>originalInvoker<span style="color:#719e07">,</span> next<span style="color:#719e07">,</span> filter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> CallbackRegistrationInvoker<span style="color:#719e07">&lt;&gt;(</span>last<span style="color:#719e07">,</span> filters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> last<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/16-deploy4.png" alt="在这里插入图片描述"></p> |
| <h3 id="16633-协议监听器wrapper-protocollistenerwrapper">16.6.3.3 协议监听器Wrapper ProtocolListenerWrapper</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心地址则直接导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 先导出对象 再创建过滤器包装对象 执行监听器逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ListenerExporterWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> Collections<span style="color:#719e07">.</span>unmodifiableList<span style="color:#719e07">(</span>ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExporterListener<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">().</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getActivateExtension<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">(),</span> EXPORTER_LISTENER_KEY<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="16634-qos的协议wrapper-qosprotocolwrapper">16.6.3.4 QOS的协议Wrapper QosProtocolWrapper</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心导出的时候开启QOS 默认端口22222 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startQosServer<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="16635-injvmprotocol-的导出方法">16.6.3.5 InjvmProtocol 的导出方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> InjvmExporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>invoker<span style="color:#719e07">,</span> invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">().</span>getServiceKey<span style="color:#719e07">(),</span> exporterMap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="167-导出服务到注册中心">16.7 导出服务到注册中心</h2> |
| <p>16.5.2 导出服务配置模板方法 中我们看到了服务导出会导出到本地和远程,接下来就看下导出到远程的方法exportRemote |
| 参数url:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>dubbo:<span style="color:#586e75">//192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=12865&amp;release=3.0.8&amp;side=provider&amp;timestamp=1653708351378 |
| </span></span></span></code></pre></div><p>参数registryURLs目前有两个 应用级注册地址和接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> URL <span style="color:#268bd2">exportRemote</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>registryURLs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有注册地址与注册模式 逐个注册 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>URL registryURL <span style="color:#719e07">:</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为协议URL 添加应用级注册service-discovery-registry参数service-name-mapping为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>SERVICE_NAME_MAPPING_KEY<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//if protocol is only injvm ,not register |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOCAL_PROTOCOL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为协议url 添加动态配置dynamic |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>DYNAMIC_KEY<span style="color:#719e07">,</span> registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>DYNAMIC_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//监控配置暂时为null |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL monitorUrl <span style="color:#719e07">=</span> ConfigValidationUtils<span style="color:#719e07">.</span>loadMonitor<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>monitorUrl <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>putAttribute<span style="color:#719e07">(</span>MONITOR_KEY<span style="color:#719e07">,</span> monitorUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// For providers, this is used to enable custom proxy to generate invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String proxy <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>PROXY_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>proxy<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registryURL <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>addParameter<span style="color:#719e07">(</span>PROXY_KEY<span style="color:#719e07">,</span> proxy<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//开始注册服务了 打印个认知 提示下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Register dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; url &#34;</span> <span style="color:#719e07">+</span> url <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to registry &#34;</span> <span style="color:#719e07">+</span> registryURL<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Export dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to url &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> doExportUrl<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>putAttribute<span style="color:#719e07">(</span>EXPORT_KEY<span style="color:#719e07">,</span> url<span style="color:#719e07">),</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Export dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to url &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> doExportUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> url<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1671---doexporturl方法">16.7.1 doExportUrl方法</h3> |
| <p>与 16.6.1 doExportUrl方法 导出本地协议是一样的逻辑 ,我们来看看点不同地方</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrl</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> withMetaData<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Invoker<span style="color:#719e07">&lt;?&gt;</span> invoker <span style="color:#719e07">=</span> proxyFactory<span style="color:#719e07">.</span>getInvoker<span style="color:#719e07">(</span>ref<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>Class<span style="color:#719e07">)</span> interfaceClass<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>withMetaData<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//远程服务导出逐个值为true 元数据invoker包装一下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invoker <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DelegateProviderMetaDataInvoker<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Exporter<span style="color:#719e07">&lt;?&gt;</span> exporter <span style="color:#719e07">=</span> protocolSPI<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exporters<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>exporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>与本地导出ProtocolFilterWrapper的不同之处 |
| 服务发现service-discovery-registry的导出UrlUtils.isRegistry(invoker.getUrl() 判断结果为true会走这个逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心的协议导出直接执行 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 服务发现service-discovery-registry的导出会走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//过滤器调用链FilterChainBuilder的扩展对象查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FilterChainBuilder builder <span style="color:#719e07">=</span> getFilterChainBuilder<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为2步 生成过滤器调用链 然后使用链表中的节点调用 这里值查询provider类型的过滤器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>builder<span style="color:#719e07">.</span>buildInvokerChain<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> SERVICE_FILTER_KEY<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>PROVIDER<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>与 协议监听器Wrapper ProtocolListenerWrapper 的不同之处</p> |
| <p>服务发现service-discovery-registry的导出UrlUtils.isRegistry(invoker.getUrl() 判断结果为true会走这个逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心地址则直接导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 服务发现service-discovery-registry的导出会走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 先导出对象 再创建过滤器包装对象 执行监听器逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ListenerExporterWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> Collections<span style="color:#719e07">.</span>unmodifiableList<span style="color:#719e07">(</span>ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExporterListener<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">().</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getActivateExtension<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">(),</span> EXPORTER_LISTENER_KEY<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>与 16.6.3.4 QOS的协议Wrapper QosProtocolWrapper 不同之处</p> |
| <p>服务发现service-discovery-registry的导出UrlUtils.isRegistry(invoker.getUrl() 判断结果为true会走这个逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心导出的时候开启QOS 默认端口22222 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startQosServer<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>启动QOS服务startQosServer</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startQosServer</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>hasStarted<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> qosEnable <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>QOS_ENABLE<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>qosEnable<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos won&#39;t be started because it is disabled. &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;Please check dubbo.application.qos.enable is configured either in system property, &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.properties or XML/spring-boot configuration.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String host <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>QOS_HOST<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> port <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>QOS_PORT<span style="color:#719e07">,</span> QosConstants<span style="color:#719e07">.</span>DEFAULT_PORT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> acceptForeignIp <span style="color:#719e07">=</span> Boolean<span style="color:#719e07">.</span>parseBoolean<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>ACCEPT_FOREIGN_IP<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;false&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> Server server <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>Server<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>setHost<span style="color:#719e07">(</span>host<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>setPort<span style="color:#719e07">(</span>port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>setAcceptForeignIp<span style="color:#719e07">(</span>acceptForeignIp<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable throwable<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Fail to start qos server: &#34;</span><span style="color:#719e07">,</span> throwable<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>QOS的Server的启动方法start</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> Throwable <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>started<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//1个主线程 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> boss <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> NioEventLoopGroup<span style="color:#719e07">(</span>1<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> DefaultThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos-boss&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//0个从线程 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> worker <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> NioEventLoopGroup<span style="color:#719e07">(</span>0<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> DefaultThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos-worker&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务端启动器,和参数设置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServerBootstrap serverBootstrap <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServerBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>group<span style="color:#719e07">(</span>boss<span style="color:#719e07">,</span> worker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>channel<span style="color:#719e07">(</span>NioServerSocketChannel<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>option<span style="color:#719e07">(</span>ChannelOption<span style="color:#719e07">.</span>SO_REUSEADDR<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>childOption<span style="color:#719e07">(</span>ChannelOption<span style="color:#719e07">.</span>TCP_NODELAY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>childHandler<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ChannelInitializer<span style="color:#719e07">&lt;</span>Channel<span style="color:#719e07">&gt;()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initChannel</span><span style="color:#719e07">(</span>Channel ch<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ch<span style="color:#719e07">.</span>pipeline<span style="color:#719e07">().</span>addLast<span style="color:#719e07">(</span><span style="color:#719e07">new</span> QosProcessHandler<span style="color:#719e07">(</span>frameworkModel<span style="color:#719e07">,</span> welcome<span style="color:#719e07">,</span> acceptForeignIp<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isBlank<span style="color:#719e07">(</span>host<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>bind<span style="color:#719e07">(</span>port<span style="color:#719e07">).</span>sync<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>bind<span style="color:#719e07">(</span>host<span style="color:#719e07">,</span> port<span style="color:#719e07">).</span>sync<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos-server bind localhost:&#34;</span> <span style="color:#719e07">+</span> port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable throwable<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos-server can not bind localhost:&#34;</span> <span style="color:#719e07">+</span> port<span style="color:#719e07">,</span> throwable<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> throwable<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>QOS处理器为QosProcessHandler关于QosProcessHandler的细节这里先不说</p> |
| <p>最后一个不同的地方调用链路走的这个 RegistryProtocol</p> |
| <h3 id="1672-通过注册协议导出服务与注册服务的流程">16.7.2 通过注册协议导出服务与注册服务的流程</h3> |
| <p>RegistryProtocol的导出方法: |
| 这个方法非常重要也是服务注册的核心代码,先概括下包含了哪些步骤</p> |
| <ul> |
| <li>覆盖配置</li> |
| <li>导出协议端口开启TCP服务</li> |
| <li>注册到注册中心</li> |
| <li>通知服务启动了</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> originInvoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//service-discovery-registry://8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=14256&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653710477057 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL registryUrl <span style="color:#719e07">=</span> getRegistryUrl<span style="color:#719e07">(</span>originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// url to export locally |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=14256&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1653710479073 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL providerUrl <span style="color:#719e07">=</span> getProviderUrl<span style="color:#719e07">(</span>originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Subscribe the override data |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// the same service. Because the subscribed is cached key with the name of the service, it causes the |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// subscription information to cover. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//provider://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;category=configurators&amp;check=false&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=14256&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1653710479073 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> URL overrideSubscribeUrl <span style="color:#719e07">=</span> getSubscribedOverrideUrl<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//override配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> OverrideListener overrideSubscribeListener <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> OverrideListener<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">,</span> originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">,</span> NotifyListener<span style="color:#719e07">&gt;</span> overrideListeners <span style="color:#719e07">=</span> getProviderConfigurationListener<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">).</span>getOverrideListeners<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> overrideListeners<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">,</span> overrideSubscribeListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> providerUrl <span style="color:#719e07">=</span> overrideUrlWithConfig<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">,</span> overrideSubscribeListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//export invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> ExporterChangeableWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> exporter <span style="color:#719e07">=</span> doLocalExport<span style="color:#719e07">(</span>originInvoker<span style="color:#719e07">,</span> providerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// url to registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//通过URL获取 注册中心Registry操作对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//需要向注册中心注册地址转换 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=14656&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1653711086189 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> URL registeredProviderUrl <span style="color:#719e07">=</span> getUrlToRegistry<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// decide if we need to delay publish (provider itself and registry should both need to register) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> register <span style="color:#719e07">=</span> providerUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> registryUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否向注册中心注册 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>register<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> register<span style="color:#719e07">(</span>registry<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register stated url on provider model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerStatedUrl<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">,</span> register<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> exporter<span style="color:#719e07">.</span>setRegisterUrl<span style="color:#719e07">(</span>registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exporter<span style="color:#719e07">.</span>setSubscribeUrl<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>registry<span style="color:#719e07">.</span>isServiceDiscovery<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Deprecated! Subscribe to override rules in 2.6.x or before. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registry<span style="color:#719e07">.</span>subscribe<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">,</span> overrideSubscribeListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内置监听器通知 这个不是通知消费者的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> notifyExport<span style="color:#719e07">(</span>exporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//Ensure that a new exporter instance is returned every time export |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> DestroyableExporter<span style="color:#719e07">&lt;&gt;(</span>exporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="168-dolocalexport本地导出协议开启端口">16.8 doLocalExport本地导出协议开启端口</h2> |
| <p>前面已经看过了本地协议JVM协议的服务导出和注册中心配置的导出,这里可以直接看一些关键代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExporterChangeableWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">doLocalExport</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> originInvoker<span style="color:#719e07">,</span> URL providerUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> getCacheKey<span style="color:#719e07">(</span>originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>ExporterChangeableWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> bounds<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> s <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Invoker<span style="color:#719e07">&lt;?&gt;</span> invokerDelegate <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InvokerDelegate<span style="color:#719e07">&lt;&gt;(</span>originInvoker<span style="color:#719e07">,</span> providerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//代码中用的这个protoco对象是dubbo自动生成的适配器对象protocol$Adaptive 适配器对象会根据当前协议的参数来查询具体的协议扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ExporterChangeableWrapper<span style="color:#719e07">&lt;&gt;((</span>Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invokerDelegate<span style="color:#719e07">),</span> originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面这个protocol$Adaptive 协议的export导出方法与之前的一样也会经历下面几个过程,具体细节可以参考JVM协议的导出:</p> |
| <ul> |
| <li>ProtocolSerializationWrapper</li> |
| <li>ProtocolFilterWrapper</li> |
| <li>ProtocolListenerWrapper</li> |
| <li>QosProtocolWrapper</li> |
| <li>唯一不同的是我们这里对应的协议扩展类型为DubboProtocol、 |
| 接下来来看下DubboProtocol的导出服务export方法实现:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务提供者的url参考例子dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=6043&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1654224285437 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export service. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//生成服务的key参考:link.elastic.dubbo.entity.DemoService:20880 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> serviceKey<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建导出服务用的导出器DubboExporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DubboExporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> exporter <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DubboExporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>invoker<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> exporterMap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//export a stub service for dispatching event |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//stub配置校验 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Boolean isStubSupportEvent <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>STUB_EVENT_KEY<span style="color:#719e07">,</span> DEFAULT_STUB_EVENT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Boolean isCallbackservice <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>IS_CALLBACK_SERVICE<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStubSupportEvent <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isCallbackservice<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String stubServiceMethods <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>STUB_EVENT_METHODS_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>stubServiceMethods <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> stubServiceMethods<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isWarnEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;consumer [&#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;], has set stubproxy support event ,but no stub methods founded.&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务开启服务端口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> openServer<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> optimizeSerialization<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> exporter<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="开启服务端口">开启服务端口</h3> |
| <p>这里就到了RPC协议的TCP通信模块了,对应DubboProtocol 的 openServer(url);方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">openServer</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// find server. 地址作为key这里是192.168.1.9:20880 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// client can export a service which only for server to invoke |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//默认提供者开启服务,消费者是不能开启服务的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> isServer <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>IS_SERVER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isServer<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//协议服务器 下面一个双重校验锁检查,如果为空则创建服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ProtocolServer server <span style="color:#719e07">=</span> serverMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>server <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> server <span style="color:#719e07">=</span> serverMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>server <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serverMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> createServer<span style="color:#719e07">(</span>url<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span><span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>reset<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// server supports reset, use together with override |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> server<span style="color:#719e07">.</span>reset<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>为当前地址创建协议服务对应方法如下: |
| DubboProtocol的createServer方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> ProtocolServer <span style="color:#268bd2">createServer</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//下面将url增加了心跳参数最终如下dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;channel.readonly.sent=true&amp;codec=dubbo&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;heartbeat=60000&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=6700&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1654225251112 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// send readonly event when server closes, it&#39;s enabled by default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>CHANNEL_READONLYEVENT_SENT_KEY<span style="color:#719e07">,</span> Boolean<span style="color:#719e07">.</span>TRUE<span style="color:#719e07">.</span>toString<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// enable heartbeat by default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>HEARTBEAT_KEY<span style="color:#719e07">,</span> String<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>DEFAULT_HEARTBEAT<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>addParameter<span style="color:#719e07">(</span>CODEC_KEY<span style="color:#719e07">,</span> DubboCodec<span style="color:#719e07">.</span>NAME<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里服务端使用的网络库这里是默认值netty |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String str <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>SERVER_KEY<span style="color:#719e07">,</span> DEFAULT_REMOTING_SERVER<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>str<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>url<span style="color:#719e07">.</span>getOrDefaultFrameworkModel<span style="color:#719e07">().</span>getExtensionLoader<span style="color:#719e07">(</span>Transporter<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>hasExtension<span style="color:#719e07">(</span>str<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unsupported server type: &#34;</span> <span style="color:#719e07">+</span> str <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, url: &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//dubbo交换器层对象创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExchangeServer server<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个方法会绑定端口,关于交换器与传输网络层到后面统一说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里通过绑定url和请求处理器来创建交换器对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> server <span style="color:#719e07">=</span> Exchangers<span style="color:#719e07">.</span>bind<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> requestHandler<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>RemotingException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Fail to start server(url: &#34;</span> <span style="color:#719e07">+</span> url <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> str <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CLIENT_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>str<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> supportedTypes <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getOrDefaultFrameworkModel<span style="color:#719e07">().</span>getExtensionLoader<span style="color:#719e07">(</span>Transporter<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>supportedTypes<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>str<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unsupported client type: &#34;</span> <span style="color:#719e07">+</span> str<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DubboProtocolServer protocolServer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DubboProtocolServer<span style="color:#719e07">(</span>server<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//关闭等待时长默认为10秒 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadServerProperties<span style="color:#719e07">(</span>protocolServer<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocolServer<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="169-向注册中心注册服务register">16.9 向注册中心注册服务register</h2> |
| <p>这个细节在下个博客中说涉及到Dubbo3的双注册</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/16-mo-kuai-fa-bu-qi-fa-bu-fu-wu-quan-guo-cheng/">16-模块发布器发布服务全过程</a></p></description></item><item><title>Blog: 15-Dubbo的三大中心之元数据中心源码解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/15/15-dubbo%E7%9A%84%E4%B8%89%E5%A4%A7%E4%B8%AD%E5%BF%83%E4%B9%8B%E5%85%83%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</link><pubDate>Mon, 15 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/15/15-dubbo%E7%9A%84%E4%B8%89%E5%A4%A7%E4%B8%AD%E5%BF%83%E4%B9%8B%E5%85%83%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</guid><description> |
| <h1 id="15-dubbo的三大中心之元数据中心源码解析">15-Dubbo的三大中心之元数据中心源码解析</h1> |
| <h2 id="151-简介">15.1 简介</h2> |
| <p>关于元数据中心的概念对于大部分用户来说是比较陌生的,配置中心的话我们还好理解,对于元数据中心是什么,我们来看下我从官网拷贝过来的一段文字:</p> |
| <p>元数据中心在2.7.x版本开始支持,随着应用级别的服务注册和服务发现在Dubbo中落地,<strong>元数据中心也变的越来越重要</strong>。在以下几种情况下会需要部署元数据中心:</p> |
| <ul> |
| <li>对于一个原先采用老版本Dubbo搭建的应用服务,在迁移到Dubbo 3时,Dubbo 3 会需要一个<strong>元数据中心来维护RPC服务与应用的映射关系(即接口与应用的映射关系)</strong>,因为如果采用了<strong>应用级别的服务发现和服务注册</strong>,在注册中心中将<strong>采用“应用 —— 实例列表”结构</strong>的数据组织形式,<strong>不再是以往的“接口 —— 实例列表”结构的数据组织形式</strong>,而以往用接口级别的服务注册和服务发现的应用服务在<strong>迁移到应用级别</strong>时,<strong>得不到接口与应用之间的对应关系</strong>,从而无法从注册中心得到实例列表信息,所以<strong>Dubbo为了兼容这种场景,在Provider端启动时,会往元数据中心存储接口与应用的映射关系</strong>。</li> |
| <li>为了让<strong>注册中心更加聚焦与地址的发现和推送能力</strong>,<strong>减轻注册中心的负担</strong>,元数据中心承载了所有的服务元数据、大量接口/方法级别配置信息等,无论是接口粒度还是应用粒度的服务发现和注册,元数据中心都起到了重要的作用。</li> |
| <li></li> |
| </ul> |
| <p>如果有以上两种需求,都可以选择部署元数据中心,并通过Dubbo的配置来集成该元数据中心。</p> |
| <p><strong>元数据中心并不依赖于注册中心和配置中心</strong>,用户可以自由选择是否集成和部署元数据中心,如下图所示:</p> |
| <p><img src="https://dubbo.apache.org/imgs/v3/concepts/centers-metadata.png" alt="/imgs/v3/concepts/centers-metadata.png"></p> |
| <p>该图中不配备配置中心,意味着可以不需要全局管理配置的能力。该图中不配备注册中心,意味着可能采用了Dubbo mesh的方案,也可能不需要进行服务注册,仅仅接收直连模式的服务调用。 |
| 官网参考文章地址:</p> |
| <ul> |
| <li><a href="https://dubbo.apache.org/zh-cn/docs/concepts/registry-configcenter-metadata/">部署架构(注册中心 配置中心 元数据中心</a></li> |
| <li><a href="https://dubbo.apache.org/zh-cn/docs/references/metadata/">元数据参考手册</a></li> |
| </ul> |
| <p>综上所述可以用几句话概括下:</p> |
| <ul> |
| <li>元数据中心来维护RPC服务与应用的映射关系(即接口与应用的映射关系)来兼容接口与应用之间的对应关系</li> |
| <li>让注册中心更加聚焦与地址的发现和推送能力</li> |
| </ul> |
| <p>注册中心的启动是在DefaultApplicationDeployer中的初始化方法 initialize() 中:如下所示</p> |
| <p>这里只看下 startMetadataCenter();方法即可</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>startLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register shutdown hook |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerShutdownHook<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> startConfigCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> loadApplicationConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initModuleDeployers<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// @since 2.7.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startMetadataCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has been initialized!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="152-深入探究元数据中心的启动过程">15.2 深入探究元数据中心的启动过程</h2> |
| <h3 id="1521-启动元数据中心的代码全貌">15.2.1 启动元数据中心的代码全貌</h3> |
| <p>关于元数据中心我们看下 startMetadataCenter()方法来大致了解下整个流程</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startMetadataCenter</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> useRegistryAsMetadataCenterIfNecessary<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用的配置信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> getApplication<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据配置类型 元数据类型, local 或 remote,,如果选择远程,则需要进一步指定元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String metadataType <span style="color:#719e07">=</span> applicationConfig<span style="color:#719e07">.</span>getMetadataType<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// FIXME, multiple metadata config support. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//查询元数据中心的地址等配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataReportConfigs <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getMetadataConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>metadataReportConfigs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个就是判断 如果选择远程,则需要进一步指定元数据中心 否则就抛出来异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>REMOTE_METADATA_STORAGE_TYPE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>metadataType<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No MetadataConfig found, Metadata Center address is required when &#39;metadata=remote&#39; is enabled.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//MetadataReport实例的存储库对象获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReportInstance metadataReportInstance <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>MetadataReportInstance<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> validMetadataReportConfigs <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>metadataReportConfigs<span style="color:#719e07">.</span>size<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetadataReportConfig metadataReportConfig <span style="color:#719e07">:</span> metadataReportConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ConfigValidationUtils<span style="color:#719e07">.</span>validateMetadataConfig<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> validMetadataReportConfigs<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReportInstance<span style="color:#719e07">.</span>init<span style="color:#719e07">(</span>validMetadataReportConfigs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//MetadataReport实例的存储库对象初始化失败则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>metadataReportInstance<span style="color:#719e07">.</span>inited<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;%s MetadataConfigs found, but none of them is valid.&#34;</span><span style="color:#719e07">,</span> metadataReportConfigs<span style="color:#719e07">.</span>size<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1522-元数据中心未配置则使用注册中心配置">15.2.2 元数据中心未配置则使用注册中心配置</h3> |
| <p>前面在说配置中心的时候有说过配置中心如果未配置会使用注册中心的地址等信息作为默认配置,这里元数据做了类似的操作:如代码: |
| DefaultApplicationDeployer类型的 useRegistryAsMetadataCenterIfNecessary()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">useRegistryAsMetadataCenterIfNecessary</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置缓存中查询元数据配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataConfigs <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getMetadataConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置存在则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>metadataConfigs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">////查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> defaultRegistries <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getDefaultRegistries<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultRegistries<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多注册中心遍历 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultRegistries |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>stream<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>filter<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>isUsedRegistryAsMetadataCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心配置映射为元数据中心 映射就是获取需要的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>registryAsMetadataCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将元数据中心配置存储在配置缓存中方便后续使用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>metadataReportConfig <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">.</span>getId<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataReportConfigs <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getMetadataConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>metadataReportConfigs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetadataReportConfig existedConfig <span style="color:#719e07">:</span> metadataReportConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>existedConfig<span style="color:#719e07">.</span>getId<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> existedConfig<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">().</span>equals<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> configManager<span style="color:#719e07">.</span>addMetadataReport<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Optional<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> configOptional <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getConfig<span style="color:#719e07">(</span>MetadataReportConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> metadataReportConfig<span style="color:#719e07">.</span>getId<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>configOptional<span style="color:#719e07">.</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> configManager<span style="color:#719e07">.</span>addMetadataReport<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;use registry as metadata-center: &#34;</span> <span style="color:#719e07">+</span> metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个代码有些细节就不细说了 我们概括下顺序梳理下思路:</p> |
| <ul> |
| <li>配置缓存中查询元数据配置,配置存在则直接返回</li> |
| <li>查询所有可用的默认注册中心列表 |
| <ul> |
| <li>多注册中心遍历</li> |
| <li>选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)</li> |
| <li>注册中心配置RegistryConfig映射转换为元数据中心配置类型MetadataReportConfig 映射就是获取需要的配置</li> |
| <li>将元数据中心配置存储在配置缓存中方便后续使用</li> |
| </ul> |
| </li> |
| </ul> |
| <p>元数据的配置可以参考官网:<a href="https://dubbo.apache.org/zh-cn/docs/references/metadata/">元数据参考手册</a></p> |
| <p>这里主要看下可配置项有哪些 对应类型为MetadataReportConfig 在官网暂时未找到合适的文档,这里整理下属性列表方便后续配置说明查看:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>配置变量</th> |
| <th>类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>id</td> |
| <td>String</td> |
| <td>配置id</td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td>String</td> |
| <td>元数据协议</td> |
| </tr> |
| <tr> |
| <td>address</td> |
| <td>String</td> |
| <td>元数据中心地址</td> |
| </tr> |
| <tr> |
| <td>port</td> |
| <td>Integer</td> |
| <td>元数据中心端口</td> |
| </tr> |
| <tr> |
| <td>username</td> |
| <td>String</td> |
| <td>元数据中心认证用户名</td> |
| </tr> |
| <tr> |
| <td>password</td> |
| <td>String</td> |
| <td>元数据中心认证密码</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>Integer</td> |
| <td>元数据中心的请求超时(毫秒)</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>String</td> |
| <td>该组将元数据保存在中。它与注册表相同</td> |
| </tr> |
| <tr> |
| <td>parameters</td> |
| <td>Map&lt;String, String&gt;</td> |
| <td>自定义参数</td> |
| </tr> |
| <tr> |
| <td>retryTimes</td> |
| <td>Integer</td> |
| <td>重试次数</td> |
| </tr> |
| <tr> |
| <td>retryPeriod</td> |
| <td>Integer</td> |
| <td>重试间隔</td> |
| </tr> |
| <tr> |
| <td>cycleReport</td> |
| <td>Boolean</td> |
| <td>默认情况下, 是否每天重复存储完整的元数据</td> |
| </tr> |
| <tr> |
| <td>syncReport</td> |
| <td>Boolean</td> |
| <td>Sync or Async report.</td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>Boolean</td> |
| <td>需要群集支持,默认为false</td> |
| </tr> |
| <tr> |
| <td>registry</td> |
| <td>String</td> |
| <td>注册表配置id</td> |
| </tr> |
| <tr> |
| <td>file</td> |
| <td>String</td> |
| <td>元数据报告文件存储位置</td> |
| </tr> |
| <tr> |
| <td>check</td> |
| <td>Boolean</td> |
| <td>连接到元数据中心时要应用的失败策略</td> |
| </tr> |
| </tbody> |
| </table> |
| <h3 id="1523-元数据中心的初始化逻辑">15.2.3 元数据中心的初始化逻辑</h3> |
| <h4 id="15231-元数据中心的初始化调用逻辑">15.2.3.1 元数据中心的初始化调用逻辑</h4> |
| <p>主要看这一行比较重要的逻辑:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//初始化元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReportInstance<span style="color:#719e07">.</span>init<span style="color:#719e07">(</span>validMetadataReportConfigs<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>在了解这一行逻辑之前我们先来看下元数据相关联的类型:</p> |
| <p>MetadataReportInstance中的初始化方法init</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">(</span>List<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataReportConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//CAS判断是否有初始化过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>init<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据类型配置如果未配置则默认为local |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataType <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">().</span>getApplicationOrElseThrow<span style="color:#719e07">().</span>getMetadataType<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataType <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataType <span style="color:#719e07">=</span> DEFAULT_METADATA_STORAGE_TYPE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取MetadataReportFactory 工厂类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReportFactory metadataReportFactory <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>MetadataReportFactory<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多元数据中心初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetadataReportConfig metadataReportConfig <span style="color:#719e07">:</span> metadataReportConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> init<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">,</span> metadataReportFactory<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">(</span>MetadataReportConfig config<span style="color:#719e07">,</span> MetadataReportFactory metadataReportFactory<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置转url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>toUrl<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>METADATA_REPORT_KEY<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String protocol <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>METADATA_REPORT_KEY<span style="color:#719e07">,</span> DEFAULT_DIRECTORY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>protocol<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>METADATA_REPORT_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>APPLICATION_KEY<span style="color:#719e07">,</span> applicationModel<span style="color:#719e07">.</span>getCurrentConfig<span style="color:#719e07">().</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> String relatedRegistryId <span style="color:#719e07">=</span> isEmpty<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">())</span> <span style="color:#719e07">?</span> <span style="color:#719e07">(</span>isEmpty<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getId<span style="color:#719e07">())</span> <span style="color:#719e07">?</span> DEFAULT_KEY <span style="color:#719e07">:</span> config<span style="color:#719e07">.</span>getId<span style="color:#719e07">())</span> <span style="color:#719e07">:</span> config<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从元数据工厂中获取元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReport metadataReport <span style="color:#719e07">=</span> metadataReportFactory<span style="color:#719e07">.</span>getMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存元数据到内存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metadataReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>relatedRegistryId<span style="color:#719e07">,</span> metadataReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>关于元数据的初始化我们主要看两个位置:</p> |
| <ul> |
| <li>一个是元数据工厂对象的创建与初始化MetadataReportFactory</li> |
| <li>一个是元数据对象的创建与初始化MetadataReport</li> |
| </ul> |
| <h4 id="15232-元数据工厂对象metadatareportfactory">15.2.3.2 元数据工厂对象MetadataReportFactory</h4> |
| <p>关于元数据工厂类型MetadataReportFactory,元数据工厂 用于<strong>创建与管理元数据对象</strong>, 相关类型如下: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/15-config.png" alt="在这里插入图片描述"></p> |
| <p>我们这里主要以为Zookeeper扩展的元数据工厂ZookeeperMetadataReportFactory类型为例子: |
| 实现类型逻辑不复杂,这里就直接贴代码看看:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ZookeeperMetadataReportFactory</span> <span style="color:#268bd2">extends</span> AbstractMetadataReportFactory <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//与Zookeeper交互的传输器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> ZookeeperTransporter zookeeperTransporter<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用程序模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> ApplicationModel applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ZookeeperMetadataReportFactory</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>zookeeperTransporter <span style="color:#719e07">=</span> ZookeeperTransporter<span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@DisableInject</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setZookeeperTransporter</span><span style="color:#719e07">(</span>ZookeeperTransporter zookeeperTransporter<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>zookeeperTransporter <span style="color:#719e07">=</span> zookeeperTransporter<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetadataReport <span style="color:#268bd2">createMetadataReport</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ZookeeperMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> zookeeperTransporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>元数据工厂的实现比较简单</p> |
| <ul> |
| <li>继承抽象的元数据工厂AbstractMetadataReportFactory</li> |
| <li>实现工厂方法createMetadataReport来创建一个元数据操作类型</li> |
| </ul> |
| <p>如果我们想要实现一个元数据工厂扩展可以参考Zookeeper的这个方式</p> |
| <h4 id="15233-元数据操作对象metadatareport的创建与初始化">15.2.3.3 元数据操作对象MetadataReport的创建与初始化</h4> |
| <p>前面的从元数据工厂中获取元数据操作对象的逻辑处理代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//从元数据工厂中获取元数据 ,url对象可以理解为配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReport metadataReport <span style="color:#719e07">=</span> metadataReportFactory<span style="color:#719e07">.</span>getMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>关于元数据对象,用于元数据信息的增删改查等逻辑的操作与元数据信息的缓存</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/15-config2.png" alt="在这里插入图片描述"></p> |
| <p>我们这里还是以Zookeeper的实现ZookeeperMetadataReportFactory类型做为参考:</p> |
| <p>我们先来看这个逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//从元数据工厂中获取元数据 ,url对象可以理解为配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReport metadataReport <span style="color:#719e07">=</span> metadataReportFactory<span style="color:#719e07">.</span>getMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>ZookeeperMetadataReportFactory的父类型AbstractMetadataReportFactory中的getMetadataReport方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetadataReport <span style="color:#268bd2">getMetadataReport</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//url值参考例子zookeeper://127.0.0.1:2181?application=dubbo-demo-api-provider&amp;client=&amp;port=2181&amp;protocol=zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果存在export则移除 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>setPath<span style="color:#719e07">(</span>MetadataReport<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameters<span style="color:#719e07">(</span>EXPORT_KEY<span style="color:#719e07">,</span> REFER_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成元数据缓存key 元数据维度 地址+名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如: zookeeper://127.0.0.1:2181/org.apache.dubbo.metadata.report.MetadataReport |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>toServiceString<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中查询 查到则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReport metadataReport <span style="color:#719e07">=</span> serviceStoreMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> metadataReport<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Lock the metadata access process to ensure a single instance of the metadata instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//存在写操作 加个锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> lock<span style="color:#719e07">.</span>lock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁在查一下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReport <span style="color:#719e07">=</span> serviceStoreMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> metadataReport<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check参数 查元数据报错是否抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> check <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CHECK_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> url<span style="color:#719e07">.</span>getPort<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> 0<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//关键模版方法 调用扩展实现的具体业务(创建元数据操作对象) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReport <span style="color:#719e07">=</span> createMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>check<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The metadata reporter failed to initialize&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check逻辑检查 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>check <span style="color:#719e07">&amp;&amp;</span> metadataReport <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can not create metadata Report &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceStoreMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> metadataReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> metadataReport<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Release the lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> lock<span style="color:#719e07">.</span>unlock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面这个抽象类AbstractMetadataReportFactory中的获取元数据操作对象的模版方法getMetadataReport(URL url), 用了双重校验锁的逻辑来创建对象缓存对象,又用了模版方法设计模式,来让抽象类做通用的逻辑,让实现类型去做扩展, 虽然代码写的太长了些整体还是用了不少的设计思想.</p> |
| <p>我们直接看这个代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>metadataReport <span style="color:#719e07">=</span> createMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这个创建元数据操作对象的代码实际上走的是实现类型的逻辑:</p> |
| <p>来自工厂Bean ZookeeperMetadataReportFactory的工厂方法如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetadataReport <span style="color:#268bd2">createMetadataReport</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ZookeeperMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> zookeeperTransporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>创建了元数据操作对象,这里我们继续看下元数据操作对象ZookeeperMetadataReport创建做了哪些逻辑: |
| 来自ZookeeperMetadataReport的构造器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ZookeeperMetadataReport</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> ZookeeperTransporter zookeeperTransporter<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//url即配置 配置传递给抽象类 做一些公共的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//url参考:zookeeper://127.0.0.1:2181/org.apache.dubbo.metadata.report.MetadataReport?application=dubbo-demo-api-provider&amp;client=&amp;port=2181&amp;protocol=zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>isAnyHost<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;registry address == null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> String group <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(</span>DEFAULT_ROOT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>group<span style="color:#719e07">.</span>startsWith<span style="color:#719e07">(</span>PATH_SEPARATOR<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> group <span style="color:#719e07">=</span> PATH_SEPARATOR <span style="color:#719e07">+</span> group<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>root <span style="color:#719e07">=</span> group<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//连接Zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> zkClient <span style="color:#719e07">=</span> zookeeperTransporter<span style="color:#719e07">.</span>connect<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>核心的公共的操作逻辑封装在父类AbstractMetadataReport里面 |
| 我们来看前面super调用的构造器逻辑: |
| 如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractMetadataReport</span><span style="color:#719e07">(</span>URL reportServerURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置url 如:zookeeper://127.0.0.1:2181/org.apache.dubbo.metadata.report.MetadataReport?application=dubbo-demo-api-provider&amp;client=&amp;port=2181&amp;protocol=zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setUrl<span style="color:#719e07">(</span>reportServerURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Start file save timer |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//缓存的文件名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//格式为: 用户目录+/.dubbo/dubbo-metadata- + 应用程序名字application + url地址(IP+端口) + 后缀.cache 如下所示 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">///Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String defaultFilename <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>USER_HOME<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> DUBBO_METADATA <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> reportServerURL<span style="color:#719e07">.</span>getApplication<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> replace<span style="color:#719e07">(</span>reportServerURL<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;:&#34;</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;-&#34;</span><span style="color:#719e07">)</span> <span style="color:#719e07">+</span> CACHE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果用户配置了缓存文件名字则以用户配置为准file |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String filename <span style="color:#719e07">=</span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>FILE_KEY<span style="color:#719e07">,</span> defaultFilename<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> File file <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件名字不为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ConfigUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>filename<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> file <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File<span style="color:#719e07">(</span>filename<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件和父目录不存在则创建文件目录 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>exists<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">().</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">().</span>mkdirs<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Invalid service store file &#34;</span> <span style="color:#719e07">+</span> file <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: Failed to create directory &#34;</span> <span style="color:#719e07">+</span> file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if this file exists, firstly delete it. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//还未初始化则已存在的历史文件删除掉 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>initialized<span style="color:#719e07">.</span>getAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> file<span style="color:#719e07">.</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> file<span style="color:#719e07">.</span>delete<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//赋值给成员变量后续继续可以用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>file <span style="color:#719e07">=</span> file<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件存在则直接加载文件中的内容 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadProperties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//sync-report配置的值为同步配置还异步配置,true是同步配置,默认为false为异步配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> syncReport <span style="color:#719e07">=</span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>SYNC_REPORT_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重试属性与逻辑也封装了一个类型 创建对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//retry-times重试次数配置 默认为100次 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//retry-period 重试间隔配置 默认为3000 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReportRetry <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetadataReportRetry<span style="color:#719e07">(</span>reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>RETRY_TIMES_KEY<span style="color:#719e07">,</span> DEFAULT_METADATA_REPORT_RETRY_TIMES<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>RETRY_PERIOD_KEY<span style="color:#719e07">,</span> DEFAULT_METADATA_REPORT_RETRY_PERIOD<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// cycle report the data switch |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//是否定期从元数据中心同步配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//cycle-report配置默认为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CYCLE_REPORT_KEY<span style="color:#719e07">,</span> DEFAULT_METADATA_REPORT_CYCLE_REPORT<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//开启重试定时器 24个小时间隔从元数据中心同步一次 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> reportTimerScheduler <span style="color:#719e07">=</span> Executors<span style="color:#719e07">.</span>newSingleThreadScheduledExecutor<span style="color:#719e07">(</span><span style="color:#719e07">new</span> NamedThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;DubboMetadataReportTimer&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> reportTimerScheduler<span style="color:#719e07">.</span>scheduleAtFixedRate<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>publishAll<span style="color:#719e07">,</span> calculateStartTime<span style="color:#719e07">(),</span> ONE_DAY_IN_MILLISECONDS<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>reportMetadata <span style="color:#719e07">=</span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REPORT_METADATA_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>reportDefinition <span style="color:#719e07">=</span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REPORT_DEFINITION_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="15234-内存中元数据自动同步到zookeeper和本地文件">15.2.3.4 内存中元数据自动同步到Zookeeper和本地文件</h4> |
| <p>这里来总结下元数据操作的初始化逻辑:</p> |
| <ul> |
| <li>首次初始化清理历史元数据文件如: |
| Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache</li> |
| <li>如果非首次进来则直接加载缓存在本地的缓存文件,赋值给properties成员变量</li> |
| <li>初始化同步配置是否异步(默认为false), sync-report配置的值为同步配置还异步配置,true是同步配置,默认为false为异步配置</li> |
| <li>初始化重试属性</li> |
| <li>是否定期从元数据中心同步配置初始化 默认为true 24小时自动同步一次</li> |
| </ul> |
| <p>关于元数据同步可以看AbstractMetadataReport类型的publishAll方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> reportTimerScheduler <span style="color:#719e07">=</span> Executors<span style="color:#719e07">.</span>newSingleThreadScheduledExecutor<span style="color:#719e07">(</span><span style="color:#719e07">new</span> NamedThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;DubboMetadataReportTimer&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> reportTimerScheduler<span style="color:#719e07">.</span>scheduleAtFixedRate<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>publishAll<span style="color:#719e07">,</span> calculateStartTime<span style="color:#719e07">(),</span> ONE_DAY_IN_MILLISECONDS<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这里有个方法叫做calculateStartTime 这个代码是随机时间的between 2:00 am to 6:00 am, the time is random. 2点到6点之间启动, 低峰期启动自动同步 |
| 返回值:</p> |
| <p>AbstractMetadataReport类型的</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishAll</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;start to publish all metadata.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>doHandleMetadataCollection<span style="color:#719e07">(</span>allMetadataReports<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractMetadataReport类型的doHandleMetadataCollection</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">doHandleMetadataCollection</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>MetadataIdentifier<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> metadataMap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataMap<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Iterator<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>MetadataIdentifier<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;&gt;</span> iterable <span style="color:#719e07">=</span> metadataMap<span style="color:#719e07">.</span>entrySet<span style="color:#719e07">().</span>iterator<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>iterable<span style="color:#719e07">.</span>hasNext<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>MetadataIdentifier<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> item <span style="color:#719e07">=</span> iterable<span style="color:#719e07">.</span>next<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>PROVIDER_SIDE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>item<span style="color:#719e07">.</span>getKey<span style="color:#719e07">().</span>getSide<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供端的元数据则存储提供端元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>storeProviderMetadata<span style="color:#719e07">(</span>item<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> <span style="color:#719e07">(</span>FullServiceDefinition<span style="color:#719e07">)</span> item<span style="color:#719e07">.</span>getValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CONSUMER_SIDE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>item<span style="color:#719e07">.</span>getKey<span style="color:#719e07">().</span>getSide<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//消费端的元数据则存储提供端元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>storeConsumerMetadata<span style="color:#719e07">(</span>item<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">)</span> item<span style="color:#719e07">.</span>getValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>提供端元数据的存储: |
| AbstractMetadataReport类型的storeProviderMetadata</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>syncReport<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> storeProviderMetadataTask<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportCacheExecutor<span style="color:#719e07">.</span>execute<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> storeProviderMetadataTask<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractMetadataReport类型的storeProviderMetadataTask |
| 具体同步代码:storeProviderMetadataTask</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadataTask</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;store provider metadata. Identifier : &#34;</span> <span style="color:#719e07">+</span> providerMetadataIdentifier <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;; definition: &#34;</span> <span style="color:#719e07">+</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> allMetadataReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> failedReports<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Gson gson <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Gson<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String data <span style="color:#719e07">=</span> gson<span style="color:#719e07">.</span>toJson<span style="color:#719e07">(</span>serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内存中的元数据同步到元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doStoreProviderMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内存中的元数据同步到本地文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> saveProperties<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> <span style="color:#719e07">!</span>syncReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// retry again. If failed again, throw exception. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> failedReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> metadataReportRetry<span style="color:#719e07">.</span>startRetryTask<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to put provider metadata &#34;</span> <span style="color:#719e07">+</span> providerMetadataIdentifier <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in &#34;</span> <span style="color:#719e07">+</span> serviceDefinition <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面代码我们主要看本地内存中的元数据同步到元数据中心和存本地的两个点:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//内存中的元数据同步到元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>doStoreProviderMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//内存中的元数据同步到本地文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>saveProperties<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> |
| </span></span></code></pre></div><p>//内存中的元数据同步到元数据中心</p> |
| <p>这个方法会调用当前子类重写的具体存储逻辑:这里我们以 |
| ZookeeperMetadataReport的doStoreProviderMetadata举例:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier metadataIdentifier<span style="color:#719e07">,</span> String v<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用zkClient创建一个节点数据为参数V v是前面说的服务定义数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> zkClient<span style="color:#719e07">.</span>create<span style="color:#719e07">(</span>getNodePath<span style="color:#719e07">(</span>metadataIdentifier<span style="color:#719e07">),</span> v<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里参数我们举个例子: 提供者的元数据内容如下: |
| 节点路径为:</p> |
| <ul> |
| <li>/dubbo/metadata/link.elastic.dubbo.entity.DemoService/provider/dubbo-demo-api-provider</li> |
| </ul> |
| <p>格式:</p> |
| <ul> |
| <li>/dubbo/metadata前缀</li> |
| <li>服务提供者接口</li> |
| <li>提供者类型provider</li> |
| <li>应用名</li> |
| </ul> |
| <p>具体的元数据内容如下: |
| 比较详细的记录了应用信息,服务接口信息和服务接口对应的方法信息</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameters&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;side&#34;</span>: <span style="color:#2aa198">&#34;provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;interface&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;pid&#34;</span>: <span style="color:#2aa198">&#34;38680&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;application&#34;</span>: <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo&#34;</span>: <span style="color:#2aa198">&#34;2.0.2&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;release&#34;</span>: <span style="color:#2aa198">&#34;3.0.8&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;anyhost&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.ip&#34;</span>: <span style="color:#2aa198">&#34;192.168.1.9&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: <span style="color:#2aa198">&#34;sayHello,sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;background&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;deprecated&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dynamic&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;service-name-mapping&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;generic&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.port&#34;</span>: <span style="color:#2aa198">&#34;20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;timestamp&#34;</span>: <span style="color:#2aa198">&#34;1653097653865&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;canonicalName&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;codeSource&#34;</span>: <span style="color:#2aa198">&#34;file:/Users/song/Desktop/Computer/A/code/gitee/weaving-a-net/weaving-test/dubbo-test/target/classes/&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHello&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;types&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;result&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;stack&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;next&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;status&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>本地缓存文件的写入 可以看下如下代码 |
| AbstractMetadataReport类型的saveProperties方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">saveProperties</span><span style="color:#719e07">(</span>MetadataIdentifier metadataIdentifier<span style="color:#719e07">,</span> String value<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> add<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> sync<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>file <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>add<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>setProperty<span style="color:#719e07">(</span>metadataIdentifier<span style="color:#719e07">.</span>getUniqueKey<span style="color:#719e07">(</span>KeyTypeEnum<span style="color:#719e07">.</span>UNIQUE_KEY<span style="color:#719e07">),</span> value<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>metadataIdentifier<span style="color:#719e07">.</span>getUniqueKey<span style="color:#719e07">(</span>KeyTypeEnum<span style="color:#719e07">.</span>UNIQUE_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">long</span> version <span style="color:#719e07">=</span> lastCacheChanged<span style="color:#719e07">.</span>incrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>sync<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取最新修改版本持久化到磁盘 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">new</span> SaveProperties<span style="color:#719e07">(</span>version<span style="color:#719e07">).</span>run<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportCacheExecutor<span style="color:#719e07">.</span>execute<span style="color:#719e07">(</span><span style="color:#719e07">new</span> SaveProperties<span style="color:#719e07">(</span>version<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>主要看如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#719e07">new</span> SaveProperties<span style="color:#719e07">(</span>version<span style="color:#719e07">).</span>run<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>SaveProperties类型代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">SaveProperties</span> <span style="color:#268bd2">implements</span> Runnable <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">long</span> version<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">SaveProperties</span><span style="color:#719e07">(</span><span style="color:#dc322f">long</span> version<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>version <span style="color:#719e07">=</span> version<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">run</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doSaveProperties<span style="color:#719e07">(</span>version<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看doSaveProperties方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doSaveProperties</span><span style="color:#719e07">(</span><span style="color:#dc322f">long</span> version<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不是最新的就不要持久化了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>version <span style="color:#719e07">&lt;</span> lastCacheChanged<span style="color:#719e07">.</span>get<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>file <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Save |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建本地文件锁: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//路径为: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">///Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache.lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> File lockfile <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File<span style="color:#719e07">(</span>file<span style="color:#719e07">.</span>getAbsolutePath<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.lock&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//锁文件不存在则创建锁文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>lockfile<span style="color:#719e07">.</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> lockfile<span style="color:#719e07">.</span>createNewFile<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//随机访问文件工具类对象创建 读写权限 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>RandomAccessFile raf <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> RandomAccessFile<span style="color:#719e07">(</span>lockfile<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;rw&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件文件Channel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//返回与此文件关联的唯一FileChannel对象。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FileChannel channel <span style="color:#719e07">=</span> raf<span style="color:#719e07">.</span>getChannel<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//FileChannel中的lock()与tryLock()方法都是尝试去获取在某一文件上的独有锁(以下简称独有锁),可以实现进程间操作的互斥。区别在于lock()会阻塞(blocking)方法的执行,tryLock()则不会。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FileLock lock <span style="color:#719e07">=</span> channel<span style="color:#719e07">.</span>tryLock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果多个线程同时进来未获取锁的则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>lock <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IOException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can not lock the metadataReport cache file &#34;</span> <span style="color:#719e07">+</span> file<span style="color:#719e07">.</span>getAbsolutePath<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, ignore and retry later, maybe multi java process use the file, please config: dubbo.metadata.file=xxx.properties&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Save |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件不存在则创建本地元数据缓存文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">///Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> file<span style="color:#719e07">.</span>createNewFile<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Properties tmpProperties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>syncReport<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// When syncReport = false, properties.setProperty and properties.store are called from the same |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// thread(reportCacheExecutor), so deep copy is not required |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> tmpProperties <span style="color:#719e07">=</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Using store method and setProperty method of the this.properties will cause lock contention |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// under multi-threading, so deep copy a new container |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//异步存储会导致锁争用 使用此的store方法和setProperty方法。属性将导致多线程下的锁争用,因此深度复制新容器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> tmpProperties <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>Object<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;&gt;</span> entries <span style="color:#719e07">=</span> properties<span style="color:#719e07">.</span>entrySet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>Object<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> entries<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> tmpProperties<span style="color:#719e07">.</span>setProperty<span style="color:#719e07">((</span>String<span style="color:#719e07">)</span> entry<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>FileOutputStream outputFile <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FileOutputStream<span style="color:#719e07">(</span>file<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//Properties类型自带的方法: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将此属性表中的属性列表(键和元素对)以适合使用load(Reader)方法的格式写入输出字符流。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> tmpProperties<span style="color:#719e07">.</span>store<span style="color:#719e07">(</span>outputFile<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;Dubbo metadataReport Cache&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> lock<span style="color:#719e07">.</span>release<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>version <span style="color:#719e07">&lt;</span> lastCacheChanged<span style="color:#719e07">.</span>get<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportCacheExecutor<span style="color:#719e07">.</span>execute<span style="color:#719e07">(</span><span style="color:#719e07">new</span> SaveProperties<span style="color:#719e07">(</span>lastCacheChanged<span style="color:#719e07">.</span>incrementAndGet<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个代码太诡异了如果是lock失败也会打印异常给人非常疑惑的感觉 后续会修复 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to save service store file, cause: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>写入文件的内容大致如下</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>link.elastic.dubbo.entity.DemoService:::provider:dubbo-demo-api-provider -&gt; { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameters&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;side&#34;</span>: <span style="color:#2aa198">&#34;provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;interface&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;pid&#34;</span>: <span style="color:#2aa198">&#34;41457&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;application&#34;</span>: <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo&#34;</span>: <span style="color:#2aa198">&#34;2.0.2&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;release&#34;</span>: <span style="color:#2aa198">&#34;3.0.8&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;anyhost&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.ip&#34;</span>: <span style="color:#2aa198">&#34;192.168.1.9&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: <span style="color:#2aa198">&#34;sayHello,sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;background&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;deprecated&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dynamic&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;service-name-mapping&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;generic&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.port&#34;</span>: <span style="color:#2aa198">&#34;20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;timestamp&#34;</span>: <span style="color:#2aa198">&#34;1653100253548&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;canonicalName&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;codeSource&#34;</span>: <span style="color:#2aa198">&#34;file:/Users/song/Desktop/Computer/A/code/gitee/weaving-a-net/weaving-test/dubbo-test/target/classes/&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHello&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;types&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;result&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;stack&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;next&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;status&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/15-dubbo-de-san-da-zhong-xin-zhi-yuan-shu-ju-zhong-xin-yuan-ma-jie-xi/">15-Dubbo的三大中心之元数据中心源码解析</a></p></description></item><item><title>Blog: 14-Dubbo配置加载全解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/14/14-dubbo%E9%85%8D%E7%BD%AE%E5%8A%A0%E8%BD%BD%E5%85%A8%E8%A7%A3%E6%9E%90/</link><pubDate>Sun, 14 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/14/14-dubbo%E9%85%8D%E7%BD%AE%E5%8A%A0%E8%BD%BD%E5%85%A8%E8%A7%A3%E6%9E%90/</guid><description> |
| <h1 id="14-dubbo配置加载全解析">14-Dubbo配置加载全解析</h1> |
| <h2 id="141-回到启动器的初始化过程">14.1 回到启动器的初始化过程</h2> |
| <p>在应用程序启动的时候会调用发布器的启动方法 ,然后调用初始化方法,在发布器DefaultApplicationDeployer中的初始化方法initialize() 如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>startLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register shutdown hook |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerShutdownHook<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> startConfigCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> loadApplicationConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initModuleDeployers<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// @since 2.7.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startMetadataCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has been initialized!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>初始化过程中会先启动配置中心配置信息处理,然后 调用加载初始化应用程序配置方法loadApplicationConfigs();进行配置加载 |
| 关于配置的官方文档链接为 <a href="https://dubbo.apache.org/zh-cn/docs/references/configuration/overview/">配置概述</a></p> |
| <p>Dubbo框架的配置项比较繁多,为了更好地管理各种配置,将其按照用途划分为不同的组件,最终所有配置项都会汇聚到URL中,传递给后续处理模块。</p> |
| <p><strong>常用配置组件如下</strong>:</p> |
| <ul> |
| <li>application: Dubbo应用配置</li> |
| <li>registry: 注册中心</li> |
| <li>protocol: 服务提供者RPC协议</li> |
| <li>config-center: 配置中心</li> |
| <li>metadata-report: 元数据中心</li> |
| <li>service: 服务提供者配置</li> |
| <li>reference: 远程服务引用配置</li> |
| <li>provider: service的默认配置或分组配置</li> |
| <li>consumer: reference的默认配置或分组配置</li> |
| <li>module: 模块配置</li> |
| <li>monitor: 监控配置</li> |
| <li>metrics: 指标配置</li> |
| <li>ssl: SSL/TLS配置</li> |
| </ul> |
| <p>配置还有几个比较重要的点:</p> |
| <p><strong>配置来源</strong> |
| 从Dubbo支持的配置来源说起,默认有6种配置来源:</p> |
| <ul> |
| <li>JVM System Properties,JVM -D 参数</li> |
| <li>System environment,JVM进程的环境变量</li> |
| <li>Externalized Configuration,外部化配置,从配置中心读取</li> |
| <li>Application Configuration,应用的属性配置,从Spring应用的Environment中提取&quot;dubbo&quot;打头的属性集</li> |
| <li>API / XML /注解等编程接口采集的配置可以被理解成配置来源的一种,是直接面向用户编程的配置采集方式</li> |
| <li>从classpath读取配置文件 dubbo.properties</li> |
| </ul> |
| <p><strong>覆盖关系</strong> |
| 下图展示了配置覆盖关系的优先级,从上到下优先级依次降低: <img src="https://dubbo.apache.org/imgs/blog/configuration.jpg" alt="在这里插入图片描述"></p> |
| <p><strong>配置方式</strong></p> |
| <ul> |
| <li>Java API配置</li> |
| <li>XML配置</li> |
| <li>Annotation配置</li> |
| <li>属性配置</li> |
| </ul> |
| <p>配置虽然非常多,但是我们掌握一下配置加载的原理,再了解下官网的文档说明路径应该基础的配置搞定是没问题的,更深入的配置很多参数还是需要了解下源码的.</p> |
| <h2 id="142-配置信息的初始化回顾">14.2 配置信息的初始化回顾</h2> |
| <p>前面我们在讲ModuleModel对象的创建的时候ModuleModel模型中包含了一个成员变量为ModuleEnvironment 代表当前的模块环境和ModuleConfigManager配置管理器 |
| 而ModuleModel模型对象的父模型对象ApplicationModel中包含了一个成员变量Environment环境和ConfigManager配置管理器.</p> |
| <p>在回顾调用过程之前我们先看下模型,配置管理器和环境与配置之间的关系如下图: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/14-config.png" alt="在这里插入图片描述"></p> |
| <p>在ModuleModel对象初始化方法initialize()中创建了模块配置管理器:ModuleConfigManager |
| 如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleConfigManager<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>ModuleEnvironment环境信息对象也会在配置管理器创建的时候被调用到: |
| 如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> ModuleEnvironment <span style="color:#268bd2">getModelEnvironment</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleEnvironment <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> moduleEnvironment <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ModuleEnvironment<span style="color:#719e07">)</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ModuleExt<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>ModuleEnvironment<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> moduleEnvironment<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在扩展对象ExtensionLoader进行对象ModuleEnvironment创建之后会对对象进行初始化调用 initExtension(instance)方法 初始化的时候调用如下代码: |
| ExtensionLoader中的初始化方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initExtension</span><span style="color:#719e07">(</span>T instance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">instanceof</span> Lifecycle<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Lifecycle lifecycle <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>Lifecycle<span style="color:#719e07">)</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> lifecycle<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="143-属性加载">14.3 属性加载</h2> |
| <h3 id="1431-environment中属性的初始化方法">14.3.1 Environment中属性的初始化方法</h3> |
| <p>这个初始化方法对应ModuleEnvironment的父类型Environment中的初始化方法如下:initialize()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载在JVM或者环境变量指定的dubbo.properties配置文件 配置的key为dubbo.properties.file ,如果未指定则查找类路径下的dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>propertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> PropertiesConfiguration<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//系统JVM参数的配置无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getProperty(key)来调用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>systemConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SystemConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//系统环境变量的配置,无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getenv(key)来获取就可以 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>environmentConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> EnvironmentConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从远程配置中心的全局配置获取对应配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>externalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;ExternalConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从远程配置中心的应用配置获取对应配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>appExternalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;AppExternalConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用内的配置比如: Spring Environment/PropertySources/application.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>appConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;AppConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载迁移配置,用户在JVM参数或者环境变量中指定的dubbo.migration.file,如果用户未指定测尝试加载类路径下的dubbo-migration.yaml |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadMigrationRule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1442--属性变量说明">14.4.2 属性变量说明</h3> |
| <p>前面我们已经基本上介绍了各个属性的含义下面用一个表格列举一下方便查看:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性变量名</th> |
| <th>属性类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>propertiesConfiguration</td> |
| <td>PropertiesConfiguration</td> |
| <td>dubbo.properties文件中的属性</td> |
| </tr> |
| <tr> |
| <td>systemConfiguration</td> |
| <td>SystemConfiguration</td> |
| <td>JVM参数 启动进程时指定的 (-D)配置</td> |
| </tr> |
| <tr> |
| <td>environmentConfiguration</td> |
| <td>EnvironmentConfiguration</td> |
| <td>环境变量中的配置</td> |
| </tr> |
| <tr> |
| <td>externalConfiguration</td> |
| <td>InmemoryConfiguration</td> |
| <td>外部配置全局配置 例如配置中心中 config-center global/default config</td> |
| </tr> |
| <tr> |
| <td>appExternalConfiguration</td> |
| <td>InmemoryConfiguration</td> |
| <td>外部的应用配置 例如配置中心中执行的当前应用的配置 config-center app config</td> |
| </tr> |
| <tr> |
| <td>appConfiguration</td> |
| <td>InmemoryConfiguration</td> |
| <td>来自应用中的配置例如:Spring Environment/PropertySources/application.properties</td> |
| </tr> |
| <tr> |
| <td>globalConfiguration</td> |
| <td>CompositeConfiguration</td> |
| <td>前面6个配置属性放到一起就是这个</td> |
| </tr> |
| <tr> |
| <td>globalConfigurationMaps</td> |
| <td>List&lt;Map&lt;String, String&raquo;</td> |
| <td>最前面的6个属性转换为map放到一起就是这个可以理解为将全局配置globalConfiguration转换成了列表 这个列表顺序在这里是:SystemConfiguration -&gt; EnvironmentConfiguration -&gt; AppExternalConfiguration -&gt; ExternalConfiguration -&gt; AppConfiguration -&gt; AbstractConfig -&gt; PropertiesConfiguration</td> |
| </tr> |
| <tr> |
| <td>defaultDynamicGlobalConfiguration</td> |
| <td>CompositeConfiguration</td> |
| <td>这个也是一个组合配置将defaultDynamicConfiguration动态配置(来自配置中心的配置)和全局配置添加到了自己的配置列表中 列表顺序为defaultDynamicConfiguration -&gt; globalConfiguration</td> |
| </tr> |
| <tr> |
| <td>localMigrationRule</td> |
| <td>String</td> |
| <td>,用户在JVM参数或者环境变量中指定的dubbo.migration.file,如果用户未指定测尝试加载类路径下的dubbo-migration.yaml</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>关于每个配置信息这里还是来了解下细节,方便大家了解原理.</p> |
| <h3 id="1433-dubboproperties配置文件加载解析原理">14.3.3 dubbo.properties配置文件加载解析原理</h3> |
| <p>如前面所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//加载在JVM或者环境变量指定的dubbo.properties配置文件 配置的key为dubbo.properties.file ,如果未指定则查找类路径下的dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>propertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> PropertiesConfiguration<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>下面就直接提构造器的PropertiesConfiguration代码了:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">PropertiesConfiguration</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refresh</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置获取的过程是借助工具类ConfigUtils来获取的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> properties <span style="color:#719e07">=</span> ConfigUtils<span style="color:#719e07">.</span>getProperties<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">.</span>getClassLoaders<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看ConfigUtils的getProperties方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> Properties <span style="color:#268bd2">getProperties</span><span style="color:#719e07">(</span>Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个配置的KEY是dubbo.properties.file System.getProperty是从JVM参数中获取配置的 一般情况下我们在启动Java进程的时候会指定Dubbo配置文件 如配置: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//-Ddubbo.properties.file=/dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_PROPERTIES_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//优先级最高的JVM参数拿不到数据则从 环境变量中获取,这个配置key也是dubbo.properties.file System.getenv是从环境变量中获取数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//例如我们在环境变量中配置 dubbo.properties.file=/dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_PROPERTIES_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果在JVM参数和环境变量都拿不到这个配置文件的路径我们就用默认的吧 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//默认的路径是类路径下的资源文件 这个路径是: dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> CommonConstants<span style="color:#719e07">.</span>DEFAULT_DUBBO_PROPERTIES<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> ConfigUtils<span style="color:#719e07">.</span>loadProperties<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">,</span> path<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>路径获取之后加载详细的配置内容:</p> |
| <p>ConfigUtils的loadProperties代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ConfigUtils<span style="color:#719e07">.</span>loadProperties<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">,</span> path<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> Properties <span style="color:#268bd2">loadProperties</span><span style="color:#719e07">(</span>Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders<span style="color:#719e07">,</span> String fileName<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> allowMultiFile<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> optional<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Properties properties <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// add scene judgement in windows environment Fix 2557 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//检查文件是否存在 直接加载配置文件如果加载到了配置文件则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>checkFileNameExist<span style="color:#719e07">(</span>fileName<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> FileInputStream input <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FileInputStream<span style="color:#719e07">(</span>fileName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>load<span style="color:#719e07">(</span>input<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> input<span style="color:#719e07">.</span>close<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为什么会有下面的逻辑呢,如果仅仅使用上面的加载方式只能加载到本系统下的配置文件,无法加载封装在jar中的根路径的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL<span style="color:#719e07">&gt;</span> set <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoadersToLoad <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>ClassUtils<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个方法loadResources在扩展加载的时候说过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> set <span style="color:#719e07">=</span> ClassLoaderResourceLoader<span style="color:#719e07">.</span>loadResources<span style="color:#719e07">(</span>fileName<span style="color:#719e07">,</span> classLoadersToLoad<span style="color:#719e07">).</span>values<span style="color:#719e07">().</span>stream<span style="color:#719e07">().</span>reduce<span style="color:#719e07">(</span><span style="color:#719e07">new</span> LinkedHashSet<span style="color:#719e07">&lt;&gt;(),</span> <span style="color:#719e07">(</span>a<span style="color:#719e07">,</span> i<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> a<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>i<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> a<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Fail to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file: &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>set<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>optional<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; found on the class path.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>allowMultiFile<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>set<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String errMsg <span style="color:#719e07">=</span> String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;only 1 %s file is expected, but %d dubbo.properties files found on class path: %s&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> fileName<span style="color:#719e07">,</span> set<span style="color:#719e07">.</span>size<span style="color:#719e07">(),</span> set<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>errMsg<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fall back to use method getResourceAsStream |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>load<span style="color:#719e07">(</span>ClassUtils<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">().</span>getResourceAsStream<span style="color:#719e07">(</span>fileName<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; properties file from &#34;</span> <span style="color:#719e07">+</span> set<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL url <span style="color:#719e07">:</span> set<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Properties p <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> InputStream input <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>openStream<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>input <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> p<span style="color:#719e07">.</span>load<span style="color:#719e07">(</span>input<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>putAll<span style="color:#719e07">(</span>p<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> input<span style="color:#719e07">.</span>close<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Fail to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> url <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>完整的配置加载流程这里用简单的话描述下:</p> |
| <ul> |
| <li>项目内配置查询 |
| <ul> |
| <li>路径查询 |
| <ul> |
| <li>从JVM参数中获取配置的 dubbo.properties.file配置文件路径</li> |
| <li>如果前面未获取到路径则从环境变量参数中获取配置的dubbo.properties.file配置文件路径</li> |
| <li>如果前面未获取到路径则使用默认路径dubbo.propertie</li> |
| </ul> |
| </li> |
| <li>配置加载 |
| <ul> |
| <li>将路径转为FileInputStream 然后使用Properties加载</li> |
| </ul> |
| </li> |
| </ul> |
| </li> |
| <li>依赖中的配置扫描查询 |
| <ul> |
| <li>使用类加载器扫描所有资源URL</li> |
| <li>url转InputStream 如 url.openStream() 然后使用Properties加载</li> |
| </ul> |
| </li> |
| </ul> |
| <h3 id="1434-加载jvm参数的配置">14.3.4 加载JVM参数的配置</h3> |
| <p>这里我们继续看SystemConfiguration配置的加载 |
| 这个直接看下代码就可以了:</p> |
| <p>这个类型仅仅是使用System.getProperty来获取JVM配置即可</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">SystemConfiguration</span> <span style="color:#268bd2">implements</span> Configuration <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Object <span style="color:#268bd2">getInternalProperty</span><span style="color:#719e07">(</span>String key<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getProperties</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">)</span> System<span style="color:#719e07">.</span>getProperties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1435-加载环境变量参数的配置">14.3.5 加载环境变量参数的配置</h3> |
| <p>这里我们来看EnvironmentConfiguration,这里我们直接来看代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">EnvironmentConfiguration</span> <span style="color:#268bd2">implements</span> Configuration <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Object <span style="color:#268bd2">getInternalProperty</span><span style="color:#719e07">(</span>String key<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String value <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>value<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> value <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>toOSStyleKey<span style="color:#719e07">(</span>key<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> value<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getProperties</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1436-内存配置的封装inmemoryconfiguration">14.3.6 内存配置的封装:InmemoryConfiguration</h3> |
| <p>这里我们看下InmemoryConfiguration的设计,这个直接看代码吧内部使用了一个LinkedHashMap来存储配置</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">InmemoryConfiguration</span> <span style="color:#268bd2">implements</span> Configuration <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String name<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// stores the configuration key-value pairs |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> store <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">InmemoryConfiguration</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">InmemoryConfiguration</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>name <span style="color:#719e07">=</span> name<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">InmemoryConfiguration</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> properties<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>setProperties<span style="color:#719e07">(</span>properties<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Object <span style="color:#268bd2">getInternalProperty</span><span style="color:#719e07">(</span>String key<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> store<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Add one property into the store, the previous value will be replaced if the key exists |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addProperty</span><span style="color:#719e07">(</span>String key<span style="color:#719e07">,</span> String value<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> store<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> value<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Add a set of properties into the store |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addProperties</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> properties<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>properties <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>store<span style="color:#719e07">.</span>putAll<span style="color:#719e07">(</span>properties<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * set store |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setProperties</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> properties<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>properties <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>store <span style="color:#719e07">=</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getProperties</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> store<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1437-dubbo迁移新版本的配置文件加载dubbo-migrationyaml">14.3.7 Dubbo迁移新版本的配置文件加载dubbo-migration.yaml</h3> |
| <p>关于配置迁移文件的用法可以看下这个Dubbo官方的<a href="https://dubbo.apache.org/zh-cn/docs/advanced/migration-invoker/">地址迁移规则说明</a></p> |
| <p>这个配置文件的文件名字为:dubbo-migration.yaml</p> |
| <p>这个和14.3.4加载JVM参数配置的过程是相似的细节可以看14.3.4节</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadMigrationRule</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//JVM参数的dubbo.migration.file配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_MIGRATION_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//环境变量的dubbo.migration.file配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_MIGRATION_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认的迁移配置文件 dubbo-migration.yaml |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> CommonConstants<span style="color:#719e07">.</span>DEFAULT_DUBBO_MIGRATION_FILE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>localMigrationRule <span style="color:#719e07">=</span> ConfigUtils<span style="color:#719e07">.</span>loadMigrationRule<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">.</span>getClassLoaders<span style="color:#719e07">(),</span> path<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="144-初始化加载应用配置">14.4 初始化加载应用配置</h2> |
| <p>加载配置涉及到了配置优先级的处理,</p> |
| <p>下面来看加载配置代码 loadApplicationConfigs()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadApplicationConfigs</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//发布器还是不处理配置加载的逻辑还是交给配置管理器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>loadConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>配置管理器加载配置:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadConfigs</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// application config has load before starting config center |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// load dubbo.applications.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载应用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ApplicationConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load dubbo.monitors.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载监控配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>MonitorConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load dubbo.metrics.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载指标监控配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>MetricsConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load multiple config types: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// load dubbo.protocols.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载协议配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ProtocolConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load dubbo.registries.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>RegistryConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load dubbo.metadata-report.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载元数据配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>MetadataReportConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// config centers has bean loaded before starting config center |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//loadConfigsOfTypeFromProps(ConfigCenterConfig.class); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> refreshAll<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// set model name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isBlank<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getModelName<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/14-dubbo-pei-zhi-jia-zai-quan-jie-xi/">Dubbo配置加载全解析</a></p></description></item><item><title>Blog: 13-Dubbo的三大中心之配置中心</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/13/13-dubbo%E7%9A%84%E4%B8%89%E5%A4%A7%E4%B8%AD%E5%BF%83%E4%B9%8B%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83/</link><pubDate>Sat, 13 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/13/13-dubbo%E7%9A%84%E4%B8%89%E5%A4%A7%E4%B8%AD%E5%BF%83%E4%B9%8B%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83/</guid><description> |
| <h1 id="13-dubbo的三大中心之配置中心">13-Dubbo的三大中心之配置中心</h1> |
| <h2 id="131-配置中心简介">13.1 配置中心简介</h2> |
| <p>百度了一段不错的文字来介绍配置中心,我看了下肯定比我写的好多了,那我就直接拷贝过来一起看:</p> |
| <p><em>对于传统的单体应用而言,常使用配置文件来管理所有配置,比如SpringBoot的application.yml文件,但是在微服务架构中全部手动修改的话很麻烦而且不易维护。微服务的配置管理一般有以下需求:</em></p> |
| <ul> |
| <li><em><strong>集中配置管理</strong>,一个微服务架构中可能有成百上千个微服务,所以集中配置管理是很重要的。</em></li> |
| <li><em><strong>不同环境不同配置</strong>,比如数据源配置在不同环境(开发,生产,测试)中是不同的。</em></li> |
| <li><em><strong>运行期间可动态调整</strong>。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小等。</em></li> |
| <li><em><strong>配置修改后可自动更新</strong>。如配置内容发生变化,微服务可以自动更新配置。</em></li> |
| </ul> |
| <p>综上所述对于微服务架构而言,一套统一的,通用的管理配置机制是不可缺少的主要组成部分。常见的做法就是通过配置服务器进行管理。</p> |
| <p>不过对于来看这个文章的小伙伴应该大部分对配置中心都会比较了解,分布式配置中心实现简单一点就是借助Zookeeper来协助存储,变更推送,不过为了实现各种不同的业务需求,市面上已经有很多很可靠的配置中心可用了,比如我从其他地方拷贝过来的图(虽然不是最新的但是可以供大家参考下):</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/register.png" alt="在这里插入图片描述"></p> |
| <p>每个配置中心都有自己的实现,如果对配置中心感兴趣的小伙伴可以自行去对应开源项目官网查看,我们这里来看Dubbo对配置中心的支持</p> |
| <p><em><strong>多配置中心:</strong> Dubbo支持多配置中心,来 <strong>保证其中一个配置中心集群出现不可用时能够切换到另一个配置中心集群</strong> ,保证能够正常从配置中心获取全局的配置、路由规则等信息。这也能够满足配置中心在部署上适应各类高可用的部署架构模式。-来自官网</em></p> |
| <p>做中间件可能考虑更多的的不仅仅是性能,还要过多的考虑高可用,高可用怎么做呢,其实就是失效转移,主备切换,降级,降级再降级这些理论的运用,多多考虑某一个服务挂了怎么办,Dubbo的多配置中心支持增加了复杂性,不过降低了服务不可用的风险,有一定的人手的公司还是值得做的。</p> |
| <p>关于Dubbo的配置中心这里我来贴个官网的图: |
| <img src="https://dubbo.apache.org/imgs/v3/concepts/centers-config.png" alt="在这里插入图片描述"> |
| 关于官网的介绍可以自行去官网看详细内容: <a href="https://dubbo.apache.org/zh-cn/docs/concepts/registry-configcenter-metadata/">部署架构(注册中心、配置中心、元数据中心</a></p> |
| <h2 id="132-启动配置中心">13.2 启动配置中心</h2> |
| <p>在上一个博客中说到了<a href="https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/">《12-全局视野来看Dubbo3.0.8的服务启动生命周期》</a>Dubbo应用的启动过程DefaultApplicationDeployer的initialize()方法的全生命周期,在初始化方法中通过调用startConfigCenter();方法来启动配置中心的加载。后面就来详细看下:</p> |
| <p>DefaultApplicationDeployer类型的startConfigCenter()代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startConfigCenter</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load application config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载应用程序配置 (配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置,也可能是多应用,多注册中心这样的配置) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ApplicationConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// try set model name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isBlank<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getModelName<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置一下模块名字和模块描述(我们再Debug里面经常会看到这个描述信息 toString直接返回了Dubbo为我们改造的对象信息) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>tryGetApplicationName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load config centers |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载配置中心配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置,也可能是多应用,多注册中心这样的配置) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ConfigCenterConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//出于兼容性目的,如果没有明确指定配置中心,并且registryConfig的UseAConfigCenter为null或true,请使用registry作为默认配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> useRegistryAsConfigCenterIfNecessary<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// check Config Center |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//配置管理器中获取配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>ConfigCenterConfig<span style="color:#719e07">&gt;</span> configCenters <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getConfigCenters<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心配置不为空则刷新配置中心配置将其放入配置管理器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//下面开始刷新配置中心配置,如果配置中心配置为空则执行空刷新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>configCenters<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心不存在的配置刷新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ConfigCenterConfig configCenterConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConfigCenterConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> configCenterConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> configCenterConfig<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//验证配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ConfigValidationUtils<span style="color:#719e07">.</span>validateConfigCenterConfig<span style="color:#719e07">(</span>configCenterConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>configCenterConfig<span style="color:#719e07">.</span>isValid<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置合法则将配置放入配置管理器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>addConfigCenter<span style="color:#719e07">(</span>configCenterConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> configCenters <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getConfigCenters<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一个或者多个配置中心配置存在的情况下的配置刷新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ConfigCenterConfig configCenterConfig <span style="color:#719e07">:</span> configCenters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> configCenterConfig<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//验证配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ConfigValidationUtils<span style="color:#719e07">.</span>validateConfigCenterConfig<span style="color:#719e07">(</span>configCenterConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心配置不为空则将配置中心配置添加到environment中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>configCenters<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多配置中心本地动态配置对象创建CompositeDynamicConfiguration |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> CompositeDynamicConfiguration compositeDynamicConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CompositeDynamicConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置中心的相关配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ConfigCenterConfig configCenter <span style="color:#719e07">:</span> configCenters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Pass config from ConfigCenterBean to environment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将配置中心的外部化配置,更新到环境里面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> environment<span style="color:#719e07">.</span>updateExternalConfigMap<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">.</span>getExternalConfiguration<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置中心的应用配置,添加到环境里面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> environment<span style="color:#719e07">.</span>updateAppExternalConfigMap<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">.</span>getAppExternalConfiguration<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Fetch config from remote config center |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//从配置中心拉取配置添加到组合配置中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> compositeDynamicConfiguration<span style="color:#719e07">.</span>addConfiguration<span style="color:#719e07">(</span>prepareEnvironment<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置中心中的动态配置信息 设置到environment的动态配置属性中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> environment<span style="color:#719e07">.</span>setDynamicConfiguration<span style="color:#719e07">(</span>compositeDynamicConfiguration<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1321-配置管理器加载配置">13.2.1 配置管理器加载配置</h3> |
| <p>前面我们看到了配置管理器会从系统属性中加载配置这里我们来详细看下,配置往往是我们使用者比较关注的内容,</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>configManager<span style="color:#719e07">.</span>loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ApplicationConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>配置管理器加载配置代码: |
| 来自ConfigManager的父类型AbstractConfigManager中</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">loadConfigsOfTypeFromProps</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> cls<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> tmpConfigs <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取属性配置 dubbo properties in classpath |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这个配置信息回头说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> PropertiesConfiguration properties <span style="color:#719e07">=</span> environment<span style="color:#719e07">.</span>getPropertiesConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load multiple configs with id |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//多注册中心配置id查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/* |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> 搜索属性并提取指定类型的配置ID。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> 例如如下配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> # 配置信息 properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> dubbo.registries.registry1.address=xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> dubbo.registries.registry2.port=xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> # 提取配置的id extract |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> Set configIds = getConfigIds(RegistryConfig.class) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> # 提取的配置id结果 result |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> configIds: [&#34;registry1&#34;, &#34;registry2&#34;] |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> configIds <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getConfigIdsFromProps<span style="color:#719e07">(</span>cls<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> configIds<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>id <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历这些配置id 判断配置缓存(configsCache成员变量)中是否已经存在当前配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>getConfig<span style="color:#719e07">(</span>cls<span style="color:#719e07">,</span> id<span style="color:#719e07">).</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> T config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建配置对象 为配置对象初始化配置id |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> config <span style="color:#719e07">=</span> createConfig<span style="color:#719e07">(</span>cls<span style="color:#719e07">,</span> scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> config<span style="color:#719e07">.</span>setId<span style="color:#719e07">(</span>id<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;create config instance failed, id: &#34;</span> <span style="color:#719e07">+</span> id <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, type:&#34;</span> <span style="color:#719e07">+</span> cls<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> addDefaultNameConfig <span style="color:#719e07">=</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// add default name config (same as id), e.g. dubbo.protocols.rest.port=1234 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> key <span style="color:#719e07">=</span> DUBBO <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.&#34;</span> <span style="color:#719e07">+</span> AbstractConfig<span style="color:#719e07">.</span>getPluralTagName<span style="color:#719e07">(</span>cls<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.&#34;</span> <span style="color:#719e07">+</span> id <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.name&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>properties<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>key<span style="color:#719e07">)</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>setProperty<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> id<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> addDefaultNameConfig <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新配置信息 好理解点就是Dubbo配置属性重写 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> config<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将当前配置信息添加到配置缓存中configsCache成员变量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>addConfig<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> tmpConfigs<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;load config failed, id: &#34;</span> <span style="color:#719e07">+</span> id <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, type:&#34;</span> <span style="color:#719e07">+</span> cls<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;load config failed, id: &#34;</span> <span style="color:#719e07">+</span> id <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, type:&#34;</span> <span style="color:#719e07">+</span> cls<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>addDefaultNameConfig <span style="color:#719e07">&amp;&amp;</span> key <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// If none config of the type, try load single config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果没有该类型的配置,请尝试加载单个配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>getConfigs<span style="color:#719e07">(</span>cls<span style="color:#719e07">).</span>isEmpty<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load single config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;&gt;</span> configurationMaps <span style="color:#719e07">=</span> environment<span style="color:#719e07">.</span>getConfigurationMaps<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ConfigurationUtils<span style="color:#719e07">.</span>hasSubProperties<span style="color:#719e07">(</span>configurationMaps<span style="color:#719e07">,</span> AbstractConfig<span style="color:#719e07">.</span>getTypePrefix<span style="color:#719e07">(</span>cls<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> T config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> config <span style="color:#719e07">=</span> createConfig<span style="color:#719e07">(</span>cls<span style="color:#719e07">,</span> scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> config<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;create default config instance failed, type:&#34;</span> <span style="color:#719e07">+</span> cls<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>addConfig<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> tmpConfigs<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> tmpConfigs<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="1322--默认使用注册中心地址为配置中心">13.2.2 默认使用注册中心地址为配置中心</h2> |
| <p>出于兼容性目的,如果没有明确指定配置中心,并且registryConfig的UseAConfigCenter为null或true,请使用registry作为默认配置中心 |
| 调用方法useRegistryAsConfigCenterIfNecessary()来处理逻辑 |
| 我们来看下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">useRegistryAsConfigCenterIfNecessary</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//我们使用DynamicConfiguration的加载状态来决定是否已启动ConfigCenter。配置中心配置加载完成之后会初始化动态配置defaultDynamicConfiguration |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>environment<span style="color:#719e07">.</span>getDynamicConfiguration<span style="color:#719e07">().</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从配置缓存中查询是否存在config-center相关配置 ,如果已经存在配置了就无需使用注册中心的配置地址直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>configManager<span style="color:#719e07">.</span>getConfigCenters<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载注册中心相关配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>RegistryConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> defaultRegistries <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getDefaultRegistries<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在注册中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultRegistries<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> defaultRegistries |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>stream<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//判断当前注册中心是否可以作为配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>filter<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>isUsedRegistryAsConfigCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将注册中心配置映射转换为配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>registryAsConfigCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历配置中心流 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>configCenter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>configManager<span style="color:#719e07">.</span>getConfigCenter<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">.</span>getId<span style="color:#719e07">()).</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置管理器中添加配置中心,方便后去读取配置中心的配置信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>addConfigCenter<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;use registry as config-center: &#34;</span> <span style="color:#719e07">+</span> configCenter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="13221-如何判断当前注册中心是否可以为配置中心">13.2.2.1 如何判断当前注册中心是否可以为配置中心</h4> |
| <p>isUsedRegistryAsConfigCenter</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isUsedRegistryAsCenter</span><span style="color:#719e07">(</span>RegistryConfig registryConfig<span style="color:#719e07">,</span> Supplier<span style="color:#719e07">&lt;</span>Boolean<span style="color:#719e07">&gt;</span> usedRegistryAsCenter<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> String centerType<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;</span> extensionClass<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">boolean</span> supported<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个useAsConfigCenter参数是来自注册中心的配置 如果配置了这个值则以这个值为准,如果配置了false则这个注册中心不能做为配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Boolean configuredValue <span style="color:#719e07">=</span> usedRegistryAsCenter<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>configuredValue <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> <span style="color:#586e75">// If configured, take its value. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> supported <span style="color:#719e07">=</span> configuredValue<span style="color:#719e07">.</span>booleanValue<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> <span style="color:#586e75">// Or check the extension existence |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这个逻辑的话是判断下注册中心的协议是否满足要求,我们例子代码中使用的是zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String protocol <span style="color:#719e07">=</span> registryConfig<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个扩展是否支持的逻辑判断是这样的扫描扩展类 看一下当前扩展类型是否有对应协议的扩展 比如在扩展文件里面这样配置过后是支持的 protocol=xxxImpl |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//动态配置的扩展类型为:interface org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//zookeeper协议肯定是支持的因为zookeeper协议实现了这个动态配置工厂 ,这个扩展类型为ZookeeperDynamicConfigurationFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//代码位置在dubbo-configcenter-zookeeper包中的org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory扩展配置中内容为zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> supported <span style="color:#719e07">=</span> supportsExtension<span style="color:#719e07">(</span>extensionClass<span style="color:#719e07">,</span> protocol<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心走注册中心会打印一条日志 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No value is configured in the registry, the %s extension[name : %s] %s as the %s center&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">,</span> extensionClass<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">(),</span> protocol<span style="color:#719e07">,</span> supported <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;supports&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;does not support&#34;</span><span style="color:#719e07">,</span> centerType<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心走注册中心会打印一条日志 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The registry[%s] will be %s as the %s center&#34;</span><span style="color:#719e07">,</span> registryConfig<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> supported <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;used&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;not used&#34;</span><span style="color:#719e07">,</span> centerType<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> supported<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个扩展是否支持的逻辑判断是这样的扫描扩展类 看一下当前扩展类型是否有对应协议的扩展 比如在扩展文件里面这样配置过后是支持的 protocol=xxxImpl |
| 配置中心的动态配置的扩展类型为 org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory</p> |
| <p>zookeeper协议肯定是支持的因为zookeeper协议实现了这个动态配置工厂 ,这个扩展类型为ZookeeperDynamicConfigurationFactory代码位置在dubbo-configcenter-zookeeper包中的org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory扩展配置中内容为</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory |
| </span></span></code></pre></div><h4 id="13222-注册中心配置转配置中心配置">13.2.2.2 注册中心配置转配置中心配置</h4> |
| <p>这个逻辑是registryAsConfigCenter方法,我来贴一下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> ConfigCenterConfig <span style="color:#268bd2">registryAsConfigCenter</span><span style="color:#719e07">(</span>RegistryConfig registryConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心协议获取这里例子中的是zookeeper协议 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String protocol <span style="color:#719e07">=</span> registryConfig<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心端口 2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Integer port <span style="color:#719e07">=</span> registryConfig<span style="color:#719e07">.</span>getPort<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//在Dubbo中配置信息 很多情况下都以URL形式表示,这里转换后的地址为zookeeper://127.0.0.1:2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> URL<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">(),</span> registryConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成当前配置中心的id 封装之后的内容为: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//config-center-zookeeper-127.0.0.1-2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String id <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;config-center-&#34;</span> <span style="color:#719e07">+</span> protocol <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">.</span>getHost<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> port<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心配置对象创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ConfigCenterConfig cc <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConfigCenterConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//config-center-zookeeper-127.0.0.1-2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setId<span style="color:#719e07">(</span>id<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cc<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>setParameters<span style="color:#719e07">(</span><span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmptyMap<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">().</span>putAll<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">());</span> <span style="color:#586e75">// copy the parameters |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">().</span>put<span style="color:#719e07">(</span>CLIENT_KEY<span style="color:#719e07">,</span> registryConfig<span style="color:#719e07">.</span>getClient<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>protocol<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setPort<span style="color:#719e07">(</span>port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>setGroup<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个方法转换地址是修复bug用的可以看bug https://github.com/apache/dubbo/issues/6476 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setAddress<span style="color:#719e07">(</span>getRegistryCompatibleAddress<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心分组做为配置中心命名空间 这里为null |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setNamespace<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zk认证信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setUsername<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getUsername<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zk认证信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setPassword<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getPassword<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getTimeout<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>setTimeout<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getTimeout<span style="color:#719e07">().</span>longValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个属性注释中已经建议了已经弃用了默认就是false了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果配置中心被赋予最高优先级,它将覆盖所有其他配置, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setHighestPriority<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="133-配置刷新逻辑">13.3 配置刷新逻辑</h2> |
| <p>来自AbstractConfig类型的refresh()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refresh</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> refreshed<span style="color:#719e07">.</span>set<span style="color:#719e07">(</span><span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// check and init before do refresh |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//刷新之前执行的逻辑 这里并做什么逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> preProcessRefresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取当前域模型的环境信息对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Environment environment <span style="color:#719e07">=</span> getScopeModel<span style="color:#719e07">().</span>getModelEnvironment<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;&gt;</span> configurationMaps <span style="color:#719e07">=</span> environment<span style="color:#719e07">.</span>getConfigurationMaps<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Search props starts with PREFIX in order |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String preferredPrefix <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String prefix <span style="color:#719e07">:</span> getPrefixes<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ConfigurationUtils<span style="color:#719e07">.</span>hasSubProperties<span style="color:#719e07">(</span>configurationMaps<span style="color:#719e07">,</span> prefix<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> preferredPrefix <span style="color:#719e07">=</span> prefix<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>preferredPrefix <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> preferredPrefix <span style="color:#719e07">=</span> getPrefixes<span style="color:#719e07">().</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Extract sub props (which key was starts with preferredPrefix) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;&gt;</span> instanceConfigMaps <span style="color:#719e07">=</span> environment<span style="color:#719e07">.</span>getConfigurationMaps<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> preferredPrefix<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> subProperties <span style="color:#719e07">=</span> ConfigurationUtils<span style="color:#719e07">.</span>getSubProperties<span style="color:#719e07">(</span>instanceConfigMaps<span style="color:#719e07">,</span> preferredPrefix<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> InmemoryConfiguration subPropsConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span>subProperties<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isDebugEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String idOrName <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>getId<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> idOrName <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;[id=&#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getId<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;]&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> ReflectUtils<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;getName&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> idOrName <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;[name=&#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;]&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>debug<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Refreshing &#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getClass<span style="color:#719e07">().</span>getSimpleName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> idOrName <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34; with prefix [&#34;</span> <span style="color:#719e07">+</span> preferredPrefix <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;], extracted props: &#34;</span> <span style="color:#719e07">+</span> subProperties<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> assignProperties<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> environment<span style="color:#719e07">,</span> subProperties<span style="color:#719e07">,</span> subPropsConfiguration<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// process extra refresh of subclass, e.g. refresh method configs |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> processExtraRefresh<span style="color:#719e07">(</span>preferredPrefix<span style="color:#719e07">,</span> subPropsConfiguration<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to override field value of config bean: &#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to override field value of config bean: &#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> postProcessRefresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/13-config-1.png" alt="在这里插入图片描述"></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/13-config2.png" alt="在这里插入图片描述"></p> |
| <h2 id="134-配置中心配置大全">13.4 配置中心配置大全</h2> |
| <p>ConfigCenterConfig类型 |
| 下面配置信息来自官网 |
| dubbo:config-center 配置</p> |
| <p>配置中心。对应的配置类:<code>org.apache.dubbo.config.ConfigCenterConfig</code></p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>protocol</td> |
| <td>config.protocol</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>zookeeper</td> |
| <td>使用哪个配置中心:apollo、zookeeper、nacos等。 以zookeeper为例 1. 指定protocol,则address可以简化为<code>127.0.0.1:2181</code>; 2. 不指定protocol,则address取值为<code>zookeeper://127.0.0.1:2181</code></td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>address</td> |
| <td>config.address</td> |
| <td>string</td> |
| <td>必填</td> |
| <td></td> |
| <td>配置中心地址。 取值参见protocol说明</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>highest-priority</td> |
| <td>config.highestPriority</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>来自配置中心的配置项具有最高优先级,即会覆盖本地配置项。</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>namespace</td> |
| <td>config.namespace</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>通常用于多租户隔离,实际含义视具体配置中心而不同。 如: zookeeper - 环境隔离,默认值<code>dubbo</code>; apollo - 区分不同领域的配置集合,默认使用<code>dubbo</code>和<code>application</code></td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>config.cluster</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>含义视所选定的配置中心而不同。 如Apollo中用来区分不同的配置集群</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>config.group</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>含义视所选定的配置中心而不同。 nacos - 隔离不同配置集 zookeeper - 隔离不同配置集</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>check</td> |
| <td>config.check</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>当配置中心连接失败时,是否终止应用启动。</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>config-file</td> |
| <td>config.configFile</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo.properties</td> |
| <td>全局级配置文件所映射到的key zookeeper - 默认路径/dubbo/config/dubbo/dubbo.properties apollo - dubbo namespace中的dubbo.properties键</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>config.timeout</td> |
| <td>integer</td> |
| <td></td> |
| <td>3000ms</td> |
| <td>获取配置的超时时间</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>username</td> |
| <td></td> |
| <td>string</td> |
| <td></td> |
| <td></td> |
| <td>如果配置中心需要做校验,用户名 Apollo暂未启用</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>password</td> |
| <td></td> |
| <td>string</td> |
| <td></td> |
| <td></td> |
| <td>如果配置中心需要做校验,密码 Apollo暂未启用</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>parameters</td> |
| <td></td> |
| <td>Map&lt;string, string&gt;</td> |
| <td></td> |
| <td></td> |
| <td>扩展参数,用来支持不同配置中心的定制化配置参数</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>include-spring-env</td> |
| <td></td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>使用Spring框架时支持,为true时,会自动从Spring Environment中读取配置。 默认依次读取 key为dubbo.properties的配置 key为dubbo.properties的PropertySource</td> |
| <td>2.7.0+</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>原文:<a href="https://blog.elastic.link/2022/07/10/dubbo/13-dubbo-de-san-da-zhong-xin-zhi-pei-zhi-zhong-xin-yuan-ma-jie-xi/">Dubbo的三大中心之配置中心</a></p></description></item><item><title>Blog: 12 全局视野来看Dubbo3的服务启动生命周期</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/12/12-%E5%85%A8%E5%B1%80%E8%A7%86%E9%87%8E%E6%9D%A5%E7%9C%8Bdubbo3%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%90%AF%E5%8A%A8%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/</link><pubDate>Fri, 12 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/12/12-%E5%85%A8%E5%B1%80%E8%A7%86%E9%87%8E%E6%9D%A5%E7%9C%8Bdubbo3%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%90%AF%E5%8A%A8%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/</guid><description> |
| <h1 id="12-全局视野来看dubbo3的服务启动生命周期">12 全局视野来看Dubbo3的服务启动生命周期</h1> |
| <h2 id="121-启动方法简介">12.1 启动方法简介</h2> |
| <p>在说启动方法之前先把视野拉回第一章<a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">《1-从一个服务提供者的Demo说起》</a>我们的Demo代码,下面只贴一下核心代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">Application</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> service<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DemoServiceImpl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一个篇章主要说这里: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化应用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化注册中心配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化协议配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面我们介绍了Dubbo启动器DubboBootstrap类型对象的创建,又介绍了为DubboBootstrap启动器初始化各种配置信息,这一个博客就开始到了分析启动方法的位置了,Dubbo启动器借助Deployer发布器来启动和发布服务,发布器的启动过程包含了启动配置中心,加载配置,启动元数据中心,启动服务等操作都是比较重要又比较复杂的过程,这里我们先来看下启动过程的生命周期来为后面的内容做好铺垫。</p> |
| <h2 id="122-启动器启动方法的调用逻辑start">12.2 启动器启动方法的调用逻辑start()</h2> |
| <p>这里我们就直接来看DubboBootstrap的start()方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用重载的方法进行启动参数代表是否等待启动结束 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>start<span style="color:#719e07">(</span><span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>我们再来看重载的start方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">start</span><span style="color:#719e07">(</span><span style="color:#dc322f">boolean</span> wait<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个发布器是在ApplicationModel对象创建之后初始化的时候进行初始化的具体类型为DefaultApplicationDeployer |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Future future <span style="color:#719e07">=</span> applicationDeployer<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>wait<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//等待异步启动的结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> future<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动失败则抛出一个异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;await dubbo application start finish failure&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="123-应用程序发布器defaultapplicationdeployer的启动方法">12.3 应用程序发布器DefaultApplicationDeployer的启动方法</h2> |
| <p>发布器是帮助我们发布服务和引用服务的,在Dubbo3中不论是服务提供者还是服务消费者如果想要启动服务都需要走这个启动方法的逻辑,所以务必重视</p> |
| <p>我们直接来看DefaultApplicationDeployer的start()代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Future <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动锁,防止重复启动 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>startLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//发布器,状态已经设置为停止或者失败了就直接抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStopping<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isStopped<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isFailed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is stopping or stopped, can not start again&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// maybe call start again after add new module, check if any new module |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//可能在添加新模块后再次调用start,检查是否有任何新模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里遍历当前应用程序下的所有模块如果某个模块是PENDING状态则这里hasPendingModule的值为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> hasPendingModule <span style="color:#719e07">=</span> hasPendingModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//发布器状态正在启动中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStarting<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// currently, is starting, maybe both start by module and application |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// if it has new modules, start them |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//存在挂器的模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>hasPendingModule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startModules<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if it is starting, reuse previous startFuture |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//模块异步启动中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if is started and no new module, just return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果已启动且没有新模块,直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStarted<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>hasPendingModule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture<span style="color:#719e07">.</span>completedFuture<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// pending -&gt; starting : first start app |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// started -&gt; starting : re-start app |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//启动状态切换,将启动状态切换到STARTING(pending和started状态无需切换) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//核心初始化逻辑,这里主要做一些应用级别启动比如配置中心,元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动模块(我们的服务提供和服务引用是在这个模块级别的) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doStart<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> onFailed<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; start failure&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个启动方法逻辑不多 主要三个方法我们重点来看:</p> |
| <ul> |
| <li>onStarting() 这个是启动之前的状态切换</li> |
| <li>initialize() 应用的初始化逻辑 比如配置中心,元数据中心的初始化</li> |
| <li>doStart() 启动模块比如启动我们的服务提供和服务引用的)</li> |
| </ul> |
| <p>继续看后面的细节吧,代码胜千言。</p> |
| <h2 id="124-应用程序发布器对应用级别的初始化逻辑">12.4 应用程序发布器对应用级别的初始化逻辑</h2> |
| <p>这个我们先来看DefaultApplicationDeployer的初始化方法initialize():</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//状态判断 如果已经初始化过了就直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//启动锁,确保在并发调用时完成初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>startLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁 如果已经初始化过了就直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register shutdown hook |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//注册关闭钩子,这个逻辑基本每个中间件应用都必须要要做的事情了,正常关闭应用回收资源,一般没这个逻辑情况下容易出现一些异常,让我们开发人员很疑惑,而这个逻辑往往并不好处理的干净。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerShutdownHook<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动配置中心,感觉Dubbo3耦合了这个玩意 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startConfigCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载配置,一般配置信息当前机器的来源:环境变量,JVM启动参数,配置文字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadApplicationConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块发布器 (发布服务提供和服务引用使用) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initModuleDeployers<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// @since 2.7.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//启动元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startMetadataCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化完成 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has been initialized!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个是个生命周期整体概览的方法,将具体逻辑拆分到各个子方法中,是代码重构的一种策略,上面注释也很清楚了就不细说了,上面每个方法在后面会有单独的博客来分析。</p> |
| <h2 id="125-应用下模块的启动服务的发布与引用">12.5 应用下模块的启动(服务的发布与引用)</h2> |
| <p>我们回过头来详细看DefaultApplicationDeployer的doStart()代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doStart</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 启动模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startModules<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>DefaultApplicationDeployer的 startModules()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startModules</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ensure init and start internal module first |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//确保初始化并首先启动内部模块,Dubbo3中将模块分为内部和外部,内部是核心代码已经提供的一些服务比如元数据服务,外部是我们自己写的服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> prepareInternalModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// filter and start pending modules, ignore new module during starting, throw exception of module start |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//启动所有的模块 (启动所有的服务) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ModuleModel moduleModel <span style="color:#719e07">:</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>applicationModel<span style="color:#719e07">.</span>getModuleModels<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个状态默认就是PENDING的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">().</span>isPending<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块启动器,发布服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> moduleModel<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">().</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个模块的启动其实就是用来启动服务的 先启动内部服务,再启动外部服务 |
| 内部服务有个元数据服务Dubbo3中每个服务都可以对外提供服务的元数据信息,来简化服务配置,不论是内部服务还是外部服务调用的代码逻辑都是模块发布器ModuleDeployer的start()方法,接下来我们详细看下模块发布器的生命周期函数。</p> |
| <h2 id="126-模块发布器发布服务的过程">12.6 模块发布器发布服务的过程</h2> |
| <p>前面我们说到了所有的服务都是经过模块发布器,ModuleDeployer的start()方法来启动的,那我们接下来就来看看这个模块发布器的启动方法。</p> |
| <p>ModuleDeployer的start()方法代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> Future <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块发布器已经停止或者启动失败则直接抛出异常返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStopping<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isStopped<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isFailed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is stopping or stopped, can not start again&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动中或者已经启动了则直接返回一个Future对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStarting<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isStarted<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//切换模块启动状态为STARTING |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onModuleStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// initialize |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果应用未初始化则初始化(非正常逻辑) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块发布器进行初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//暴露服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// prepare application instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// exclude internal module to avoid wait itself |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModel <span style="color:#719e07">!=</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getInternalModule<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer<span style="color:#719e07">.</span>prepareInternalModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// refer services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//引用服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if no async export/refer services, just set started |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//非异步启动则直接切换状态为STARTED |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>asyncExportingFutures<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> asyncReferringFutures<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> onModuleStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果是异步的则等待服务发布和服务引用异步回调 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> frameworkExecutorRepository<span style="color:#719e07">.</span>getSharedExecutor<span style="color:#719e07">().</span>submit<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// wait for export finish |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> waitExportFinish<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// wait for refer finish |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> waitReferFinish<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;wait for export/refer services occurred an exception&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//异步回调完成 所有服务都启动了,再切换状态 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onModuleStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> onModuleFailed<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; start failed: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>好了整体的服务启动生命周期就如上代码,后续我们再详细来看每个细节。</p> |
| <h2 id="127-发布器简介">12.7 发布器简介</h2> |
| <p>前面主要说了应用和模块的发布器的启动和初始化,下面简单了解下它们的关系,如下所示 |
| <img src="https://img-blog.csdnimg.cn/37e7c05796ab4b38aa7658377e16c0aa.png" alt="在这里插入图片描述"> |
| 可以发布器主要包含</p> |
| <ul> |
| <li>应用的发布器ApplicationDeployer用于初始化并启动应用程序实例</li> |
| <li>模块发布器ModuleDeployer 模块(服务)的导出/引用服务</li> |
| </ul> |
| <p>两种发布器有各自的接口,他们都继承了抽象的发布器AbstractDeployer 封装了一些公共的操作比如状态切换,状态查询的逻辑。</p> |
| <p>另外我们再来看下发布过程的状态枚举DeployState如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">enum</span> DeployState <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Unknown state |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> UNKNOWN<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Pending, wait for start |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> PENDING<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Starting |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> STARTING<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Started |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> STARTED<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Stopping |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> STOPPING<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Stopped |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> STOPPED<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Failed |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> FAILED |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>Dubbo这一块后续可以优化以下,这里的状态切换全部耦合在一起了,可以考虑使用状态机将状态与行为解耦。</p> |
| <p>原文:<a href="https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/">Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</a></p></description></item><item><title>Blog: 11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/11/11-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E5%8D%8F%E8%AE%AE%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFprotocolconfig/</link><pubDate>Thu, 11 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/11/11-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E5%8D%8F%E8%AE%AE%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFprotocolconfig/</guid><description> |
| <h1 id="11-dubbo启动器dubbobootstrap添加协议配置信息protocolconfig">11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</h1> |
| <h2 id="111-简介">11.1 简介</h2> |
| <p>先贴个代码用来参考:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>上个博客我们说了 RegistryConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码协议配置信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span></code></pre></div><h2 id="112--协议的配置相关">11.2 协议的配置相关</h2> |
| <p>下面的配置来源于官网</p> |
| <p>服务提供者协议配置。对应的配置类: org.apache.dubbo.config.ProtocolConfig。同时,如果需要支持多协议,可以声明多个 <a href="dubbo:protocol">dubbo:protocol</a> 标签,并在 <a href="dubbo:service">dubbo:service</a> 中通过 protocol 属性指定使用的协议。</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>id</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>配置关联</td> |
| <td>协议BeanId,可以在&lt;dubbo:service protocol=&quot;&quot;&gt;中引用此ID,如果ID不填,缺省和name属性值一样,重复则在name后加序号。</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>name</td> |
| <td><protocol></td> |
| <td>string</td> |
| <td><strong>必填</strong></td> |
| <td>dubbo</td> |
| <td>性能调优</td> |
| <td>协议名称</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>port</td> |
| <td><port></td> |
| <td>int</td> |
| <td>可选</td> |
| <td>dubbo协议缺省端口为20880,rmi协议缺省端口为1099,http和hessian协议缺省端口为80;如果<strong>没有</strong>配置port,则自动采用默认端口,如果配置为**-1**,则会分配一个没有被占用的端口。Dubbo 2.4.0+,分配的端口在协议缺省端口的基础上增长,确保端口段可控。</td> |
| <td>服务发现</td> |
| <td>服务端口</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>host</td> |
| <td><host></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>自动查找本机IP</td> |
| <td>服务发现</td> |
| <td>-服务主机名,多网卡选择或指定VIP及域名时使用,为空则自动查找本机IP,-建议不要配置,让Dubbo自动获取本机IP</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>threadpool</td> |
| <td>threadpool</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>fixed</td> |
| <td>性能调优</td> |
| <td>线程池类型,可选:fixed/cached</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>threads</td> |
| <td>threads</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>200</td> |
| <td>性能调优</td> |
| <td>服务线程池大小(固定大小)</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>iothreads</td> |
| <td>threads</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>cpu个数+1</td> |
| <td>性能调优</td> |
| <td>io线程池大小(固定大小)</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>accepts</td> |
| <td>accepts</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>服务提供方最大可接受连接数</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>payload</td> |
| <td>payload</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>8388608(=8M)</td> |
| <td>性能调优</td> |
| <td>请求及响应数据包大小限制,单位:字节</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>codec</td> |
| <td>codec</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>性能调优</td> |
| <td>协议编码方式</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>serialization</td> |
| <td>serialization</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为hessian2,rmi协议缺省为java,http协议缺省为json</td> |
| <td>性能调优</td> |
| <td>协议序列化方式,当协议支持多种序列化方式时使用,比如:dubbo协议的dubbo,hessian2,java,compactedjava,以及http协议的json等</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>accesslog</td> |
| <td>accesslog</td> |
| <td>string/boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>path</td> |
| <td><path></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>提供者上下文路径,为服务path的前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>transporter</td> |
| <td>transporter</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为netty</td> |
| <td>性能调优</td> |
| <td>协议的服务端和客户端实现类型,比如:dubbo协议的mina,netty等,可以分拆为server和client配置</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>server</td> |
| <td>server</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为netty,http协议缺省为servlet</td> |
| <td>性能调优</td> |
| <td>协议的服务器端实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>client</td> |
| <td>client</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为netty</td> |
| <td>性能调优</td> |
| <td>协议的客户端实现类型,比如:dubbo协议的mina,netty等</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>dispatcher</td> |
| <td>dispatcher</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为all</td> |
| <td>性能调优</td> |
| <td>协议的消息派发方式,用于指定线程模型,比如:dubbo协议的all, direct, message, execution, connection等</td> |
| <td>2.1.0以上版本</td> |
| </tr> |
| <tr> |
| <td>queues</td> |
| <td>queues</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>线程池队列大小,当线程池满时,排队等待执行的队列大小,建议不要设置,当线程池满时应立即失败,重试其它服务提供机器,而不是排队,除非有特殊需求。</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>charset</td> |
| <td>charset</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>UTF-8</td> |
| <td>性能调优</td> |
| <td>序列化编码</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>buffer</td> |
| <td>buffer</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>8192</td> |
| <td>性能调优</td> |
| <td>网络读写缓冲区大小</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>heartbeat</td> |
| <td>heartbeat</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>心跳间隔,对于长连接,当物理层断开时,比如拔网线,TCP的FIN消息来不及发送,对方收不到断开事件,此时需要心跳来帮助检查连接是否已断开</td> |
| <td>2.0.10以上版本</td> |
| </tr> |
| <tr> |
| <td>telnet</td> |
| <td>telnet</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>所支持的telnet命令,多个命令用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>register</td> |
| <td>register</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>该协议的服务是否注册到注册中心</td> |
| <td>2.0.8以上版本</td> |
| </tr> |
| <tr> |
| <td>contextpath</td> |
| <td>contextpath</td> |
| <td>String</td> |
| <td>可选</td> |
| <td>缺省为空串</td> |
| <td>服务治理</td> |
| <td></td> |
| <td>2.0.6以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>变量</th> |
| <th>类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>threadname</td> |
| <td>String</td> |
| <td>线程池名称</td> |
| </tr> |
| <tr> |
| <td>corethreads</td> |
| <td>Integer</td> |
| <td>线程池核心线程大小</td> |
| </tr> |
| <tr> |
| <td>alive</td> |
| <td>Integer</td> |
| <td>线程池keepAliveTime,默认单位时间单位。毫秒</td> |
| </tr> |
| <tr> |
| <td>exchanger</td> |
| <td>String</td> |
| <td>交换器配置信息如何交换</td> |
| </tr> |
| <tr> |
| <td>prompt</td> |
| <td>String</td> |
| <td>命令行提示符</td> |
| </tr> |
| <tr> |
| <td>status</td> |
| <td>String</td> |
| <td>状态检查</td> |
| </tr> |
| <tr> |
| <td>sslEnabled</td> |
| <td>Boolean</td> |
| <td>ssl是否启用</td> |
| </tr> |
| </tbody> |
| </table> |
| <h2 id="113-协议配置对象创建与添加">11.3 协议配置对象创建与添加</h2> |
| <p>前面例子中调用的代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span></code></pre></div><p>这里我们配置了协议类型为dubbo 端口为-1则会分配一个没有被占用的端口</p> |
| <p>继续看下DubboBootstrap的protocol方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">protocol</span><span style="color:#719e07">(</span>ProtocolConfig protocolConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置信息转List |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> protocols<span style="color:#719e07">(</span>singletonList<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看protocols方法 ,这个代码与前面两个博客中看到的向配置管理器添加配置对象的逻辑是一样的 |
| 这里就不说了可以看前面的博客<a href="https://blog.elastic.link/2022/07/10/dubbo/9-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-ying-yong-cheng-xu-de-pei-zhi-xin-xi-applicationconfig/">《9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig》</a></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">protocols</span><span style="color:#719e07">(</span>List<span style="color:#719e07">&lt;</span>ProtocolConfig<span style="color:#719e07">&gt;</span> protocolConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>protocolConfigs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ProtocolConfig protocolConfig <span style="color:#719e07">:</span> protocolConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> protocolConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> configManager<span style="color:#719e07">.</span>addProtocol<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文:<a href="https://blog.elastic.link/2022/07/10/dubbo/11-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-xie-yi-pei-zhi-xin-xi-protocolconfig/">Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</a></p></description></item><item><title>Blog: 10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/10/10-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFregistryconfig/</link><pubDate>Wed, 10 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/10/10-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFregistryconfig/</guid><description> |
| <h1 id="10-dubbo启动器dubbobootstrap添加注册中心配置信息registryconfig">10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig</h1> |
| <h2 id="101-简介">10.1 简介</h2> |
| <p>先贴个代码用来参考:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>上个博客我们说了启动器ApplicationConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码注册中心配置信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span></code></pre></div><h2 id="102--注册中心的配置相关">10.2 注册中心的配置相关</h2> |
| <p>下面的配置来源于官网</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>id</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>配置关联</td> |
| <td>注册中心引用BeanId,可以在&lt;dubbo:service registry=&quot;&quot;&gt;或&lt;dubbo:reference registry=&quot;&quot;&gt;中引用此ID</td> |
| <td>1.0.16以上版本</td> |
| </tr> |
| <tr> |
| <td>address</td> |
| <td><a href="host:port">host:port</a></td> |
| <td>string</td> |
| <td><strong>必填</strong></td> |
| <td></td> |
| <td>服务发现</td> |
| <td>注册中心服务器地址,如果地址没有端口缺省为9090,同一集群内的多个地址用逗号分隔,如:ip:port,ip:port,不同集群的注册中心,请配置多个<a href="dubbo:registry">dubbo:registry</a>标签</td> |
| <td>1.0.16以上版本</td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td><protocol></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>服务发现</td> |
| <td>注册中心地址协议,支持<code>dubbo</code>, <code>multicast</code>, <code>zookeeper</code>, <code>redis</code>, <code>consul(2.7.1)</code>, <code>sofa(2.7.2)</code>, <code>etcd(2.7.2)</code>, <code>nacos(2.7.2)</code>等协议</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>port</td> |
| <td><port></td> |
| <td>int</td> |
| <td>可选</td> |
| <td>9090</td> |
| <td>服务发现</td> |
| <td>注册中心缺省端口,当address没有带端口时使用此端口做为缺省值</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>username</td> |
| <td><username></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>登录注册中心用户名,如果注册中心不需要验证可不填</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>password</td> |
| <td><password></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>登录注册中心密码,如果注册中心不需要验证可不填</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>transport</td> |
| <td>registry.transporter</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>netty</td> |
| <td>性能调优</td> |
| <td>网络传输方式,可选mina,netty</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>registry.timeout</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>5000</td> |
| <td>性能调优</td> |
| <td>注册中心请求超时时间(毫秒)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>session</td> |
| <td>registry.session</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>60000</td> |
| <td>性能调优</td> |
| <td>注册中心会话超时时间(毫秒),用于检测提供者非正常断线后的脏数据,比如用心跳检测的实现,此时间就是心跳间隔,不同注册中心实现不一样。</td> |
| <td>2.1.0以上版本</td> |
| </tr> |
| <tr> |
| <td>file</td> |
| <td>registry.file</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>使用文件缓存注册中心地址列表及服务提供者列表,应用重启时将基于此文件恢复,注意:两个注册中心不能使用同一文件存储</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>wait</td> |
| <td>registry.wait</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>停止时等待通知完成时间(毫秒)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>check</td> |
| <td>check</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>注册中心不存在时,是否报错</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>register</td> |
| <td>register</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>是否向此注册中心注册服务,如果设为false,将只订阅,不注册</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>subscribe</td> |
| <td>subscribe</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>是否向此注册中心订阅服务,如果设为false,将只注册,不订阅</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>dynamic</td> |
| <td>dynamic</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>服务是否动态注册,如果设为false,注册后将显示为disable状态,需人工启用,并且服务提供者停止时,也不会自动取消注册,需人工禁用。</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>group</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>服务治理</td> |
| <td>服务注册分组,跨组的服务不会相互影响,也无法相互调用,适用于环境隔离。</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>simplified</td> |
| <td>simplified</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>注册到注册中心的URL是否采用精简模式的(与低版本兼容)</td> |
| <td>2.7.0以上版本</td> |
| </tr> |
| <tr> |
| <td>extra-keys</td> |
| <td>extraKeys</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>在simplified=true时,extraKeys允许你在默认参数外将额外的key放到URL中,格式:“interface,key1,key2”。</td> |
| <td>2.7.0以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>变量</th> |
| <th>类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>server</td> |
| <td>String</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>client</td> |
| <td>String</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>String</td> |
| <td>影响流量在注册中心之间的分布,在订阅多个注册中心时很有用,可用选项:1。区域感知,特定类型的流量总是根据流量的来源进入一个注册表。</td> |
| </tr> |
| <tr> |
| <td>zone</td> |
| <td>String</td> |
| <td>注册表所属的区域,通常用于隔离流量</td> |
| </tr> |
| <tr> |
| <td>parameters</td> |
| <td>Map&lt;String, String&gt;</td> |
| <td>自定义参数</td> |
| </tr> |
| <tr> |
| <td>useAsConfigCenter</td> |
| <td>Boolean</td> |
| <td>该地址是否用作配置中心</td> |
| </tr> |
| <tr> |
| <td>useAsMetadataCenter</td> |
| <td>Boolean</td> |
| <td>该地址是否用作远程元数据中心</td> |
| </tr> |
| <tr> |
| <td>accepts</td> |
| <td>String</td> |
| <td>此注册表接受的rpc协议列表,例如“dubbo,rest”</td> |
| </tr> |
| <tr> |
| <td>preferred</td> |
| <td>Boolean</td> |
| <td>如果设置为true,则始终首先使用此注册表,这在订阅多个注册表时非常有用</td> |
| </tr> |
| <tr> |
| <td>weight</td> |
| <td>Integer</td> |
| <td>影响注册中心之间的流量分布,当订阅多个注册中心仅在未指定首选注册中心时才生效时,此功能非常有用。</td> |
| </tr> |
| <tr> |
| <td>registerMode</td> |
| <td>String</td> |
| <td>注册模式:实例级,接口级,所有</td> |
| </tr> |
| <tr> |
| <td>enableEmptyProtection</td> |
| <td>Boolean</td> |
| <td>收到的空url地址列表和空保护被禁用,将清除当前可用地址</td> |
| </tr> |
| </tbody> |
| </table> |
| <h2 id="103-注册中心配置对象创建与添加">10.3 注册中心配置对象创建与添加</h2> |
| <p>前面例子中调用的代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span></code></pre></div><p>首先我们要来看的是RegistryConfig类型的构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">RegistryConfig</span><span style="color:#719e07">(</span>String address<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> setAddress<span style="color:#719e07">(</span>address<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看setAddress方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setAddress</span><span style="color:#719e07">(</span>String address<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//保存地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>address <span style="color:#719e07">=</span> address<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//下面是支持将参数在url地址后面 比如用户名,密码,协议,端口,这几个参数提前做解析放入成员变量中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>address <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//地址转Dubbo的URL对象 这个URL是Dubbo自行实现的URL封装信息的类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> URL<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>address<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Refactor since 2.7.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//值不存在时候更新属性,非常巧妙的代码 重构了多个if判断 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//第一个参数值不存在则调用第二个方法,第二个方法的参数为第三方方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> updatePropertyIfAbsent<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>getUsername<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">::</span>setUsername<span style="color:#719e07">,</span> url<span style="color:#719e07">.</span>getUsername<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>getPassword<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">::</span>setPassword<span style="color:#719e07">,</span> url<span style="color:#719e07">.</span>getPassword<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>getProtocol<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">::</span>setProtocol<span style="color:#719e07">,</span> url<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>getPort<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">::</span>setPort<span style="color:#719e07">,</span> url<span style="color:#719e07">.</span>getPort<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//移除掉url中的backup自定义参数 (备份的注册中心地址) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> params <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmptyMap<span style="color:#719e07">(</span>params<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> params<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>BACKUP_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将自定义参数存储到成员变量中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> updateParameters<span style="color:#719e07">(</span>params<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception ignored<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>然后再回过头来看DubboBootstrap的registry方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">registry</span><span style="color:#719e07">(</span>RegistryConfig registryConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将applicationModel对象设置给注册中心配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将注册中心配置对象添加到配置管理器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>addRegistry<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>直接来看配置管理器configManager的添加注册中心配置addRegistry方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addRegistry</span><span style="color:#719e07">(</span>RegistryConfig registryConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> addConfig<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>configManager 的addConfig方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">final</span> <span style="color:#719e07">&lt;</span>T <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> T <span style="color:#268bd2">addConfig</span><span style="color:#719e07">(</span>AbstractConfig config<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore MethodConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//检查当前配置管理器支持管理的配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isSupportConfigType<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unsupported config type: &#34;</span> <span style="color:#719e07">+</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> config<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中是否存在 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> AbstractConfig<span style="color:#719e07">&gt;</span> configsMap <span style="color:#719e07">=</span> configsCache<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>getTagName<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()),</span> type <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不是服务级接口配置则直接从缓存中读取到配置之后直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// fast check duplicated equivalent config before write lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!(</span>config <span style="color:#719e07">instanceof</span> ReferenceConfigBase <span style="color:#719e07">||</span> config <span style="color:#719e07">instanceof</span> ServiceConfigBase<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>AbstractConfig value <span style="color:#719e07">:</span> configsMap<span style="color:#719e07">.</span>values<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>value<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>config<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> value<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// lock by config type |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//添加配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>configsMap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> addIfAbsent<span style="color:#719e07">(</span>config<span style="color:#719e07">,</span> configsMap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigManager配置管理器的addIfAbsent方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>C <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> C <span style="color:#268bd2">addIfAbsent</span><span style="color:#719e07">(</span>C config<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> C<span style="color:#719e07">&gt;</span> configsMap<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置信息为空直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> configsMap <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// find by value |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//根据配置规则判断,配置存在则返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Optional<span style="color:#719e07">&lt;</span>C<span style="color:#719e07">&gt;</span> prevConfig <span style="color:#719e07">=</span> findDuplicatedConfig<span style="color:#719e07">(</span>configsMap<span style="color:#719e07">,</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>prevConfig<span style="color:#719e07">.</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> prevConfig<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成配置key |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>getId<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>key <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">do</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generate key if id is not set |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> key <span style="color:#719e07">=</span> generateConfigId<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>configsMap<span style="color:#719e07">.</span>containsKey<span style="color:#719e07">(</span>key<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不相同的配置key重复则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> C existedConfig <span style="color:#719e07">=</span> configsMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>existedConfig <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isEquals<span style="color:#719e07">(</span>existedConfig<span style="color:#719e07">,</span> config<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String type <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">().</span>getSimpleName<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Duplicate %s found, there already has one default %s or more than two %ss have the same id, &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;you can try to give each %s a different id, override previous config with later config. id: %s, prev: %s, later: %s&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> existedConfig<span style="color:#719e07">,</span> config<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// override existed config if any |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将配置对象存入configsMap对象中,configsMap来源于configsCache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configsMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/10-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-zhu-ce-zhong-xin-pei-zhi-xin-xi-registryconfig//">&laquo;Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig&raquo;</a></p></description></item><item><title>Blog: 9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/09/9-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%9A%84%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFapplicationconfig/</link><pubDate>Tue, 09 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/09/9-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%9A%84%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFapplicationconfig/</guid><description> |
| <h1 id="9-dubbo启动器dubbobootstrap添加应用程序的配置信息applicationconfig">9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig</h1> |
| <h2 id="91-简介">9.1 简介</h2> |
| <p>先贴个代码用来参考:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>上个博客我们说了启动器对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span></code></pre></div><h2 id="92-应用程序applicationconfig的配置信息">9.2 应用程序ApplicationConfig的配置信息</h2> |
| <p>ApplicationConfig的构造器比较简单就是为他的成员变量name赋值来标识这个应用程序的名字 |
| 下面我们直接参考下官网的配置表格:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>name</td> |
| <td>application</td> |
| <td>string</td> |
| <td><strong>必填</strong></td> |
| <td></td> |
| <td>服务治理</td> |
| <td>当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样,此参数不是匹配条件,你当前项目叫什么名字就填什么,和提供者消费者角色无关,比如:kylin应用调用了morgan应用的服务,则kylin项目配成kylin,morgan项目配成morgan,可能kylin也提供其它服务给别人使用,但kylin项目永远配成kylin,这样注册中心将显示kylin依赖于morgan</td> |
| <td>1.0.16以上版本</td> |
| </tr> |
| <tr> |
| <td>version</td> |
| <td>application.version</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>当前应用的版本</td> |
| <td>2.2.0以上版本</td> |
| </tr> |
| <tr> |
| <td>owner</td> |
| <td>owner</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>应用负责人,用于服务治理,请填写负责人公司邮箱前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>organization</td> |
| <td>organization</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>组织名称(BU或部门),用于注册中心区分服务来源,此配置项建议不要使用autoconfig,直接写死在配置中,比如china,intl,itu,crm,asc,dw,aliexpress等</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>architecture</td> |
| <td>architecture</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>用于服务分层对应的架构。如,intl、china。不同的架构使用不同的分层。</td> |
| <td>2.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>environment</td> |
| <td>environment</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>应用环境,如:develop/test/product,不同环境使用不同的缺省值,以及作为只用于开发测试功能的限制条件</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>compiler</td> |
| <td>compiler</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>javassist</td> |
| <td>性能优化</td> |
| <td>Java字节码编译器,用于动态类的生成,可选:jdk或javassist</td> |
| <td>2.1.0以上版本</td> |
| </tr> |
| <tr> |
| <td>logger</td> |
| <td>logger</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>slf4j</td> |
| <td>性能优化</td> |
| <td>日志输出方式,可选:slf4j,jcl,log4j,log4j2,jdk</td> |
| <td>2.2.0以上版本</td> |
| </tr> |
| <tr> |
| <td>metadata-type</td> |
| <td>metadata-type</td> |
| <td>String</td> |
| <td>可选</td> |
| <td>local</td> |
| <td>服务治理</td> |
| <td>metadata 传递方式,是以 Provider 视角而言的,Consumer 侧配置无效,可选值有: remote - Provider 把 metadata 放到远端注册中心,Consumer 从注册中心获取 local - Provider 把 metadata 放在本地,Consumer 从 Provider 处直接获取</td> |
| <td>2.7.6以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>官网的配置很详细了上面有一些属性是值得注意的比如这个name,compiler,logger,metadata-type 我们可能要多看下默认值是什么,方便我们在使用过程中遇到问题的排查</p> |
| <p>常用的属性参考官网的表格已经足够了,不过上面的属性不是列举了所有的属性,后续应该官方文档回更新: |
| 我这里把缺失的一些属性列举出来:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>变量</th> |
| <th>类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>registries</td> |
| <td>List<RegistryConfig></td> |
| <td>应用级注册中心列表</td> |
| </tr> |
| <tr> |
| <td>registryIds</td> |
| <td>String</td> |
| <td>注册中心id列表</td> |
| </tr> |
| <tr> |
| <td>monitor</td> |
| <td>MonitorConfig</td> |
| <td>应用级监控配置</td> |
| </tr> |
| <tr> |
| <td>dumpDirectory</td> |
| <td>String</td> |
| <td>保存线程转储的目录</td> |
| </tr> |
| <tr> |
| <td>qosEnable</td> |
| <td>Boolean</td> |
| <td>是否启用qos</td> |
| </tr> |
| <tr> |
| <td>qosHost</td> |
| <td>String</td> |
| <td>要侦听的qos主机地址</td> |
| </tr> |
| <tr> |
| <td>qosPort</td> |
| <td>Integer</td> |
| <td>要侦听的qos端口</td> |
| </tr> |
| <tr> |
| <td>qosAcceptForeignIp</td> |
| <td>Boolean</td> |
| <td>qos是否接收外部IP</td> |
| </tr> |
| <tr> |
| <td>parameters</td> |
| <td>Map&lt;String, String&gt;</td> |
| <td>自定义参数</td> |
| </tr> |
| <tr> |
| <td>shutwait</td> |
| <td>String</td> |
| <td>应用程序关闭时间</td> |
| </tr> |
| <tr> |
| <td>hostname</td> |
| <td>String</td> |
| <td>主机名</td> |
| </tr> |
| <tr> |
| <td>registerConsumer</td> |
| <td>Boolean</td> |
| <td>用于控制是否将实例注册到注册表。仅当实例是纯消费者时才设置为“false”。</td> |
| </tr> |
| <tr> |
| <td>repository</td> |
| <td>String</td> |
| <td>没找到哪里用了</td> |
| </tr> |
| <tr> |
| <td>enableFileCache</td> |
| <td>Boolean</td> |
| <td>是否开启本地文件缓存</td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td>String</td> |
| <td>此应用程序的首选协议(名称)适用于难以确定哪个是首选协议的地方</td> |
| </tr> |
| <tr> |
| <td>metadataServiceProtocol</td> |
| <td>String</td> |
| <td>用于点对点的元数据传输的协议</td> |
| </tr> |
| <tr> |
| <td>metadataServicePort</td> |
| <td>Integer</td> |
| <td>元数据服务端口号,用于服务发现</td> |
| </tr> |
| <tr> |
| <td>livenessProbe</td> |
| <td>String</td> |
| <td>Liveness 存活探针 用于设置qos中探测器的扩展</td> |
| </tr> |
| <tr> |
| <td>readinessProbe</td> |
| <td>String</td> |
| <td>Readiness 就绪探针</td> |
| </tr> |
| <tr> |
| <td>startupProbe</td> |
| <td>String</td> |
| <td>Startup 启动探针</td> |
| </tr> |
| <tr> |
| <td>registerMode</td> |
| <td>String</td> |
| <td>注册模式,实例级,接口集,所有</td> |
| </tr> |
| <tr> |
| <td>enableEmptyProtection</td> |
| <td>Boolean</td> |
| <td>接收到的空url地址列表和空保护被禁用,将清除当前可用地址</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>这里我们先来简单了解下这个实体类型的基本配置,直接看配置可能不太好理解,后面我们讲到每个配置的时候可以回来参考一下</p> |
| <h2 id="应用程序配置对象添加到启动器中的配置管理器中">应用程序配置对象添加到启动器中的配置管理器中</h2> |
| <p>了解了配置信息再回过头来看下这个配置信息如何存放到启动器里面的:</p> |
| <p>我们的Demo调用代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span></code></pre></div><p>DubboBootstrap的application方法设置一个应用程序配置ApplicationConfig对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">application</span><span style="color:#719e07">(</span>ApplicationConfig applicationConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将启动器构造器中初始化的默认应用程序模型对象传递给配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置信息添加到配置管理器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>setApplication<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigManager配置管理器的setApplication方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@DisableInject</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setApplication</span><span style="color:#719e07">(</span>ApplicationConfig application<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> addConfig<span style="color:#719e07">(</span>application<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigManager配置管理器的addConfig方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">final</span> <span style="color:#719e07">&lt;</span>T <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> T <span style="color:#268bd2">addConfig</span><span style="color:#719e07">(</span>AbstractConfig config<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore MethodConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//检查当前配置管理器支持管理的配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isSupportConfigType<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unsupported config type: &#34;</span> <span style="color:#719e07">+</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> config<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中是否存在 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> AbstractConfig<span style="color:#719e07">&gt;</span> configsMap <span style="color:#719e07">=</span> configsCache<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>getTagName<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()),</span> type <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fast check duplicated equivalent config before write lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//不是服务级配置则直接从缓存中读取到配置之后直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!(</span>config <span style="color:#719e07">instanceof</span> ReferenceConfigBase <span style="color:#719e07">||</span> config <span style="color:#719e07">instanceof</span> ServiceConfigBase<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>AbstractConfig value <span style="color:#719e07">:</span> configsMap<span style="color:#719e07">.</span>values<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>value<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>config<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> value<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// lock by config type |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//添加配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>configsMap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> addIfAbsent<span style="color:#719e07">(</span>config<span style="color:#719e07">,</span> configsMap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigManager配置管理器的addIfAbsent方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>C <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> C <span style="color:#268bd2">addIfAbsent</span><span style="color:#719e07">(</span>C config<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> C<span style="color:#719e07">&gt;</span> configsMap<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置信息为空直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> configsMap <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// find by value |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//根据配置规则判断,配置存在则返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Optional<span style="color:#719e07">&lt;</span>C<span style="color:#719e07">&gt;</span> prevConfig <span style="color:#719e07">=</span> findDuplicatedConfig<span style="color:#719e07">(</span>configsMap<span style="color:#719e07">,</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>prevConfig<span style="color:#719e07">.</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> prevConfig<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成配置key |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>getId<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>key <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">do</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generate key if id is not set |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> key <span style="color:#719e07">=</span> generateConfigId<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>configsMap<span style="color:#719e07">.</span>containsKey<span style="color:#719e07">(</span>key<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不相同的配置key重复则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> C existedConfig <span style="color:#719e07">=</span> configsMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>existedConfig <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isEquals<span style="color:#719e07">(</span>existedConfig<span style="color:#719e07">,</span> config<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String type <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">().</span>getSimpleName<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Duplicate %s found, there already has one default %s or more than two %ss have the same id, &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;you can try to give each %s a different id, override previous config with later config. id: %s, prev: %s, later: %s&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> existedConfig<span style="color:#719e07">,</span> config<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// override existed config if any |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将配置对象存入configsMap对象中,configsMap来源于configsCache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configsMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/9-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-ying-yong-cheng-xu-de-pei-zhi-xin-xi-applicationconfig/">&laquo;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&raquo;</a></p></description></item><item><title>Blog: 8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/08/8-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E5%80%9F%E5%8A%A9%E5%8F%8C%E9%87%8D%E6%A0%A1%E9%AA%8C%E9%94%81%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E8%BF%9B%E8%A1%8C%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96/</link><pubDate>Mon, 08 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/08/8-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E5%80%9F%E5%8A%A9%E5%8F%8C%E9%87%8D%E6%A0%A1%E9%AA%8C%E9%94%81%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E8%BF%9B%E8%A1%8C%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96/</guid><description> |
| <h1 id="8-dubbo启动器dubbobootstrap借助双重校验锁的单例模式进行对象的初始化">8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化</h1> |
| <h2 id="81-启动器简介">8.1 启动器简介</h2> |
| <p>在说启动器之前先把视野拉回第一章<a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">《1-从一个服务提供者的Demo说起》</a>我们的Demo代码,下面只贴一下核心代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">Application</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> service<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DemoServiceImpl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一个篇章主要说这里: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>Dubbo3 往云原生的方向走自然要针对云原生应用的应用启动,应用运行,应用发布等信息做一些建模,这个DubboBootstrap就是用来启动Dubbo服务的.类似于Netty的Bootstrap类型和ServerBootstrap启动器</p> |
| <h2 id="82-双重校验锁的单例模式创建启动器对象的">8.2 双重校验锁的单例模式创建启动器对象的</h2> |
| <p>Dubbo的bootstrap类为啥要用单例模式:</p> |
| <p>通过调用静态方法getInstance()获取单例实例。之所以设计为单例,是因为Dubbo中的一些类(如ExtensionLoader)只为每个进程设计一个实例。</p> |
| <p>下面就来直接看代码吧,代码胜千言: |
| 对象的调用代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>DubboBootstrap获取对象的getInstance()方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> DubboBootstrap <span style="color:#268bd2">getInstance</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁第一次判断空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为空都进行排队 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>DubboBootstrap<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁第二次判断空 上面为空的都排队了这里得判断下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用重载方法获取对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">(</span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>DubboBootstrap获取对象重载的getInstance(ApplicationModel applicationModel)方法:</p> |
| <p><em>computeIfAbsent() 方法对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hashMap 中。</em></p> |
| <p>instanceMap设计为Map&lt;ApplicationModel, DubboBootstrap&gt;类型 Key,意味着可以为多个应用程序模型创建不同的启动器,启动多个服务</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> DubboBootstrap <span style="color:#268bd2">getInstance</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instanceMap<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> _k <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> DubboBootstrap<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="83-dubbobootstrap的构造器代码">8.3 DubboBootstrap的构造器代码</h2> |
| <p>构造器代码是逻辑比较复杂的地方,我们先来看下代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">DubboBootstrap</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存储应用程序启动模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置管理器ConfigManager: 配置管理器的扩展类型ApplicationExt ,扩展名字config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取环境信息Environment: 环境信息的扩展类型为ApplicationExt,扩展名字为environment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> environment <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getModelEnvironment<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//执行器存储仓库(线程池)ExecutorRepository: 扩展类型为ExecutorRepository,默认扩展扩展名字为default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> executorRepository <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getDefaultExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化并启动应用程序实例ApplicationDeployer,DefaultApplicationDeployer类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// listen deploy events |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//为发布器 设置生命周期回调 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>addDeployListener<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DeployListenerAdapter<span style="color:#719e07">&lt;</span>ApplicationModel<span style="color:#719e07">&gt;()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onStarted</span><span style="color:#719e07">(</span>ApplicationModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> notifyStarted<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onStopped</span><span style="color:#719e07">(</span>ApplicationModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> notifyStopped<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onFailure</span><span style="color:#719e07">(</span>ApplicationModel scopeModel<span style="color:#719e07">,</span> Throwable cause<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> notifyStopped<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将启动器对象注册到应用程序模型applicationModel的Bean工厂中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// register DubboBootstrap bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>registerBean<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/8-dubbo-qi-dong-qi-dubbobootstrap-jie-zhu-shuang-chong-xiao-yan-suo-de-dan-li-mo-shi-jin-xing-dui-xiang-de-chu-shi-hua/">&laquo;Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化&raquo;</a></p></description></item><item><title>Blog: 7-Dubbo的SPI扩展机制之自动激活扩展Activate源码解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/07/7-dubbo%E7%9A%84spi%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E4%B9%8B%E8%87%AA%E5%8A%A8%E6%BF%80%E6%B4%BB%E6%89%A9%E5%B1%95activate%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</link><pubDate>Sun, 07 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/07/7-dubbo%E7%9A%84spi%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E4%B9%8B%E8%87%AA%E5%8A%A8%E6%BF%80%E6%B4%BB%E6%89%A9%E5%B1%95activate%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</guid><description> |
| <h1 id="7-dubbo的spi扩展机制之自动激活扩展activate源码解析">7-Dubbo的SPI扩展机制之自动激活扩展Activate源码解析</h1> |
| <h2 id="71-activate扩展的说明">7.1 Activate扩展的说明</h2> |
| <p>此注解对于使用给定条件自动激活某些扩展非常有用,例如:@Activate可用于在有多个实现时加载某些筛选器扩展。</p> |
| <ul> |
| <li><strong>group()</strong> 指定组条件。框架SPI定义了有效的组值。</li> |
| <li><strong>value()</strong> 指定URL条件中的参数键。</li> |
| </ul> |
| <p>SPI提供程序可以调用ExtensionLoader。getActivateExtension(URL、String、String)方法以查找具有给定条件的所有已激活扩展。</p> |
| <p>比如后面我们会说到的<strong>过滤器扩展对象</strong>的获取,如下通过调用<strong>getActivateExtension方法的</strong>代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Filter<span style="color:#719e07">&gt;</span> filters<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Filter<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> moduleModels<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">)).</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> group<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><h2 id="72-获取自动激活扩展的源码">7.2 获取自动激活扩展的源码</h2> |
| <p>前面我们看了激活扩展是通过调用getActivateExtension方法来获取对象的,那接下来就来看下这个方法做了什么操作:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">* @param url 服务的url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">* @param key 用于获取扩展点名称的url参数键 比如监听器:exporter.listener,过滤器:params-filter,telnet处理器:telnet |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">*/</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getActivateExtension</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> String key<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续调用重载的方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param url 服务的url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param key 用于获取扩展点名称的url参数键 比如监听器:exporter.listener,过滤器:params-filter,telnet处理器:telnet |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param group group 用于筛选的分组,比如过滤器中使用此参数来区分消费者使用这个过滤器还是提供者使用这个过滤器他们的group参数分表为consumer,provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return 已激活的扩展列表。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getActivateExtension</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> String key<span style="color:#719e07">,</span> String group<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从参数中获取url指定的值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String value <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用下个重载的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>value<span style="color:#719e07">)</span> <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">:</span> COMMA_SPLIT_PATTERN<span style="color:#719e07">.</span>split<span style="color:#719e07">(</span>value<span style="color:#719e07">),</span> group<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面的重载方法都是用来转换参数的,下面这个方法才是真正的逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 获取激活扩展. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param url 服务的url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param values 这个value是扩展点的名字 当指定了时候会使用指定的名字的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param group group 用于筛选的分组,比如过滤器中使用此参数来区分消费者使用这个过滤器还是提供者使用这个过滤器他们的group参数分表为consumer,provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return 获取激活扩展. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getActivateExtension</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> values<span style="color:#719e07">,</span> String group<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查扩展加载器是否被销毁了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// solve the bug of using @SPI&#39;s wrapper method to report a null pointer exception. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//创建个有序的Map集合,用来对扩展进行排序 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>Class<span style="color:#719e07">&lt;?&gt;,</span> T<span style="color:#719e07">&gt;</span> activateExtensionsMap <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> TreeMap<span style="color:#719e07">&lt;&gt;(</span>activateComparator<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化扩展名字,指定了扩展名字values不为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> names <span style="color:#719e07">=</span> values <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>0<span style="color:#719e07">)</span> <span style="color:#719e07">:</span> asList<span style="color:#719e07">(</span>values<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展名字使用Set集合进行去重 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> namesSet <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashSet<span style="color:#719e07">&lt;&gt;(</span>names<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//参数常量是 -default 扩展名字是否不包含默认的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> DEFAULT_KEY<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次进来肯定是没有缓存对象双重校验锁检查下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedActivateGroups<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>cachedActivateGroups<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// cache all extensions |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedActivateGroups<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载扩展类型对应的扩展类,这个具体细节参考源码或者《[Dubbo3.0.7源码解析系列]-5-Dubbo的SPI扩展机制与自适应扩展对象的创建与扩展文件的扫描源码解析》章节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getExtensionClasses<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> cachedActivates<span style="color:#719e07">.</span>entrySet<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> entry<span style="color:#719e07">.</span>getKey<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Object activate <span style="color:#719e07">=</span> entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> activateGroup<span style="color:#719e07">,</span> activateValue<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的activates列表获取group()和value()值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>activate <span style="color:#719e07">instanceof</span> Activate<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> activateGroup <span style="color:#719e07">=</span> <span style="color:#719e07">((</span>Activate<span style="color:#719e07">)</span> activate<span style="color:#719e07">).</span>group<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> activateValue <span style="color:#719e07">=</span> <span style="color:#719e07">((</span>Activate<span style="color:#719e07">)</span> activate<span style="color:#719e07">).</span>value<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>activate <span style="color:#719e07">instanceof</span> com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> activateGroup <span style="color:#719e07">=</span> <span style="color:#719e07">((</span>com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate<span style="color:#719e07">)</span> activate<span style="color:#719e07">).</span>group<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> activateValue <span style="color:#719e07">=</span> <span style="color:#719e07">((</span>com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate<span style="color:#719e07">)</span> activate<span style="color:#719e07">).</span>value<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存分组值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivateGroups<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> HashSet<span style="color:#719e07">&lt;&gt;(</span>Arrays<span style="color:#719e07">.</span>asList<span style="color:#719e07">(</span>activateGroup<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[][]</span> keyPairs <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[</span>activateValue<span style="color:#719e07">.</span>length<span style="color:#719e07">][];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历指定的激活扩展的扩展名字列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0<span style="color:#719e07">;</span> i <span style="color:#719e07">&lt;</span> activateValue<span style="color:#719e07">.</span>length<span style="color:#719e07">;</span> i<span style="color:#719e07">++)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>activateValue<span style="color:#719e07">[</span>i<span style="color:#719e07">].</span>contains<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;:&#34;</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[</span>2<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> arr <span style="color:#719e07">=</span> activateValue<span style="color:#719e07">[</span>i<span style="color:#719e07">].</span>split<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;:&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">][</span>0<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> arr<span style="color:#719e07">[</span>0<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">][</span>1<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> arr<span style="color:#719e07">[</span>1<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[</span>1<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">][</span>0<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> activateValue<span style="color:#719e07">[</span>i<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存指定扩展信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivateValues<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> keyPairs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// traverse all cached extensions |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//遍历所有激活的扩展名字和扩展分组集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivateGroups<span style="color:#719e07">.</span>forEach<span style="color:#719e07">((</span>name<span style="color:#719e07">,</span> activateGroup<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//筛选当前扩展的扩展分组与激活扩展的扩展分组是否可以匹配 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isMatchGroup<span style="color:#719e07">(</span>group<span style="color:#719e07">,</span> activateGroup<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不能是指定的扩展名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>name<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//也不能是带有 -指定扩展名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> name<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果在Active注解中配置了value则当指定的键出现在URL的参数中时,激活当前扩展名。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果未配置value属性则默认都是匹配的(cachedActivateValues中不存在对应扩展名字的缓存的时候默认为true) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">&amp;&amp;</span> isActive<span style="color:#719e07">(</span>cachedActivateValues<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>name<span style="color:#719e07">),</span> url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存激活的扩展类型映射的扩展名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> activateExtensionsMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>getExtensionClass<span style="color:#719e07">(</span>name<span style="color:#719e07">),</span> getExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>DEFAULT_KEY<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// will affect order |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// `ext1,default,ext2` means ext1 will happens before all of the default extensions while ext2 will after them |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ArrayList<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> extensionsResult <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>activateExtensionsMap<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> names<span style="color:#719e07">.</span>size<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0<span style="color:#719e07">;</span> i <span style="color:#719e07">&lt;</span> names<span style="color:#719e07">.</span>size<span style="color:#719e07">();</span> i<span style="color:#719e07">++)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> names<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>i<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>name<span style="color:#719e07">.</span>startsWith<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>DEFAULT_KEY<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>containsExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> extensionsResult<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>getExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> extensionsResult<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>activateExtensionsMap<span style="color:#719e07">.</span>values<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extensionsResult<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// add extensions, will be sorted by its order |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0<span style="color:#719e07">;</span> i <span style="color:#719e07">&lt;</span> names<span style="color:#719e07">.</span>size<span style="color:#719e07">();</span> i<span style="color:#719e07">++)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> names<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>i<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>name<span style="color:#719e07">.</span>startsWith<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>DEFAULT_KEY<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>containsExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> activateExtensionsMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>getExtensionClass<span style="color:#719e07">(</span>name<span style="color:#719e07">),</span> getExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>activateExtensionsMap<span style="color:#719e07">.</span>values<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>再来回顾下扫描扩展类型的时候,与激活扩展的相关扫描代码: |
| 与激活注解关键的代码位置在这里ExtensionLoader的loadClass方法中 |
| 我来贴下loadClass方法核心的代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">if</span> <span style="color:#719e07">(</span>clazz<span style="color:#719e07">.</span>isAnnotationPresent<span style="color:#719e07">(</span>Adaptive<span style="color:#719e07">.</span>class<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheAdaptiveClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isWrapperClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheWrapperClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> name <span style="color:#719e07">=</span> findAnnotationName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>name<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No such extension name for the class &#34;</span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in the config &#34;</span> <span style="color:#719e07">+</span> resourceURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> names <span style="color:#719e07">=</span> NAME_SEPARATOR<span style="color:#719e07">.</span>split<span style="color:#719e07">(</span>name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ArrayUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>names<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//位置在这里其他地方就不标记注释了,前面判断了如果不是Adaptive也不是Wrapper类型则我们可以来判断是否为Activate 类型如果是的话调用cacheActivateClass方法将扩展缓存进cachedActivates缓存中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheActivateClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> names<span style="color:#719e07">[</span>0<span style="color:#719e07">]);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String n <span style="color:#719e07">:</span> names<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> n<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> saveInExtensionClass<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> clazz<span style="color:#719e07">,</span> n<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">cacheActivateClass</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">,</span> String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Activate activate <span style="color:#719e07">=</span> clazz<span style="color:#719e07">.</span>getAnnotation<span style="color:#719e07">(</span>Activate<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>activate <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注解存在则加入激活注解缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivates<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> activate<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// support com.alibaba.dubbo.common.extension.Activate |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate oldActivate <span style="color:#719e07">=</span> clazz<span style="color:#719e07">.</span>getAnnotation<span style="color:#719e07">(</span>com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>oldActivate <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cachedActivates<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> oldActivate<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/7-dubbo-de-spi-kuo-zhan-ji-zhi-zhi-zi-dong-ji-huo-kuo-zhan-activate-yuan-ma-jie-xi/">《Dubbo的SPI扩展机制之自动激活扩展Activate源码解析》</a></p></description></item><item><title>Blog: 06-Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/06/06-dubbo%E7%9A%84spi%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E4%B9%8B%E6%99%AE%E9%80%9A%E6%89%A9%E5%B1%95%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BA%E4%B8%8Ewrapper%E6%9C%BA%E5%88%B6%E7%9A%84%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</link><pubDate>Sat, 06 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/06/06-dubbo%E7%9A%84spi%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E4%B9%8B%E6%99%AE%E9%80%9A%E6%89%A9%E5%B1%95%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BA%E4%B8%8Ewrapper%E6%9C%BA%E5%88%B6%E7%9A%84%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</guid><description> |
| <h1 id="6-dubbo的spi扩展机制之普通扩展对象的创建与wrapper机制的源码解析">6 Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析</h1> |
| <h2 id="61-普通扩展对象的加载与创建">6.1 普通扩展对象的加载与创建</h2> |
| <p>这里我们要分析的是ExtensionLoader类型的getExtension(String name)方法, 有了前面自适应扩展的铺垫,这里就更容易来看了getExtension是根据扩展名字获取具体扩展的通用方法,我们来根据某个类型来获取扩展的时候就是走的这里,比如在这个博客开头的介绍:</p> |
| <ul> |
| <li>ApplicationModel中获取配置管理器对象</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> configManager <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ConfigManager<span style="color:#719e07">)</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationExt<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>ConfigManager<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><h3 id="611-getextension方法源码">6.1.1 getExtension方法源码</h3> |
| <p>先来看下getExtension方法的源码,根据扩展名字查询扩展对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">public</span> T <span style="color:#268bd2">getExtension</span>(String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里并不能看到什么,只多传了个参数wrap为true调用另外一个重载的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T extension <span style="color:#719e07">=</span> getExtension(name, <span style="color:#b58900">true</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (extension <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException(<span style="color:#2aa198">&#34;Not find extension: &#34;</span> <span style="color:#719e07">+</span> name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extension; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> T <span style="color:#268bd2">getExtension</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> wrap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查扩展加载器是否已被销毁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Extension name == null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展名字为true则加载默认扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getDefaultExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//非wrap类型则将缓存的扩展名字key加上_origin后缀 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//wrap是aop机制 俗称切面,这个origin在aop里面可以称为切点,下面的wrap扩展可以称为增强通知的类型,普通扩展和wrap扩展的扩展名字是一样的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String cacheKey <span style="color:#719e07">=</span> name<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>wrap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheKey <span style="color:#719e07">+=</span> <span style="color:#2aa198">&#34;_origin&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从cachedInstances缓存中查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Holder<span style="color:#719e07">&lt;</span>Object<span style="color:#719e07">&gt;</span> holder <span style="color:#719e07">=</span> getOrCreateHolder<span style="color:#719e07">(</span>cacheKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Object instance <span style="color:#719e07">=</span> holder<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中不存在则创建扩展对象 双重校验锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>holder<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁的方式 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> holder<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> createExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> wrap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> holder<span style="color:#719e07">.</span>set<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>我们先来看一下默认扩展的加载代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> T <span style="color:#268bd2">getDefaultExtension</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载扩展类型对应的所有扩展SPI实现类型,在加载所有扩展实现类型的时候会缓存这个扩展的默认实现类型,将对象缓存在cachedDefaultName中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getExtensionClasses<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isBlank<span style="color:#719e07">(</span>cachedDefaultName<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> <span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>cachedDefaultName<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//再回到加载扩展的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> getExtension<span style="color:#719e07">(</span>cachedDefaultName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>创建扩展对象方法这个和自适应扩展的创建扩展类似 |
| createExtension: |
| 具体过程如下:</p> |
| <ul> |
| <li>加载扩展类型:getExtensionClasses()</li> |
| <li>创建扩展对象:createExtensionInstance(clazz)</li> |
| <li>注入自适应扩展: injectExtension(instance);</li> |
| <li>wrap处理</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> T <span style="color:#268bd2">createExtension</span>(String name, boolean wrap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展的创建的第一步扫描所有jar中的扩展实现,这里扫描完之后获取对应扩展名字的扩展实现类型的Class对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Class<span style="color:#719e07">&lt;?&gt;</span> clazz <span style="color:#719e07">=</span> getExtensionClasses().get(name); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//出现异常了 转换下异常信息 再抛出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (clazz <span style="color:#719e07">==</span> null <span style="color:#719e07">||</span> unacceptableExceptions.contains(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> findException(name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前扩展对象是否已经创建过了则直接从缓存中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T instance <span style="color:#719e07">=</span> (T) extensionInstances.get(clazz); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次获取缓存中肯定没有则创建扩展对象然后缓存起来 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//createExtensionInstance 这个是与自适应扩展对象创建对象的不同之处 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz)); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> (T) extensionInstances.get(clazz); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessBeforeInitialization(instance, name); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展自适应方法,这个方法前面讲自适应扩展时候说了,注入自适应扩展方法的自适应扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> injectExtension(instance); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessAfterInitialization(instance, name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否开启了wrap |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//Dubbo通过Wrapper实现AOP的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (wrap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个可以参考下Dubbo扩展的加载 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>Class<span style="color:#719e07">&lt;?&gt;&gt;</span> wrapperClassesList <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//wrap类型排序 这个wrap类型是如何来的呢,在前面扫描扩展类型的时候如果当前扩展类型不是Adaptive注解修饰的,并且当前类型type有个构造器参数是type自身的也是前面加载扩展类型时候说的装饰器模式 可以参考DubboProtocol的构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (cachedWrapperClasses <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> wrapperClassesList.addAll(cachedWrapperClasses); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据Wrapper注解的order值来进行排序值越小越在列表的前面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> wrapperClassesList.sort(WrapperComparator.COMPARATOR); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//反转之后值越大就会在列表的前面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collections.reverse(wrapperClassesList); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从缓存中查到了wrapper扩展则遍历这些wrapp扩展进行筛选 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(wrapperClassesList)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Class<span style="color:#719e07">&lt;?&gt;</span> wrapperClass : wrapperClassesList) { |
| </span></span><span style="display:flex;"><span> Wrapper wrapper <span style="color:#719e07">=</span> wrapperClass.getAnnotation(Wrapper.<span style="color:#719e07">class</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//需要包装的扩展名。当此数组为空时,默认值为匹配 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//看下当前扩展是否匹配这个wrap,如何判断呢? |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//wrapper注解不存在或者matches匹配,或者mismatches不包含当前扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> boolean match <span style="color:#719e07">=</span> (wrapper <span style="color:#719e07">==</span> null) <span style="color:#719e07">||</span> |
| </span></span><span style="display:flex;"><span> ((ArrayUtils.isEmpty(wrapper.matches()) <span style="color:#719e07">||</span> ArrayUtils.contains(wrapper.matches(), name)) <span style="color:#719e07">&amp;&amp;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">!</span>ArrayUtils.contains(wrapper.mismatches(), name)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这是扩展类型是匹配wrapp的则开始注入 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (match) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//匹配到了就创建所有的wrapper类型的对象同时构造器参数设置为当前类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessAfterInitialization(instance, name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//初始化扩展,如果当前扩展是Lifecycle类型则调用初始化方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initExtension(instance); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> <span style="color:#268bd2">IllegalStateException</span>(<span style="color:#2aa198">&#34;Extension instance (name: &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, class: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;)</span> couldn&#39;t be instantiated: <span style="color:#2aa198">&#34; + t.getMessage(), t)</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="612-创建扩展对象">6.1.2 创建扩展对象</h3> |
| <p>前面加载扩展类型在自适应扩展的时候已经说过了这里就不重复了,这里我们来看下 |
| 扩展对象的创建过程:createExtensionInstance(clazz)</p> |
| <p>前面看自适应扩展对象创建的时候自适应扩展对象仅仅是使用反射newInstance了一个扩展对象,而普通的扩展类型创建对象的过程就相对复杂一点,接下来我们来看下:</p> |
| <p>ExtensionLoader的createExtensionInstance方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> Object <span style="color:#268bd2">createExtensionInstance</span>(Class<span style="color:#719e07">&lt;?&gt;</span> type) <span style="color:#719e07">throws</span> ReflectiveOperationException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//在ExtensionLoader构造器中,有个initInstantiationStrategy()方法中new了一个初始化策略InstantiationStrategy类型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> instantiationStrategy.instantiate(type); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>InstantiationStrategy的实例化对象方法instantiate</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T instantiate(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type) <span style="color:#719e07">throws</span> ReflectiveOperationException { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// should not use default constructor directly, maybe also has another constructor matched scope model arguments |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 1. try to get default constructor |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Constructor<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> defaultConstructor <span style="color:#719e07">=</span> null; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//反射获取对应类型的无参构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultConstructor <span style="color:#719e07">=</span> type.getConstructor(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (NoSuchMethodException e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore no default constructor |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 2. use matched constructor if found |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>Constructor<span style="color:#719e07">&gt;</span> matchedConstructors <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取所有构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Constructor<span style="color:#719e07">&lt;?&gt;</span>[] declaredConstructors <span style="color:#719e07">=</span> type.getConstructors(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历构造器列表, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> (Constructor<span style="color:#719e07">&lt;?&gt;</span> constructor : declaredConstructors) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果存在构造器则构造器参数类型是否为ScopeModel类型,如果为ScopeModel则为匹配的构造器 说明我们扩展类型在这个版本如果想要让这个构造器生效必须参数类型为ScopeModel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (isMatched(constructor)) { |
| </span></span><span style="display:flex;"><span> matchedConstructors.add(constructor); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// remove default constructor from matchedConstructors |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (defaultConstructor <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> matchedConstructors.remove(defaultConstructor); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// match order: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 1. the only matched constructor with parameters |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 2. default constructor if absent |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> Constructor targetConstructor; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//匹配的参数ScopeModel的构造器太多了就抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (matchedConstructors.size() <span style="color:#719e07">&gt;</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> <span style="color:#268bd2">IllegalArgumentException</span>(<span style="color:#2aa198">&#34;Expect only one but found &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> matchedConstructors.size() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; matched constructors for type: &#34;</span> <span style="color:#719e07">+</span> type.getName() <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;, matched constructors: &#34;</span> <span style="color:#719e07">+</span> matchedConstructors); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (matchedConstructors.size() <span style="color:#719e07">==</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一个参数一般为一个参数类型ScopeModel的构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> targetConstructor <span style="color:#719e07">=</span> matchedConstructors.get(<span style="color:#2aa198">0</span>); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (defaultConstructor <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果没有自定义构造器则使用空参数构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> targetConstructor <span style="color:#719e07">=</span> defaultConstructor; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一个构造器也没匹配上也要报错 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> <span style="color:#268bd2">IllegalArgumentException</span>(<span style="color:#2aa198">&#34;None matched constructor was found for type: &#34;</span> <span style="color:#719e07">+</span> type.getName()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// create instance with arguments |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//反射获取构造器参数的参数类型列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Class[] parameterTypes <span style="color:#719e07">=</span> targetConstructor.getParameterTypes(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果存在参数则为参数设置值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Object[] args <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Object[parameterTypes.length]; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (<span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> <span style="color:#2aa198">0</span>; i <span style="color:#719e07">&lt;</span> parameterTypes.length; i<span style="color:#719e07">++</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//借助scopeModelAccessor工具获取参数类型,这个参数类型为当前的域模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> args[i] <span style="color:#719e07">=</span> getArgumentValueForType(parameterTypes[i]); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> (T) targetConstructor.newInstance(args); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h2 id="62-wrap机制">6.2 wrap机制</h2> |
| <h3 id="621-wrapper机制说明">6.2.1 Wrapper机制说明</h3> |
| <p>Dubbo通过Wrapper实现AOP的方法</p> |
| <p>Wrapper机制,即扩展点自动包装。Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。 |
| 扩展点的 Wrapper 类可以有多个,也可以根据需要新增。 |
| 通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。</p> |
| <p>Wrapper的规范 |
| Wrapper 机制不是通过注解实现的,而是通过一套 Wrapper 规范实现的。 |
| Wrapper 类在定义时需要遵循如下规范。</p> |
| <ul> |
| <li>该类要实现 SPI 接口</li> |
| <li>该类中要有 SPI 接口的引用</li> |
| <li>该类中必须含有一个含参的构造方法且参数只能有一个类型为SPI接口</li> |
| <li>在接口实现方法中要调用 SPI 接口引用对象的相应方法</li> |
| <li>该类名称以 Wrapper 结尾</li> |
| </ul> |
| <p>比如如下几个扩展类型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#719e07">class</span> <span style="color:#268bd2">org</span>.apache.dubbo.rpc.protocol.ProtocolListenerWrapper |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">class</span> <span style="color:#268bd2">org</span>.apache.dubbo.qos.protocol.QosProtocolWrapper |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">class</span> <span style="color:#268bd2">org</span>.apache.dubbo.rpc.protocol.ProtocolListenerWrapper |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">class</span> <span style="color:#268bd2">org</span>.apache.dubbo.qos.protocol.QosProtocolWrapper |
| </span></span></code></pre></div><p>回顾下Wrapper扩展类型的扫描于对象的创建</p> |
| <h3 id="622-wrapper类型的扫描">6.2.2 Wrapper类型的扫描</h3> |
| <p><strong>Wrapper类型的扫描代码如下:</strong></p> |
| <p>来自4.5.2.3小节ExtensionLoader类型中的loadClass方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#586e75">//扩展子类型是否存在这个注解@Adaptive |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (clazz.isAnnotationPresent(Adaptive.<span style="color:#719e07">class</span>)) { |
| </span></span><span style="display:flex;"><span> cacheAdaptiveClass(clazz, overridden); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (isWrapperClass(clazz)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展子类型构造器中是否有这个类型的接口 (这个可以想象下我们了解的Java IO流中的类型使用到的装饰器模式 构造器传个类型) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheWrapperClass(clazz); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span></code></pre></div><p>isWrapperClass方法通过判断构造器类型是否为当前类型来判断是否为Wrapper类型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#719e07">private</span> boolean <span style="color:#268bd2">isWrapperClass</span>(Class<span style="color:#719e07">&lt;?&gt;</span> clazz) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> clazz.getConstructor(type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#b58900">true</span>; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (NoSuchMethodException e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#b58900">false</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="623-wrapper类型的创建">6.2.3 Wrapper类型的创建</h3> |
| <p>这个可以看下4.6.1 getExtension方法源码的获取扩展对象时候查询扩展对象是否有对应的Wrapper类型的扩展为其创建Wrapper扩展对象,如下代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#586e75">//Dubbo通过Wrapper实现AOP的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (wrap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个可以参考下Dubbo扩展的加载 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>Class<span style="color:#719e07">&lt;?&gt;&gt;</span> wrapperClassesList <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//wrap类型排序 这个wrap类型是如何来的呢,在前面扫描扩展类型的时候如果当前扩展类型不是Adaptive注解修饰的,并且当前类型type有个构造器参数是type自身的也是前面加载扩展类型时候说的装饰器模式 可以参考DubboProtocol的构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (cachedWrapperClasses <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> wrapperClassesList.addAll(cachedWrapperClasses); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据Wrapper注解的order值来进行排序值越小越在列表的前面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> wrapperClassesList.sort(WrapperComparator.COMPARATOR); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//反转之后值越大就会在列表的前面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collections.reverse(wrapperClassesList); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从缓存中查到了wrapper扩展则遍历这些wrapp扩展进行筛选 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(wrapperClassesList)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Class<span style="color:#719e07">&lt;?&gt;</span> wrapperClass : wrapperClassesList) { |
| </span></span><span style="display:flex;"><span> Wrapper wrapper <span style="color:#719e07">=</span> wrapperClass.getAnnotation(Wrapper.<span style="color:#719e07">class</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//需要包装的扩展名。当此数组为空时,默认值为匹配 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//看下当前扩展是否匹配这个wrap,如何判断呢? |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//wrapper注解不存在或者matches匹配,或者mismatches不包含当前扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> boolean match <span style="color:#719e07">=</span> (wrapper <span style="color:#719e07">==</span> null) <span style="color:#719e07">||</span> |
| </span></span><span style="display:flex;"><span> ((ArrayUtils.isEmpty(wrapper.matches()) <span style="color:#719e07">||</span> ArrayUtils.contains(wrapper.matches(), name)) <span style="color:#719e07">&amp;&amp;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">!</span>ArrayUtils.contains(wrapper.mismatches(), name)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这是扩展类型是匹配wrapp的则开始注入 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (match) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//匹配到了就创建所有的wrapper类型的对象同时构造器参数设置为当前类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessAfterInitialization(instance, name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>主要来看下什么情况下才为当前扩展类型创建Wrapper包装类型:</p> |
| <ul> |
| <li>wrapper注解不存在(前面判断过Wrapper类型是构造器满足条件的)</li> |
| <li>存在Wrapper注解: |
| <ul> |
| <li>matches匹配,</li> |
| <li>或者mismatches不包含当前扩展</li> |
| </ul> |
| </li> |
| </ul> |
| <p>如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装</p> |
| <p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/6-dubbo-de-spi-kuo-zhan-ji-zhi-zhi-pu-tong-kuo-zhan-dui-xiang-de-chuang-jian-yu-wrapper-ji-zhi-de-yuan-ma-jie-xi/">《Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析》</a></p></description></item><item><title>Blog: 05-自适应扩展对象的创建getAdaptiveExtension方法</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/05/05-%E8%87%AA%E9%80%82%E5%BA%94%E6%89%A9%E5%B1%95%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BAgetadaptiveextension%E6%96%B9%E6%B3%95/</link><pubDate>Fri, 05 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/05/05-%E8%87%AA%E9%80%82%E5%BA%94%E6%89%A9%E5%B1%95%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BAgetadaptiveextension%E6%96%B9%E6%B3%95/</guid><description> |
| <h2 id="5-自适应扩展对象的创建getadaptiveextension方法">5 自适应扩展对象的创建getAdaptiveExtension方法</h2> |
| <p>自适应扩展又称为动态扩展,可以在运行时生成扩展对象</p> |
| <p>ExtensionLoader中的getAdaptiveExtension()方法,这个方法也是我们看到的第一个获取扩展对象的方法. ,这个方法可以帮助我们通过SPI机制从扩展文件中找到需要的扩展类型并创建它的对象, |
| <strong>自适应扩展:<strong>如果对设计模式比较了解的可能会联想到</strong>适配器模式</strong>,自适应扩展其实就是适配器模式的思路,自适应扩展有两种策略:</p> |
| <ul> |
| <li> |
| <p>一种是我们自己实现自适应扩展:然后使用@Adaptive修饰这个时候适配器的逻辑由我们自己实现,当扩展加载器去查找具体的扩展的时候可以通过找到我们这个对应的适配器扩展,然后适配器扩展帮忙去查询真正的扩展,这个比如我们下面要举的扩展注入器的例子,具体扩展通过扩展注入器适配器,注入器适配器来查询具体的注入器扩展实现来帮忙查找扩展。</p> |
| </li> |
| <li> |
| <p>还有一种方式是我们未实现这个自适应扩展,Dubbo在运行时通过字节码动态代理的方式在运行时生成一个适配器,使用这个适配器映射到具体的扩展. 第二种情况往往用在比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。(如果还不了解可以考虑看下@Adaptive注解加载方法上面的时候扩展是如何加载的)</p> |
| </li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> T <span style="color:#268bd2">getAdaptiveExtension</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查当前扩展加载器是否已经被销毁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从自适应扩展缓存中查询扩展对象如果存在就直接返回,这个自适应扩展类型只会有一个扩展实现类型如果是多个的话根据是否可以覆盖参数决定扩展实现类是否可以相互覆盖 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Object instance <span style="color:#719e07">=</span> cachedAdaptiveInstance<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个if判断不太优雅 容易多层嵌套,上面instance不为空就可以直接返回了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建异常则抛出异常直接返回(多线程场景下可能第一个线程异常了第二个线程进来之后走到这里) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>createAdaptiveInstanceError <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to create adaptive instance: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> createAdaptiveInstanceError<span style="color:#719e07">.</span>toString<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> createAdaptiveInstanceError<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加锁排队 (单例模式创建对象的思想 双重校验锁) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>cachedAdaptiveInstance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加锁的时候对象都是空的,进来之后先判断下防止重复创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> cachedAdaptiveInstance<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//只有第一个进来锁的对象为空开始创建扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据SPI机制获取类型,创建对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> createAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存入缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedAdaptiveInstance<span style="color:#719e07">.</span>set<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> createAdaptiveInstanceError <span style="color:#719e07">=</span> t<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to create adaptive instance: &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>toString<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面使用单例思想来调用创建自适应扩展对象的方法,下面就让我们深入探究下创建自适应扩展对象的整个过程createAdaptiveExtension();方法:</p> |
| <h2 id="51-创建扩展对象的生命周期方法-注意这个后续会详细解析这个声明周期方法的细节">5.1 创建扩展对象的生命周期方法-注意这个后续会详细解析这个声明周期方法的细节</h2> |
| <p>createAdaptiveExtension() |
| 我们先来看ExtensionLoader类型中的createAdaptiveExtension();方法,这个方法包含了扩展对象创建初始化的整个生命周期,如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> T <span style="color:#268bd2">createAdaptiveExtension</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取扩展类型实现类, 创建扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T instance <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> getAdaptiveExtensionClass<span style="color:#719e07">().</span>newInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象之前的回调方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> postProcessBeforeInitialization<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> injectExtension<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象之后的回调方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> postProcessAfterInitialization<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化扩展对象的属性,如果当前扩展实例的类型实现了Lifecycle则调用当前扩展对象的生命周期回调方法initialize()(来自Lifecycle接口) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//参考样例第一个instance为ExtensionInjector的自适应扩展对象类型为AdaptiveExtensionInjector,自适应扩展注入器(适配器)用来查询具体支持的扩展注入器比如scope,spi,spring注入器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initExtension<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can&#39;t create adaptive extension &#34;</span> <span style="color:#719e07">+</span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="52-spi机制获取扩展对象实现类型getadaptiveextensionclass">5.2 SPI机制获取扩展对象实现类型getAdaptiveExtensionClass()</h2> |
| <p>这个方法可以帮助我们了解具体的Dubbo SPI机制 如果找到扩展类型的实现类,会寻找哪些文件,扩展文件的优先级又是什么,对我们自己写扩展方法很有帮助,接下来我们就来看下它的源码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> Class<span style="color:#719e07">&lt;?&gt;</span> getAdaptiveExtensionClass<span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取扩展类型,将扩展类型存入成员变量cachedClasses中进行缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getExtensionClasses<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//在上个方法的详细解析中的最后一步loadClass方法中如果扩展类型存在Adaptive注解将会将扩展类型赋值给cachedAdaptiveClass,否则的话会把扩展类型都缓存起来存储在扩展集合extensionClasses中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedAdaptiveClass <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cachedAdaptiveClass<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展实现类型没有一个这个自适应注解Adaptive时候会走到这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//刚刚我们扫描到了扩展类型然后将其存入cachedClasses集合中了 接下来我们看下如何创建扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> cachedAdaptiveClass <span style="color:#719e07">=</span> createAdaptiveExtensionClass<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看获取扩展类型的方法<strong>getExtensionClasses()</strong>:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> getExtensionClasses<span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中查询扩展类型是否存在 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> classes <span style="color:#719e07">=</span> cachedClasses<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>classes <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//单例模式双重校验锁判断 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>cachedClasses<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> classes <span style="color:#719e07">=</span> cachedClasses<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>classes <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> classes <span style="color:#719e07">=</span> loadExtensionClasses<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将我们扫描到的扩展类型存入成员变量cachedClasses中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedClasses<span style="color:#719e07">.</span>set<span style="color:#719e07">(</span>classes<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> classes<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="521-使用不同的的策略加载加载不同目录下的扩展">5.2.1 使用不同的的策略加载加载不同目录下的扩展</h3> |
| <p>加载扩展类型的方法<strong>loadExtensionClasses()</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#719e07">private</span> Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> loadExtensionClasses() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查扩展加载器是否被销毁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存默认的扩展名到成员变量cachedDefaultName中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheDefaultExtensionName(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载到的扩展集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展策略,在4.3章节中我们介绍了这个类型的UML与说明 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//LoadingStrategy扩展加载策略,目前有3个扩展加载策略 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//DubboInternalLoadingStrategy:Dubbo内置的扩展加载策略,将加载文件目录为META-INF/dubbo/internal/的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//DubboLoadingStrategy:Dubbo普通的扩展加载策略,将加载目录为META-INF/dubbo/的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//ServicesLoadingStrategy:JAVA SPI加载策略 ,将加载目录为META-INF/services/的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//扩展策略集合对象在什么时候初始化的呢在成员变量初始化的时候就创建了集合对象,这个可以看方法loadLoadingStrategies() 通过Java的 SPI加载策略 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> (LoadingStrategy strategy : strategies) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据策略从指定文件目录中加载扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadDirectory(extensionClasses, strategy, type.getName()); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// compatible with old ExtensionFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果当前要加载的扩展类型是扩展注入类型则扫描下ExtensionFactory类型的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.type <span style="color:#719e07">==</span> ExtensionInjector.<span style="color:#719e07">class</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个方法和上面那个方法是一样的就不详细说了 扫描文件 找到扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadDirectory(extensionClasses, strategy, ExtensionFactory.<span style="color:#719e07">class</span>.<span style="color:#268bd2">getName</span>()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过loadDirectory扫描 扫描到了ExtensionInjector类型的扩展实现类有3个 我们将会得到这样一个集合例子: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//&#34;spring&#34; -&gt; &#34;class org.apache.dubbo.config.spring.extension.SpringExtensionInjector&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//&#34;scopeBean&#34; -&gt; &#34;class org.apache.dubbo.common.beans.ScopeBeanExtensionInjector&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//&#34;spi&#34; -&gt; &#34;class org.apache.dubbo.common.extension.inject.SpiExtensionInjector&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> extensionClasses; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>从文件中加载扩展实现loadDirectory方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadDirectory</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> LoadingStrategy strategy<span style="color:#719e07">,</span> String type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载并根据策略的参数来加载 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadDirectory<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> strategy<span style="color:#719e07">.</span>directory<span style="color:#719e07">(),</span> type<span style="color:#719e07">,</span> strategy<span style="color:#719e07">.</span>preferExtensionClassLoader<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> strategy<span style="color:#719e07">.</span>overridden<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>includedPackages<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>excludedPackages<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>onlyExtensionClassLoaderPackages<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//下面两行就是要兼容alibaba的扩展包了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String oldType <span style="color:#719e07">=</span> type<span style="color:#719e07">.</span>replace<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;org.apache&#34;</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;com.alibaba&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> loadDirectory<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> strategy<span style="color:#719e07">.</span>directory<span style="color:#719e07">(),</span> oldType<span style="color:#719e07">,</span> strategy<span style="color:#719e07">.</span>preferExtensionClassLoader<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> strategy<span style="color:#719e07">.</span>overridden<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>includedPackagesInCompatibleType<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>excludedPackages<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>onlyExtensionClassLoaderPackages<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>带扩展策略参数的loadDirectory方法</p> |
| <p>关于扩展策略的参数列表我这里列个表格方便大家来看</p> |
| <table> |
| <thead> |
| <tr> |
| <th>扩展类型</th> |
| <th>dir(目录)</th> |
| <th>extensionLoaderClassLoaderFirst(优先扩展类型的类加载器)</th> |
| <th>overridden(是否允许覆盖同名扩展)</th> |
| <th>includedPackages (明确包含的扩展包)</th> |
| <th>excludedPackages (明确排除的扩展包)</th> |
| <th>onlyExtensionClassLoaderPackages(限制应该从Dubbo的类加载器加载的类)</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>DubboInternalLoadingStrategy</td> |
| <td>META-INF/dubbo/internal/</td> |
| <td>false</td> |
| <td>false</td> |
| <td>null</td> |
| <td>null</td> |
| <td>[]</td> |
| </tr> |
| <tr> |
| <td>DubboLoadingStrategy</td> |
| <td>META-INF/dubbo/</td> |
| <td>false</td> |
| <td>true</td> |
| <td>null</td> |
| <td>null</td> |
| <td>[]</td> |
| </tr> |
| <tr> |
| <td>ServicesLoadingStrategy</td> |
| <td>META-INF/services/</td> |
| <td>false</td> |
| <td>true</td> |
| <td>null</td> |
| <td>null</td> |
| <td>[]</td> |
| </tr> |
| </tbody> |
| </table> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 不同的扩展策略传递了不同的参数,但是扩展的加载流程是相同的,这里我们可以参考上面表格 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param extensionClasses |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param dir |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param type 这里我们参考的示例这个值为org.apache.dubbo.common.extension.ExtensionInjector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param extensionLoaderClassLoaderFirst |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param overridden false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param includedPackages |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param excludedPackages |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param onlyExtensionClassLoaderPackages |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadDirectory</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> String dir<span style="color:#719e07">,</span> String type<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> extensionLoaderClassLoaderFirst<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> includedPackages<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> excludedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展目录 + 扩展类型全路径 比如: META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String fileName <span style="color:#719e07">=</span> dir <span style="color:#719e07">+</span> type<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoadersToLoad <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// try to load from ExtensionLoader&#39;s ClassLoader first |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//是否优先使用扩展加载器的 类加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>extensionLoaderClassLoaderFirst<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ClassLoader extensionLoaderClassLoader <span style="color:#719e07">=</span> ExtensionLoader<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ClassLoader<span style="color:#719e07">.</span>getSystemClassLoader<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> extensionLoaderClassLoader<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>extensionLoaderClassLoader<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load from scope model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取域模型对象的类型加载器 ,这个域模型对象在初始化的时候会将自己的类加载器放入集合中可以参考《3.2.2 初始化ScopeModel》章节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">.</span>getClassLoaders<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//没有可用的类加载器则从使用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从用于加载类的搜索路径中查找指定名称的所有资源。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Enumeration<span style="color:#719e07">&lt;</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL<span style="color:#719e07">&gt;</span> resources <span style="color:#719e07">=</span> ClassLoader<span style="color:#719e07">.</span>getSystemResources<span style="color:#719e07">(</span>fileName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>resources <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>resources<span style="color:#719e07">.</span>hasMoreElements<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> loadResource<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> resources<span style="color:#719e07">.</span>nextElement<span style="color:#719e07">(),</span> overridden<span style="color:#719e07">,</span> includedPackages<span style="color:#719e07">,</span> excludedPackages<span style="color:#719e07">,</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用类加载资源加载器(ClassLoaderResourceLoader)来加载具体的资源 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">,</span> Set<span style="color:#719e07">&lt;</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL<span style="color:#719e07">&gt;&gt;</span> resources <span style="color:#719e07">=</span> ClassLoaderResourceLoader<span style="color:#719e07">.</span>loadResources<span style="color:#719e07">(</span>fileName<span style="color:#719e07">,</span> classLoadersToLoad<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历从所有资源文件中读取到资源url地址,key为类加载器,值为扩展文件url如夏所示 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//jar:file:/Users/song/.m2/repository/org/apache/dubbo/dubbo/3.0.7/dubbo-3.0.7.jar!/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> resources<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(((</span>classLoader<span style="color:#719e07">,</span> urls<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从文件中加载完资源之后开始根据类加载器和url加载具体的扩展类型,最后将扩展存放进extensionClasses集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadFromClass<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> overridden<span style="color:#719e07">,</span> urls<span style="color:#719e07">,</span> classLoader<span style="color:#719e07">,</span> includedPackages<span style="color:#719e07">,</span> excludedPackages<span style="color:#719e07">,</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Exception occurred when loading extension class (interface: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, description file: &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;).&#34;</span><span style="color:#719e07">,</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="522-借助类加载器的getresources方法遍历所有文件进行扩展文件的查询">5.2.2 借助类加载器的getResources方法遍历所有文件进行扩展文件的查询</h3> |
| <p>查找扩展类型对应的扩展文件的url方法:ClassLoaderResourceLoader类型的loadResources源码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">public</span> <span style="color:#719e07">static</span> Map<span style="color:#719e07">&lt;</span>ClassLoader, Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;&gt;</span> loadResources(String fileName, List<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>ClassLoader, Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;&gt;</span> resources <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不同的类加载器之间使用不同的线程异步的方式进行扫描 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> CountDownLatch countDownLatch <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CountDownLatch(classLoaders.size()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ClassLoader classLoader : classLoaders) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多线程扫描,这个是个newCachedThreadPool的类型的线程池 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> GlobalResourcesRepository.getGlobalExecutorService().submit(() <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> resources.put(classLoader, loadResources(fileName, classLoader)); |
| </span></span><span style="display:flex;"><span> countDownLatch.countDown(); |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> countDownLatch.await(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (InterruptedException e) { |
| </span></span><span style="display:flex;"><span> e.printStackTrace(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> Collections.unmodifiableMap(<span style="color:#719e07">new</span> LinkedHashMap<span style="color:#719e07">&lt;&gt;</span>(resources)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>加载具体类加载器中的资源文件的loadResources方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">public</span> <span style="color:#719e07">static</span> Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> loadResources(String fileName, ClassLoader currentClassLoader) { |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>ClassLoader, Map<span style="color:#719e07">&lt;</span>String, Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;&gt;&gt;</span> classLoaderCache; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次进来类加载器资源缓存是空的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (classLoaderResourcesCache <span style="color:#719e07">==</span> null <span style="color:#719e07">||</span> (classLoaderCache <span style="color:#719e07">=</span> classLoaderResourcesCache.get()) <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//类对象锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> synchronized (ClassLoaderResourceLoader.<span style="color:#719e07">class</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (classLoaderResourcesCache <span style="color:#719e07">==</span> null <span style="color:#719e07">||</span> (classLoaderCache <span style="color:#719e07">=</span> classLoaderResourcesCache.get()) <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> classLoaderCache <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个类资源映射url的软引用缓存对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//软引用(soft references),用于帮助垃圾收集器管理内存使用和消除潜在的内存泄漏。当内存快要不足的时候,GC会迅速的把所有的软引用清除掉,释放内存空间 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> classLoaderResourcesCache <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SoftReference<span style="color:#719e07">&lt;&gt;</span>(classLoaderCache); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次进来时候类加载器url映射缓存是空的,给类加载器缓存对象新增一个值,key是类加载器,值是map类型用来存储文件名对应的url集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>classLoaderCache.containsKey(currentClassLoader)) { |
| </span></span><span style="display:flex;"><span> classLoaderCache.putIfAbsent(currentClassLoader, <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;&gt;</span> urlCache <span style="color:#719e07">=</span> classLoaderCache.get(currentClassLoader); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中没有就从文件里面找 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>urlCache.containsKey(fileName)) { |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> set <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedHashSet<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> Enumeration<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> urls; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//getResources这个方法是这样的:加载当前类加载器以及父类加载器所在路径的资源文件,将遇到的所有资源文件全部返回!这个可以理解为使用双亲委派模型中的类加载器 加载各个位置的资源文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> urls <span style="color:#719e07">=</span> currentClassLoader.getResources(fileName); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//native配置 是否为本地镜像(k可以参考官方文档:https://dubbo.apache.org/zh-cn/docs/references/graalvm/support-graalvm/ |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> boolean isNative <span style="color:#719e07">=</span> NativeUtils.isNative(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (urls <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历找到的对应扩展的文件url将其加入集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">while</span> (urls.hasMoreElements()) { |
| </span></span><span style="display:flex;"><span> URL url <span style="color:#719e07">=</span> urls.nextElement(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isNative) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//In native mode, the address of each URL is the same instead of different paths, so it is necessary to set the ref to make it different |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//动态修改jdk底层url对象的ref变量为可访问,让我们在用反射时访问私有变量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setRef(url); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> set.add(url); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (IOException e) { |
| </span></span><span style="display:flex;"><span> e.printStackTrace(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存入缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> urlCache.put(fileName, set); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//返回结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> urlCache.get(fileName); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="523-使用找到的扩展资源url加载具体扩展类型到内存">5.2.3 使用找到的扩展资源url加载具体扩展类型到内存</h3> |
| <p>ExtensionLoader类型中的loadFromClass方法 遍历url 开始加载扩展类型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadFromClass</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">,</span> Set<span style="color:#719e07">&lt;</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL<span style="color:#719e07">&gt;</span> urls<span style="color:#719e07">,</span> ClassLoader classLoader<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> includedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> excludedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>urls<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL url <span style="color:#719e07">:</span> urls<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> loadResource<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> classLoader<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> overridden<span style="color:#719e07">,</span> includedPackages<span style="color:#719e07">,</span> excludedPackages<span style="color:#719e07">,</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中的loadResource方法 使用IO流读取扩展文件的内容 |
| 读取内容之前我这里先贴一下我们参考的扩展注入类型的文件中的内容如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>adaptive<span style="color:#719e07">=</span>org.apache.dubbo.common.extension.inject.AdaptiveExtensionInjector |
| </span></span><span style="display:flex;"><span>spi<span style="color:#719e07">=</span>org.apache.dubbo.common.extension.inject.SpiExtensionInjector |
| </span></span><span style="display:flex;"><span>scopeBean<span style="color:#719e07">=</span>org.apache.dubbo.common.beans.ScopeBeanExtensionInjector |
| </span></span></code></pre></div><p>扩展中的文件都是一行一行的,并且扩展名字和扩展类型之间使用等号隔开= |
| 了解了文件内容之后 应该下面的代码大致思路就知道了,我们可以详细看下</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadResource</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> ClassLoader classLoader<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL resourceURL<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> includedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> excludedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里固定了文件的格式为utf8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>BufferedReader reader <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> BufferedReader<span style="color:#719e07">(</span><span style="color:#719e07">new</span> InputStreamReader<span style="color:#719e07">(</span>resourceURL<span style="color:#719e07">.</span>openStream<span style="color:#719e07">(),</span> StandardCharsets<span style="color:#719e07">.</span>UTF_8<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String line<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> String clazz<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//按行读取 例如读取到的内容:spring=org.apache.dubbo.config.spring.extension.SpringExtensionInjector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">while</span> <span style="color:#719e07">((</span>line <span style="color:#719e07">=</span> reader<span style="color:#719e07">.</span>readLine<span style="color:#719e07">())</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不知道为何会有这么一行代码删除#之后的字符串 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">int</span> ci <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>indexOf<span style="color:#719e07">(</span><span style="color:#2aa198">&#39;#&#39;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ci <span style="color:#719e07">&gt;=</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> line <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>substring<span style="color:#719e07">(</span>0<span style="color:#719e07">,</span> ci<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> line <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>trim<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>line<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展文件可能如上面我贴的那样 名字和类型等号隔开,也可能是无类型的,例如扩展加载策略使用的是JDK自带的方式services内容中只包含具体的扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>indexOf<span style="color:#719e07">(</span><span style="color:#2aa198">&#39;=&#39;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>i <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> name <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>substring<span style="color:#719e07">(</span>0<span style="color:#719e07">,</span> i<span style="color:#719e07">).</span>trim<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> clazz <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>substring<span style="color:#719e07">(</span>i <span style="color:#719e07">+</span> 1<span style="color:#719e07">).</span>trim<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> clazz <span style="color:#719e07">=</span> line<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//isExcluded是否为加载策略要排除的配置,参数这里为空代表全部类型不排除 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//isIncluded是否为加载策略包含的类型,参数这里为空代表全部文件皆可包含 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//onlyExtensionClassLoaderPackages参数是否只有扩展类的类加载器可以加载扩展,其他扩展类型的类加载器不能加载扩展 这里结果为false 不排除任何类加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>clazz<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isExcluded<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> excludedPackages<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> isIncluded<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> includedPackages<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isExcludedByClassLoader<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> classLoader<span style="color:#719e07">,</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据类全路径加载类到内存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadClass<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> resourceURL<span style="color:#719e07">,</span> Class<span style="color:#719e07">.</span>forName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> classLoader<span style="color:#719e07">),</span> name<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> IllegalStateException e <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load extension class (interface: &#34;</span> <span style="color:#719e07">+</span> type <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;, class line: &#34;</span> <span style="color:#719e07">+</span> line <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) in &#34;</span> <span style="color:#719e07">+</span> resourceURL <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exceptions<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>line<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Exception occurred when loading extension class (interface: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, class file: &#34;</span> <span style="color:#719e07">+</span> resourceURL <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) in &#34;</span> <span style="color:#719e07">+</span> resourceURL<span style="color:#719e07">,</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中的loadClass方法加载具体的类到内存</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadClass</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL resourceURL<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">,</span> String name<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> NoSuchMethodException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前clazz是否为type的子类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里第一次访问到的type是ExtensionInjector,clazz是SpringExtensionInjector 父子类型关系满足情况 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>type<span style="color:#719e07">.</span>isAssignableFrom<span style="color:#719e07">(</span>clazz<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Error occurred when loading extension class (interface: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, class line: &#34;</span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;), class &#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is not subtype of interface.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展子类型是否存在这个注解@Adaptive |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>clazz<span style="color:#719e07">.</span>isAnnotationPresent<span style="color:#719e07">(</span>Adaptive<span style="color:#719e07">.</span>class<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheAdaptiveClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isWrapperClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展子类型构造器中是否有这个类型的接口 (这个可以想象下我们了解的Java IO流中的类型使用到的装饰器模式 构造器传个类型) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheWrapperClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//无自适应注解,也没有构造器是扩展类型参数 ,这个name我们在扩展文件中找到了就是等号前面那个 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//低版本中可以使用@Extension 扩展注解来标注扩展类型,这里获取注解有两个渠道: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//先查询@Extension注解是否存在如果存在则取value值,如果不存在@Extension注解则获取当前类型的名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> name <span style="color:#719e07">=</span> findAnnotationName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>name<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No such extension name for the class &#34;</span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in the config &#34;</span> <span style="color:#719e07">+</span> resourceURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取扩展名字数组,扩展名字可能为逗号隔开的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String<span style="color:#719e07">[]</span> names <span style="color:#719e07">=</span> NAME_SEPARATOR<span style="color:#719e07">.</span>split<span style="color:#719e07">(</span>name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ArrayUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>names<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//@Activate注解修饰的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheActivateClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> names<span style="color:#719e07">[</span>0<span style="color:#719e07">]);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String n <span style="color:#719e07">:</span> names<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//cachedNames缓存集合缓存当前扩展类型的扩展名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> n<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将扩展类型加入结果集合extensionClasses中,不允许覆盖的话出现同同名字扩展将抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> saveInExtensionClass<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> clazz<span style="color:#719e07">,</span> n<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中cacheAdaptiveClass |
| Adaptive 机制,即扩展类的自适应机制。即其可以指定想要加载的扩展名,也可以不指定。若不指定,则直接加载默认的扩展类。即其会自动匹配,做到自适应。其是通过@Adaptive注解实现的。 |
| 自适应注解修饰的扩展同一个扩展名字只能有一个扩展实现类型, 扩展策略中提供的参数overridden是否允许覆盖扩展覆盖</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">cacheAdaptiveClass</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedAdaptiveClass <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> overridden<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//成员变量存储这个自适应扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedAdaptiveClass <span style="color:#719e07">=</span> clazz<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>cachedAdaptiveClass<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>clazz<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;More than 1 adaptive class found: &#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> cachedAdaptiveClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, &#34;</span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中cacheWrapperClass |
| Wrapper 机制,即扩展类的包装机制。就是对扩展类中的 SPI 接口方法进行增强,进行包装,是 AOP 思想的体现,是 Wrapper 设计模式的应用。一个 SPI 可以包含多个 Wrapper。这个也是可以同一个类型多个</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">cacheWrapperClass</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedWrapperClasses <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cachedWrapperClasses <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashSet<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存这个Wrapper类型的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedWrapperClasses<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中cacheActivateClass |
| Activate用于激活扩展类的。 这个扩展类型可以出现多个比如过滤器可以同一个扩展名字多个过滤器实现,所以不需要有override判断 |
| Activate 机制,即扩展类的激活机制。通过指定的条件来激活当前的扩展类。其是通过@Activate 注解实现的。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">cacheActivateClass</span>(Class<span style="color:#719e07">&lt;?&gt;</span> clazz, String name) { |
| </span></span><span style="display:flex;"><span> Activate activate <span style="color:#719e07">=</span> clazz.getAnnotation(Activate.<span style="color:#719e07">class</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (activate <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存Activate类型的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivates.put(name, activate); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// support com.alibaba.dubbo.common.extension.Activate |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> com.alibaba.dubbo.common.extension.Activate oldActivate <span style="color:#719e07">=</span> clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.<span style="color:#719e07">class</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (oldActivate <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> cachedActivates.put(name, oldActivate); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>ExtensionLoader类型中的saveInExtensionClass方法</p> |
| <p>上面扩展对象加载了这么多最终的目的就是将这个扩展类型存放进结果集合extensionClasses中,扩展策略中提供的参数overridden是否允许覆盖扩展覆盖</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">saveInExtensionClass</span>(Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses, Class<span style="color:#719e07">&lt;?&gt;</span> clazz, String name, boolean overridden) { |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;</span> c <span style="color:#719e07">=</span> extensionClasses.get(name); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (c <span style="color:#719e07">==</span> null <span style="color:#719e07">||</span> overridden) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//上面扩展对象加载了这么多最终的目的就是将这个扩展类型存放进结果集合中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extensionClasses.put(name, clazz); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> (c <span style="color:#719e07">!=</span> clazz) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// duplicate implementation is unacceptable |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> unacceptableExceptions.add(name); |
| </span></span><span style="display:flex;"><span> String duplicateMsg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Duplicate extension &#34;</span> <span style="color:#719e07">+</span> type.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; name &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; on &#34;</span> <span style="color:#719e07">+</span> c.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; and &#34;</span> <span style="color:#719e07">+</span> clazz.getName(); |
| </span></span><span style="display:flex;"><span> logger.error(duplicateMsg); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException(duplicateMsg); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h2 id="53-自适应扩展代理对象的代码生成与编译">5.3 自适应扩展代理对象的代码生成与编译</h2> |
| <h3 id="531-自适应扩展对象的创建">5.3.1 自适应扩展对象的创建</h3> |
| <p>Dubbo 的<strong>自适应扩展机制</strong>中如果 <strong>自己生成了自适应扩展的代理类</strong></p> |
| <p>Dubbo 的自适应扩展为了做什么:<strong>在运行时动态调用扩展方法</strong>。以及怎么做的:生成扩展代理类。比如: 代理类中根据 URL 获取扩展名,使用 SPI 加载扩展类,并调用同名方法,返回执行结果。</p> |
| <p>看了上一个章节,我们了解到了Dubbo是如何通过扫描目录来查询扩展实现类的这一次我们看下扩展类我们找到了之后,如果这个扩展类型未加上这个@Adaptive注解那么是如何创建这个类型的,接下来看createAdaptiveExtensionClass方法,这个方法是借助字节码工具来动态生成所需要的扩展类型的包装类型的代码,这个代码在编译时我们可能看不到,但是在Debug的时候,我们还是可以看到这个对象名字的,但是往往Debug的时候又进不到具体的代码位置,这里可以注意下</p> |
| <p>当扩展点的方法被@Adaptive修饰时,在Dubbo初始化扩展点时会自动生成和编译一个动态的Adaptive类。</p> |
| <p>下面我们可以以interface org.apache.dubbo.rpc.Protocol 这个协议扩展类型来看 协议扩展类型目前没有一个是带有自适应注解的</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> Class<span style="color:#719e07">&lt;?&gt;</span> createAdaptiveExtensionClass<span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Adaptive Classes&#39; ClassLoader should be the same with Real SPI interface classes&#39; ClassLoader |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ClassLoader classLoader <span style="color:#719e07">=</span> type<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// //native配置 是否为本地镜像(可以参考官方文档:https://dubbo.apache.org/zh-cn/docs/references/graalvm/support-graalv |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>NativeUtils<span style="color:#719e07">.</span>isNative<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> classLoader<span style="color:#719e07">.</span>loadClass<span style="color:#719e07">(</span>type<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;$Adaptive&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable ignore<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个代码生成器,来生成代码 详细内容我们就下一章来看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String code <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> AdaptiveClassCodeGenerator<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> cachedDefaultName<span style="color:#719e07">).</span>generate<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取编译器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>compiler<span style="color:#719e07">.</span>Compiler compiler <span style="color:#719e07">=</span> extensionDirector<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>compiler<span style="color:#719e07">.</span>Compiler<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成的代码进行编译 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> compiler<span style="color:#719e07">.</span>compile<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> code<span style="color:#719e07">,</span> classLoader<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="54-为扩展对象的set方法注入自适应扩展对象">5.4 为扩展对象的set方法注入自适应扩展对象</h2> |
| <p>在4.4.5小节中我们已经讲解了获取扩展类型实现类, 创建扩展对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> T instance <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> getAdaptiveExtensionClass<span style="color:#719e07">().</span>newInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>接下来就让我们来看下为扩展对象的set方法注入自适应的扩展对象 |
| 调用方法代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象之前的回调方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>injectExtension<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型的injectExtension方法具体代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> T <span style="color:#268bd2">injectExtension</span>(T instance) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果注入器为空则直接返回当前对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (injector <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取当前对象的当前类的所有方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> (Method method : instance.getClass().getMethods()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否为set方法 不是的话则跳过,在这里合法的set方法满足3个条件: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//set开头,参数只有一个,public修饰 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isSetter(method)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Check {@link DisableInject} to see if we need auto injection for this property |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//方法上面是否有注解DisableInject修饰,这种情况也直接跳过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (method.isAnnotationPresent(DisableInject.<span style="color:#719e07">class</span>)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//方法的参数如果是原生类型也跳过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Class<span style="color:#719e07">&lt;?&gt;</span> pt <span style="color:#719e07">=</span> method.getParameterTypes()[<span style="color:#2aa198">0</span>]; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ReflectUtils.isPrimitives(pt)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取set方法对应的成员变量如setProtocol 属性为protocol |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String property <span style="color:#719e07">=</span> getSetterProperty(method); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据参数类型如Protocol和属性名字如protocol获取应该注入的对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Object object <span style="color:#719e07">=</span> injector.getInstance(pt, property); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (object <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//执行对应对象和对应参数的这个方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> method.invoke(instance, object); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> logger.error(<span style="color:#2aa198">&#34;Failed to inject via method &#34;</span> <span style="color:#719e07">+</span> method.getName() |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; of interface &#34;</span> <span style="color:#719e07">+</span> type.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;: &#34;</span> <span style="color:#719e07">+</span> e.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> logger.error(e.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="541-获取注入对象">5.4.1 获取注入对象</h3> |
| <p>这里我们主要来看下如何通过注入器找到需要注入的那个对象 调用代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> Object object <span style="color:#719e07">=</span> injector.getInstance(pt, property); |
| </span></span></code></pre></div><p>在前面看注入器扩展对象的获取的时候是会获取到ExtensionInjector扩展的一个自适应扩展注入器实现类型 AdaptiveExtensionInjector,这个地方对应的getInstance也是这个扩展里面的,我们来看下它的方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T <span style="color:#268bd2">getInstance</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的扩展注入器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ExtensionInjector injector <span style="color:#719e07">:</span> injectors<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的扩展注入器,如果可以获取到扩展对象则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T extension <span style="color:#719e07">=</span> injector<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>extension <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extension<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看到上面代码按扩展注入器顺序来遍历的第一个找到的对象就直接返回了,</p> |
| <p>这个AdaptiveExtensionInjector在初始化的时候会获取所有的ExtensionInjector的扩展,非自适应的,它本身自适应的扩展,这里会获取非自适应的扩展列表一共有3个按顺序为:</p> |
| <ul> |
| <li>ScopeBeanExtensionInjector</li> |
| <li>SpiExtensionInjector</li> |
| <li>SpringExtensionInjector</li> |
| </ul> |
| <p>接下来我们详细看下每种扩展注入器加载扩展对象的策略:</p> |
| <h3 id="542-域模型中的bean扩展注入器scopebeanextensioninjector">5.4.2 域模型中的Bean扩展注入器ScopeBeanExtensionInjector</h3> |
| <p>ScopeBeanExtensionInjector的getInstance方法: |
| 每个域模型都会有个ScopeBeanFactory类型的对象用于存储共享对象,并且域模型之间按照层级子类型的Bean工厂可以从父域的Bean工厂中查询对象,</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>@Override |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getInstance(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> beanFactory.getBean(name, type); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>ScopeBeanFactory的getBean方法 |
| 先从当前域空间查询对象,如果找不到对应类型的扩展对象则从父域工厂查询扩展对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getBean(String name, Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前域下注册的扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T bean <span style="color:#719e07">=</span> getBeanInternal(name, type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (bean <span style="color:#719e07">==</span> null <span style="color:#719e07">&amp;&amp;</span> parent <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//父域中查找扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> parent.getBean(name, type); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> bean; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>ScopeBeanFactory的getBeanInternal方法 |
| 从当前域下找注册的参数类型的对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getBeanInternal(String name, Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type) { |
| </span></span><span style="display:flex;"><span> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// All classes are derived from java.lang.Object, cannot filter bean by it |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (type <span style="color:#719e07">==</span> Object.<span style="color:#719e07">class</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>BeanInfo<span style="color:#719e07">&gt;</span> candidates <span style="color:#719e07">=</span> null; |
| </span></span><span style="display:flex;"><span> BeanInfo firstCandidate <span style="color:#719e07">=</span> null; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历列表查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> (BeanInfo beanInfo : registeredBeanInfos) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if required bean type is same class/superclass/interface of the registered bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (type.isAssignableFrom(beanInfo.instance.getClass())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEquals(beanInfo.name, name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) beanInfo.instance; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// optimize for only one matched bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (firstCandidate <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> firstCandidate <span style="color:#719e07">=</span> beanInfo; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (candidates <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> candidates <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> candidates.add(firstCandidate); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> candidates.add(beanInfo); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if bean name not matched and only single candidate |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (candidates <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (candidates.size() <span style="color:#719e07">==</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) candidates.get(<span style="color:#2aa198">0</span>).instance; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (candidates.size() <span style="color:#719e07">&gt;</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> candidateBeanNames <span style="color:#719e07">=</span> candidates.stream().map(beanInfo <span style="color:#719e07">-&gt;</span> beanInfo.name).collect(Collectors.toList()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> ScopeBeanException(<span style="color:#2aa198">&#34;expected single matching bean but found &#34;</span> <span style="color:#719e07">+</span> candidates.size() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; candidates for type [&#34;</span> <span style="color:#719e07">+</span> type.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;]: &#34;</span> <span style="color:#719e07">+</span> candidateBeanNames); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (firstCandidate <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) firstCandidate.instance; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="543-spi扩展机制注入器spiextensioninjector">5.4.3 SPI扩展机制注入器SpiExtensionInjector</h3> |
| <p>SPI是Dubbo自行实现的一套扩展机制,我们来看下它是如何查找扩展对象的</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>@Override |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getInstance(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果是一个标准的被@SPI注解修饰的扩展接口则满足条件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (type.isInterface() <span style="color:#719e07">&amp;&amp;</span> type.isAnnotationPresent(SPI.<span style="color:#719e07">class</span>)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用扩展访问器来获取对应类型的扩展加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> loader <span style="color:#719e07">=</span> extensionAccessor.getExtensionLoader(type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (loader <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用对应类型的扩展加载器来加载自适应扩展 这个加载的扩展可以参考4.4.6小节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>loader.getSupportedExtensions().isEmpty()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader.getAdaptiveExtension(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="544-spring扩展注入器">5.4.4 Spring扩展注入器</h3> |
| <p>SpringExtensionInjector</p> |
| <p>Spring扩展注入器主要是用来从Spring容器中查询当前类型的Bean是否存在的,如下代码直接看代码吧</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>@Override |
| </span></span><span style="display:flex;"><span> @SuppressWarnings(<span style="color:#2aa198">&#34;unchecked&#34;</span>) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getInstance(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, String name) { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (context <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore if spring context is not bound |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check @SPI annotation ,类型需要满足SPI机制 @SPI修饰的接口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (type.isInterface() <span style="color:#719e07">&amp;&amp;</span> type.isAnnotationPresent(SPI.<span style="color:#719e07">class</span>)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从Spring容器中查询Bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T bean <span style="color:#719e07">=</span> getOptionalBean(context, name, type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (bean <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> bean; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//logger.warn(&#34;No spring extension (bean) named:&#34; + name + &#34;, try to find an extension (bean) of type &#34; + type.getName()); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getOptionalBean(ListableBeanFactory beanFactory, String name, Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//要搜索的扩展名字为空就根据类型搜索 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//返回与给定类型(包括子类)匹配的bean的名称,对于FactoryBeans |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String[] beanNamesForType <span style="color:#719e07">=</span> beanFactory.getBeanNamesForType(type, <span style="color:#b58900">true</span>, <span style="color:#b58900">false</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (beanNamesForType <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (beanNamesForType.length <span style="color:#719e07">==</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//返回指定bean的实例,该实例可以是共享的,也可以是独立的。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//根据Bean Name和类型 查询具体的扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> beanFactory.getBean(beanNamesForType[<span style="color:#2aa198">0</span>], type); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (beanNamesForType.length <span style="color:#719e07">&gt;</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException(<span style="color:#2aa198">&#34;Expect single but found &#34;</span> <span style="color:#719e07">+</span> beanNamesForType.length <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; beans in spring context: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> Arrays.toString(beanNamesForType)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展名字不为空则直接通过名字搜索Bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (beanFactory.containsBean(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> beanFactory.getBean(name, type); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/5-dubbo-de-spi-kuo-zhan-ji-zhi-yu-zi-gua-ying-kuo-zhan-dui-xiang-de-chuang-jian-yu-kuo-zhan-wen-jian-de-sao-miao-yuan-ma-jie-xi/">《自适应扩展对象的创建getAdaptiveExtension方法》</a></p></description></item><item><title>Blog: 04-Dubbo的扩展机制</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/04/04-dubbo%E7%9A%84%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6/</link><pubDate>Thu, 04 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/04/04-dubbo%E7%9A%84%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6/</guid><description> |
| <h1 id="4-dubbo的扩展机制">4-Dubbo的扩展机制</h1> |
| <h2 id="41-回顾我们前面使用到扩展场景">4.1 回顾我们前面使用到扩展场景</h2> |
| <p>在上一章中我们初始化应用模型对象的时候,了解到有几个地方用到了扩展机制来创建对象,这一章我们会详细来讲一下这个扩展对象的加载过程,这里我们先来回顾下哪些地方用到了扩展机制:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// 使用扩展机制获取TypeBuilder |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>Set<span style="color:#719e07">&lt;</span>TypeBuilder<span style="color:#719e07">&gt;</span> tbs <span style="color:#719e07">=</span> model<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>TypeBuilder<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//获取域模型初始化器ScopeModelInitializer扩展对象,执行初始化方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// OrderedPropertiesConfiguration 中获取有序配置提供器对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>ExtensionLoader<span style="color:#719e07">&lt;</span>OrderedPropertiesProvider<span style="color:#719e07">&gt;</span> propertiesProviderExtensionLoader <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>OrderedPropertiesProvider<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// ApplicationModel中获取配置管理器对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ConfigManager<span style="color:#719e07">)</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationExt<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>ConfigManager<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//ModuleModel中获取模块扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>Set<span style="color:#719e07">&lt;</span>ModuleExt<span style="color:#719e07">&gt;</span> exts <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ModuleExt<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// ApplicationModel中获Environment对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>environment <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>Environment<span style="color:#719e07">)</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationExt<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>Environment<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// ApplicationModel中获取应用初始化监听器ApplicationInitListener扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>ExtensionLoader<span style="color:#719e07">&lt;</span>ApplicationInitListener<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationInitListener<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span>Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> listenerNames <span style="color:#719e07">=</span> extensionLoader<span style="color:#719e07">.</span>getSupportedExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//ScopeModel中创建扩展访问器: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ExtensionDirector<span style="color:#719e07">(</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent<span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> scope<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>有了以上的应用场景我们可以来看下扩展机制了</p> |
| <h2 id="42-为什么要用到扩展机制">4.2 为什么要用到扩展机制?</h2> |
| <p>为什么要用到扩展这个想必每个编程人员都比较了解,一个好的程序是要遵循一定的设计规范比如设计模式中的<strong>开闭原则</strong> 英文全称是 Open Closed Principle,简写为 OCP,对扩展开放、对修改关闭:</p> |
| <p><strong>对扩展开放:</strong> 指的是我们系统中的模块、类、方法对它们的提供者(开发者)应该是开放的,提供者可以对系统进行扩展(新增)新的功能。</p> |
| <p><strong>对修改关闭:</strong> 指的是系统中的模块、类、方法对它们的使用者(调用者)应该是关闭的。使用者使用这些功能时,不会因为提供方新增了功能而导致使用者也进行相应修改。</p> |
| <p>我们再来了解下Dubbo的一些基本特性: |
| 下面这句话是我摘自官网的: |
| <em>Apache Dubbo 是一款微服务开发框架,它提供了 <strong>RPC通信</strong> 与 <strong>微服务治理</strong> 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在<strong>任意功能点去定制自己的实现</strong>,以改变框架的默认行为来满足自己的业务需求。 |
| Dubbo3 基于 Dubbo2 演进而来,在保持原有核心功能特性的同时, Dubbo3 在易用性、超大规模微服务实践、云原生基础设施适配、安全设计等几大方向上进行了全面升级。 以下文档都将基于 Dubbo3 展开。</em></p> |
| <p><strong>对修改关闭的地方:</strong> 对于Apache Dubbo来说 不变的是RPC调用流程,微服务治理这些抽象的概念,我们可以用摘自官网的下面几个图表示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-arch.png" alt="在这里插入图片描述"></p> |
| <center>图4.1 Dubbo架构图</center> |
| <p>再来看一个调用链路的架构图</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-arch2.png" alt="在这里插入图片描述"></p> |
| <center>图4.2 Dubbo RPC调用链路</center> |
| <br/> |
| 上面两个图整体来看都是Dubbo不变的地方涉及到服务的RPC调用和服务治理的一些概念与流程,但是对于每个环节又可以使用各种方式实现,比如序列化机制可以是Json,Java序列化,Hession2或者Protobuf等等,网络传输层可以是netty实现的tcp通信,也可以使用http协议,那Dubbo又是如何封装不变部分扩展这种可变部分呢?,那就是接下来要说的**微内核机制**,这个我们待会说 |
| <p><strong>对扩展开放:</strong> : 对于Apache Dubbo来说 变化的是RPC调用流程和微服务治理这些抽象的概念的具体实现,每个点应该用什么技术实现,又是用什么场景,这个可以用如下图来表示下: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-arch3.png" alt="在这里插入图片描述"></p> |
| <center>图4.3 Dubbo的扩展生态</center> |
| </br> |
| <p>看到这里 应该各位就明白了,我们写程序是为了业务,而针对不同的业务需求很多场景下我们是需要使用不同的实现来满足的,Dubbo使用微内核的架构,将具体的实现开放出来,让使用者可以根据自己的需求来选择,定制. Dubbo开放了很多的扩展点供大家扩展,可想而知使用Dubbo的灵活性是非常高的。</p> |
| <p><strong>微内核架构:</strong> |
| 微内核架构由两大架构模块组成:<strong>核心系统</strong>与<strong>插件模块</strong>,设计一个微内核体系关键工作全部集中于核心系统怎么构建。 |
| <strong>核心系统</strong> : 负责和具体业务功能无关的通用功能,例如模块加载、模块间通信等,这个其实对应着Dubbo的SPI机制。 |
| <strong>插件模块</strong> : 负责实现具体的业务逻辑,Dubbo,SPI接口与实现。</p> |
| <h2 id="43-dubbo的扩展机制包含了哪些重要的组成部分">4.3 Dubbo的扩展机制包含了哪些重要的组成部分?</h2> |
| <p>前面我们说了为什么要使用扩展机制,这里我们来看下具体实现</p> |
| <p>先将扩展包里面的代码截个图认识认识各类型的单词 |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-extension.png" alt="在这里插入图片描述"></p> |
| <p>顺便我们先简单看下类结构图,后续再详细看每个类型的解释: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-extension2.png" alt="在这里插入图片描述"></p> |
| <p>为了后续看具体的扩展加载流程我们先看下以上类型的解释说明:</p> |
| <ul> |
| <li> |
| <p><strong>ExtensionAccessor</strong></p> |
| <ul> |
| <li>扩展的统一访问器</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionDirector</strong></p> |
| <ul> |
| <li>ExtensionDirector是一个作用域扩展加载程序管理器。</li> |
| <li>ExtensionDirector支持多个级别,子级可以继承父级的扩展实例。 |
| 查找和创建扩展实例的方法类似于Java classloader。</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionScope</strong></p> |
| <ul> |
| <li>扩展SPI域,目前有FRAMEWORK,APPLICATION,MODULE,SELF</li> |
| <li><strong>FRAMEWORK</strong> : 扩展实例在框架内使用,与所有应用程序和模块共享。 |
| 框架范围SPI扩展只能获取FrameworkModel,无法获取ApplicationModel和ModuleModel。 |
| 考虑: |
| 一些SPI需要在框架内的应用程序之间共享数据 |
| 无状态SPI在框架内是安全共享的</li> |
| <li><strong>APPLICATION</strong> 扩展实例在一个应用程序中使用,与应用程序的所有模块共享,不同的应用程序创建不同的扩展实例。 |
| 应用范围SPI扩展可以获取FrameworkModel和ApplicationModel,无法获取ModuleModel。 |
| 考虑: |
| 在框架内隔离不同应用程序中的扩展数据 |
| 在应用程序内部的所有模块之间共享扩展数据</li> |
| <li><strong>MODULE</strong> 扩展实例在一个模块中使用,不同的模块创建不同的扩展实例。 |
| 模块范围SPI扩展可以获得FrameworkModel、ApplicationModel和ModuleModel。 |
| 考虑: |
| 隔离应用程序内部不同模块中的扩展数据</li> |
| <li><strong>SELF</strong> 自给自足,为每个作用域创建一个实例,用于特殊的SPI扩展,如ExtensionInjector</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionLoader</strong></p> |
| <ul> |
| <li>ApplicationModel、DubboBootstrap和这个类目前被设计为单例或静态(本身完全静态或使用一些静态字段)。因此,从它们返回的实例属于process或classloader范围。如果想在一个进程中支持多个dubbo服务器,可能需要重构这三个类。</li> |
| <li>加载dubbo扩展</li> |
| <li>自动注入依赖项扩展</li> |
| <li>包装器中的自动包装扩展</li> |
| <li>默认扩展是一个自适应实例</li> |
| <li>JDK自带SPI参考地址 <a href="https://docs.oracle.com/javase/1.5.0/docs/guide/jar/jar.html#Service%20Provider">点击查看</a></li> |
| <li>@SPI 服务扩展接口 详细内容看后面</li> |
| <li>@Adaptive自适应扩展点注解 详细内容看后面</li> |
| <li>@Activate自动激活扩展点注解 详细内容看后面</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionPostProcessor</strong></p> |
| <ul> |
| <li>在扩展初始化之前或之后调用的后处理器。</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>LoadingStrategy</strong></p> |
| <ul> |
| <li>扩展加载策略,目前有3个扩展加载策略分别从不同文件目录加载扩展</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>DubboInternalLoadingStrategy</strong></p> |
| <ul> |
| <li>Dubbo内置的扩展加载策略,将加载文件目录为META-INF/dubbo/internal/的扩展</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>DubboLoadingStrategy</strong></p> |
| <ul> |
| <li>Dubbo普通的扩展加载策略,将加载目录为META-INF/dubbo/的扩展</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ServicesLoadingStrategy</strong></p> |
| <ul> |
| <li>JAVA SPI加载策略 ,将加载目录为META-INF/services/的扩展</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>Wrapper</strong>注解</p> |
| </li> |
| <li> |
| <p>SPI注解</p> |
| </li> |
| <li> |
| <p><strong>ExtensionInjector</strong>接口</p> |
| <ul> |
| <li>为SPI扩展提供资源的注入器。</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionAccessorAware</strong></p> |
| <ul> |
| <li>SPI扩展可以实现这个感知接口,以获得适当的xtensionAccessor实例。</li> |
| </ul> |
| </li> |
| <li> |
| <p>DisableInject注解</p> |
| </li> |
| <li> |
| <p><strong>AdaptiveClassCodeGenerator</strong></p> |
| <ul> |
| <li>自适应类的代码生成器</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>Adaptive</strong>注解</p> |
| </li> |
| <li> |
| <p>为ExtensionLoader注入依赖扩展实例提供有用信息。</p> |
| </li> |
| <li> |
| <p><strong>Activate</strong>注解</p> |
| <ul> |
| <li>Activate。此注解对于使用给定条件自动激活某些扩展非常有用,例如:@Activate可用于在有多个实现时加载某些筛选器扩展。 |
| **group()**指定组条件。框架SPI定义了有效的组值。 |
| **value()**指定URL条件中的参数键。 |
| SPI提供程序可以调用ExtensionLoader。getActivateExtension(URL、String、String)方法以查找具有给定条件的所有已激活扩展。</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ActivateComparator</strong></p> |
| <ul> |
| <li>Activate扩展的排序器</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>MultiInstanceActivateComparator</strong></p> |
| </li> |
| <li> |
| <p><strong>WrapperComparator</strong></p> |
| </li> |
| <li> |
| <p><strong>AdaptiveExtensionInjector</strong></p> |
| </li> |
| <li> |
| <p><strong>SpiExtensionInjector</strong></p> |
| </li> |
| </ul> |
| <h2 id="44-扩展加载创建之前的调用过程">4.4 扩展加载创建之前的调用过程</h2> |
| <h3 id="441-扩展的调用代码示例">4.4.1 扩展的调用代码示例</h3> |
| <p>了解了这么多与扩展相关的概念,接下来我们就来从前面的代码调用中找几个例子来看下扩展的调用过程:</p> |
| <p>代码来源于FrameworkModel对象的初始化initialize()中的如下代码调用:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> TypeDefinitionBuilder<span style="color:#719e07">.</span>initBuilders<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>TypeDefinitionBuilder中初始化类型构建器代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initBuilders</span><span style="color:#719e07">(</span>FrameworkModel model<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>TypeBuilder<span style="color:#719e07">&gt;</span> tbs <span style="color:#719e07">=</span> model<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>TypeBuilder<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> BUILDERS <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>tbs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="442-dubbo的分层模型获取扩展加载器对象">4.4.2 Dubbo的分层模型获取扩展加载器对象</h3> |
| <p>以上扩展调用的时候对于扩展加载器对象的获取代码如下所示,我们来看下它的调用链路</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>model<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>TypeBuilder<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span></code></pre></div><p>getExtensionLoader方法来源于FrameworkModel类型的父类型ScopeModel的实现的接口ExtensionAccessor中的默认方法(JDK8 默认方法)</p> |
| <p>ExtensionAccessor接口中的getExtensionLoader方法如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">default</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getExtensionLoader</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">().</span>getExtensionLoader<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>获取扩展加载器之前需要先获取扩展访问器: |
| 这里的链路先梳理下:</p> |
| <p><strong>模型对象(FrameworkModel)-</strong>&ndash;&gt; <strong>扩展访问器(ExtensionAccessor)</strong> &mdash;&gt; <strong>作用域扩展加载程序管理器(ExtensionDirector)</strong> &mdash;&gt;</p> |
| <p>这个getExtensionDirector()方法来源于FrameworkModel的抽象父类型ScopeModel中的getExtensionDirector()如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> ExtensionDirector <span style="color:#268bd2">getExtensionDirector</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extensionDirector<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里直接返回了extensionDirector,,不知道介绍到这里记得这个扩展加载程序管理器extensionDirector对象的由来不, 在上个章节<a href="https://blog.elastic.link/2022/07/10/dubbo/3-kuang-jia-ying-yong-cheng-xu-mo-kuai-ling-yu-mo-xing-model-dui-xiang-de-chu-shi-hua/">《3-框架,应用程序,模块领域模型Model对象的初始化》</a>中3.2.2 初始化ScopeModel的章节中的ScopeModel类型的初始化方法initialize()方法中我们提到过这个对象的创建,具体代码如下所示(这个代码比较简单):</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ExtensionDirector<span style="color:#719e07">(</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent<span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> scope<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>我们继续前面getExtensionLoader(type)方法调用逻辑,前面我们知道了这个扩展访问器的对象是ExtensionDirector,接下来我们看下ExtensionDirector中获取扩展加载器的代码(如下所示): |
| 在详细介绍扩展加载器对象获取之前我们先来看下当前我们要加载的扩展类型的源码,后续会用到: |
| 我们要加载的扩展类型TypeBuilder接口</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@SPI</span><span style="color:#719e07">(</span>scope <span style="color:#719e07">=</span> ExtensionScope<span style="color:#719e07">.</span>FRAMEWORK<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">TypeBuilder</span> <span style="color:#268bd2">extends</span> Prioritized <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Whether the build accept the class passed in. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">accept</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Build type definition with the type or class. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> TypeDefinition <span style="color:#268bd2">build</span><span style="color:#719e07">(</span>Type type<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> TypeDefinition<span style="color:#719e07">&gt;</span> typeCache<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionDirector类型中获取扩展加载器的代码 |
| 这个代码非常有意思 <strong>其实就是前面说到的域模型架构的数据访问架构</strong>类似于JVM类加载器访问加载类的情况,但是这个顺序可能有所不同,Dubbo的扩展加载器是如何访问的呢? 遵循以下顺序:</p> |
| <ul> |
| <li>先从<strong>缓存中</strong>查询扩展加载器</li> |
| <li>如果前面没找到则查询扩展类型的scope所属域,如果是<strong>当前域扩展</strong>则从直接创建扩展加载器</li> |
| <li>如果前面没找到就从<strong>父扩展访问器</strong>中查询,查询这个扩展是否数据父扩展域</li> |
| <li>前面都没找到就尝试创建</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getExtensionLoader</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果扩展加载器已经被销毁则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里参数类型传的是TypeBuilder.class不为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>type <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Extension type == null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展类型不为接口也要抛出异常,这个TypeBuilder.class具体类型代码往上看,这个类型是一个接口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>type<span style="color:#719e07">.</span>isInterface<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Extension type (&#34;</span> <span style="color:#719e07">+</span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) is not an interface!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个判断逻辑是判断这个扩展接口是有有@SPI注解,TypeBuilder是有的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>withExtensionAnnotation<span style="color:#719e07">(</span>type<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Extension type (&#34;</span> <span style="color:#719e07">+</span> type <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;) is not an extension, because it is NOT annotated with @&#34;</span> <span style="color:#719e07">+</span> SPI<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 1. find in local cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//被加载的扩展类型对应的扩展加载器会放到extensionLoadersMap这个ConcurrentHashMap类型的集合中方便缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> loader <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> extensionLoadersMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//查询扩展所属域,这个类型的扩展域是框架级别的ExtensionScope.FRAMEWORK |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//extensionScopeMap为ConcurrentHashMap类型的扩展域缓存集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionScope scope <span style="color:#719e07">=</span> extensionScopeMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scope <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> SPI annotation <span style="color:#719e07">=</span> type<span style="color:#719e07">.</span>getAnnotation<span style="color:#719e07">(</span>SPI<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> scope <span style="color:#719e07">=</span> annotation<span style="color:#719e07">.</span>scope<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> extensionScopeMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> scope<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//首次访问的时候当前类型的扩展加载器类型肯定是空的,会走如下两个逻辑中的其中一个进行创建扩展加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//1)如果 扩展域为SELF 自给自足,为每个作用域创建一个实例,用于特殊的SPI扩展,如{@link ExtensionInjector} |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> scope <span style="color:#719e07">==</span> ExtensionScope<span style="color:#719e07">.</span>SELF<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// create an instance in self scope |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loader <span style="color:#719e07">=</span> createExtensionLoader0<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 2. find in parent |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//3) 从父扩展加载器中查询当前扩展加载器是否存在,这里parent是空的先不考虑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> loader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>parent<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 3. create it |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//4) 这个是我们本次会走的逻辑,大部分是会走这个逻辑来创建扩展加载器对象的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> loader <span style="color:#719e07">=</span> createExtensionLoader<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面提到的withExtensionAnnotation判断代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">withExtensionAnnotation</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> type<span style="color:#719e07">.</span>isAnnotationPresent<span style="color:#719e07">(</span>SPI<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionDirector类型的createExtensionLoader方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">createExtensionLoader</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> loader <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前类型注解的scope与当前扩展访问器ExtensionDirector的scope是否一致,不一致则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//当前类型ExtensionDirector的scope是在构造器中传递的,在Model对象初始化的时候创建的本类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isScopeMatched<span style="color:#719e07">(</span>type<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if scope is matched, just create it |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loader <span style="color:#719e07">=</span> createExtensionLoader0<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if scope is not matched, ignore it |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionDirector类型的createExtensionLoader0方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">createExtensionLoader0</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查当前扩展访问器是否被销毁掉了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> loader<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为当前扩展类型创建一个扩展访问器并缓存到,当前成员变量extensionLoadersMap中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extensionLoadersMap<span style="color:#719e07">.</span>putIfAbsent<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>type<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> scopeModel<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> loader <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> extensionLoadersMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="443-扩展加载器对象extensionloader的构造器">4.4.3 扩展加载器对象ExtensionLoader的构造器</h3> |
| <p>扩展加载器相对来说是比较复杂的实现内容比较多,用到哪里我们说下哪里,这里先来看ExtensionLoader的构造器代码如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> type<span style="color:#719e07">,</span> ExtensionDirector extensionDirector<span style="color:#719e07">,</span> ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前扩展加载器,需要加载的扩展的类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>type <span style="color:#719e07">=</span> type<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建扩展加载器的扩展访问器对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector <span style="color:#719e07">=</span> extensionDirector<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从扩展访问器中获取扩展执行前后的回调器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionPostProcessors <span style="color:#719e07">=</span> extensionDirector<span style="color:#719e07">.</span>getExtensionPostProcessors<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建实例化对象的策略对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initInstantiationStrategy<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果当前扩展类型为扩展注入器类型则设置当前注入器变量为空,否则的话获取一个扩展注入器扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>injector <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>type <span style="color:#719e07">==</span> ExtensionInjector<span style="color:#719e07">.</span>class <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">:</span> extensionDirector<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExtensionInjector<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getAdaptiveExtension<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建Activate注解的排序器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>activateComparator <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ActivateComparator<span style="color:#719e07">(</span>extensionDirector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为扩展加载器下的域模型对象赋值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>先来看 创建实例化对象的策略对象代码 initInstantiationStrategy();</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initInstantiationStrategy</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ExtensionPostProcessor extensionPostProcessor <span style="color:#719e07">:</span> extensionPostProcessors<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ScopeModelAwareExtensionProcessor在域模型对象时候为扩展访问器添加了这个域模型扩展处理器对象ScopeModelAwareExtensionProcessor,这个类型实现了ScopeModelAccessor域模型访问器可以用来获取域模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>extensionPostProcessor <span style="color:#719e07">instanceof</span> ScopeModelAccessor<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> instantiationStrategy <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InstantiationStrategy<span style="color:#719e07">((</span>ScopeModelAccessor<span style="color:#719e07">)</span> extensionPostProcessor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instantiationStrategy <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> instantiationStrategy <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InstantiationStrategy<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>再来看ExtensionInjector扩展对象的获取</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//1)这里有个type为空的判断,普通的扩展类型肯定不是ExtensionInjector类型 这里必定会为每个非扩展注入ExtensionInjector类型创建一个ExtensionInjector类型的扩展对象, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">//2) 这里代码会走extensionDirector.getExtensionLoader(ExtensionInjector.class)这一步进去之后的代码刚刚看过就不再看了,这个代码会创建一个为ExtensionInjector扩展对象的加载器对象ExtensionLoader |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">//3) getAdaptiveExtension() 这个方法就是通过扩展加载器获取具体的扩展对象的方法我们会详细说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>injector <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>type <span style="color:#719e07">==</span> ExtensionInjector<span style="color:#719e07">.</span>class <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">:</span> extensionDirector<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExtensionInjector<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getAdaptiveExtension<span style="color:#719e07">());</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/4-dubbo-de-spi-kuo-zhan-ji-zhi-yu-extensionloader-dui-xiang-de-chuang-jian-yuan-ma-jie-xi/">&laquo;Dubbo的扩展机制&raquo;</a></p></description></item><item><title>Blog: 03-框架,应用程序,模块领域模型Model对象的初始化</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/03/03-%E6%A1%86%E6%9E%B6%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E6%A8%A1%E5%9D%97%E9%A2%86%E5%9F%9F%E6%A8%A1%E5%9E%8Bmodel%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96/</link><pubDate>Wed, 03 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/03/03-%E6%A1%86%E6%9E%B6%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E6%A8%A1%E5%9D%97%E9%A2%86%E5%9F%9F%E6%A8%A1%E5%9E%8Bmodel%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96/</guid><description> |
| <h1 id="3-框架应用程序模块领域模型model对象的初始化">3-框架,应用程序,模块领域模型Model对象的初始化</h1> |
| <p>在上一章中我们详细看了服务配置ServiceConfig类型的初始化,不过我们跳过了AbstractMethodConfig的构造器中创建模块模型对象的过程,那这一章我们就来看下模块模型对象的初始化过程:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractMethodConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">().</span>getDefaultModule<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>那为什么会在Dubbo3的新版本中加入这个域模型呢</strong>,主要有如下原因 |
| 之前dubbo都是只有一个作用域的,通过静态类 属性共享 |
| 增加域模型是为了:</p> |
| <ol> |
| <li>让Dubbo支持多应用的部署,这块一些大企业有诉求</li> |
| <li>从架构设计上,解决静态属性资源共享、清理的问题</li> |
| <li>分层模型将应用的管理和服务的管理分开</li> |
| </ol> |
| <p>可能比较抽象,可以具体点来看。Dubbo3中在启动时候需要启动配置中心、元数据中心,这个配置中心和元数据中心可以归应用模型来管理。Dubbo作为RPC框架又需要启动服务和引用服务,服务级别的管理就交给了这个模块模型来管理。分层次的管理方便我们理解和处理逻辑,父子级别的模型又方便了数据传递。</p> |
| <p>了解过JVM类加载机制的同学应该就比较清楚JVM类加载过程中的数据访问模型。子类加载器先交给父类加载器查找,找不到再从子类加载器中查找。Dubbo的分层模型类似这样一种机制,这一章先来简单了解下,后面用到时候具体细说。</p> |
| <h2 id="31-模型对象的关系">3.1 模型对象的关系</h2> |
| <p>为了不增加复杂性,我们这里仅仅列出模型对象类型类型之间的继承关系如下所示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/3-model.png" alt="在这里插入图片描述"></p> |
| <center>图3.1 模型对象的继承关系</center> |
| <p>模型对象一共有4个,公共的属性和操作放在了域模型类型中,下面我们来详细说下这几个模型类型:</p> |
| <ul> |
| <li> |
| <p><strong>ExtensionAccessor</strong> 扩展的统一访问器</p> |
| <ul> |
| <li>用于获取扩展加载管理器ExtensionDirector对象</li> |
| <li><strong>获取扩展对象ExtensionLoader</strong></li> |
| <li>根据扩展名字<strong>获取具体扩展对象</strong></li> |
| <li>获取自适应扩展对象</li> |
| <li>获取默认扩展对象</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ScopeModel</strong> 模型对象的公共抽象父类型</p> |
| <ul> |
| <li>内部id用于表示模型树的层次结构</li> |
| <li>公共模型名称,可以被用户设置</li> |
| <li>描述信息</li> |
| <li>类加载器管理</li> |
| <li>父模型管理parent</li> |
| <li>当前模型的所属域ExtensionScope有:<strong>FRAMEWORK(框架)</strong>,<strong>APPLICATION(应用)</strong>,<strong>MODULE(模块)</strong>,<strong>SELF(自给自足</strong>,为每个作用域创建一个实例,用于特殊的SPI扩展,如ExtensionInjector)</li> |
| <li>具体的扩展加载程序管理器对象的管理:<strong>ExtensionDirector</strong></li> |
| <li>域Bean工厂管理,一个内部共享的Bean工厂<strong>ScopeBeanFactory</strong></li> |
| <li>等等</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>FrameworkModel</strong> dubbo框架模型,可与多个应用程序共享</p> |
| <ul> |
| <li>FrameworkModel实例对象集合,allInstances</li> |
| <li>所有ApplicationModel实例对象集合,applicationModels</li> |
| <li>发布的ApplicationModel实例对象集合pubApplicationModels</li> |
| <li>框架的服务存储库<strong>FrameworkServiceRepository</strong>类型对象(数据存储在内存中)</li> |
| <li>内部的应用程序模型对象internalApplicationModel</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ApplicationModel</strong> 表示正在使用Dubbo的应用程序,并存储基本<strong>元数据信息</strong>,以便在RPC调用过程中使用。 |
| ApplicationModel包括许多关于<strong>发布服务</strong>的ProviderModel和许多关于订阅服务的Consumer Model。</p> |
| <ul> |
| <li>ExtensionLoader、DubboBootstrap和这个类目前被设计为单例或静态(本身完全静态或使用一些静态字段)。因此,从它们返回的实例属于流程范围。如果想在一个进程中支持多个dubbo服务器,可能需要重构这三个类。</li> |
| <li><strong>所有ModuleModel实例</strong>对象集合moduleModels</li> |
| <li><strong>发布的ModuleModel实例</strong>对象集合pubModuleModels</li> |
| <li><strong>环境信息Environment实例</strong>对象environment</li> |
| <li><strong>配置管理ConfigManager实例</strong>对象configManager</li> |
| <li><strong>服务存储库ServiceRepository实例</strong>对象serviceRepository</li> |
| <li><strong>应用程序部署器ApplicationDeployer实例</strong>对象deployer</li> |
| <li><strong>所属框架FrameworkModel实例</strong>对象frameworkModel</li> |
| <li><strong>内部的模块模型ModuleModel实例</strong>对象internalModule</li> |
| <li><strong>默认的模块模型ModuleModel实例</strong>对象defaultModule</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ModuleModel</strong> 服务模块的模型</p> |
| <ul> |
| <li><strong>所属应用程序模型ApplicationModel实例</strong>对象applicationModel</li> |
| <li><strong>模块环境信息ModuleEnvironment实例</strong>对象moduleEnvironment</li> |
| <li><strong>模块服务存储库ModuleServiceRepository实例</strong>对象serviceRepository</li> |
| <li><strong>模块的服务配置管理ModuleConfigManager实例</strong>对象moduleConfigManager</li> |
| <li><strong>模块部署器ModuleDeployer实例</strong>对象deployer用于导出和引用服务</li> |
| </ul> |
| </li> |
| </ul> |
| <p>了解了这几个模型对象的关系我们可以了解到这几个模型对象的管理层级从框架到应用程序,然后到模块的管理(FrameworkModel-&gt;ApplicationModel-&gt;ModuleModel),他们主要用来针对框架,应用程序,模块的<strong>存储</strong>,<strong>发布管理,</strong>,<strong>配置管理</strong></p> |
| <p>看来Dubbo3 针对应用服务治理与运维这一块也是在努力尝试.</p> |
| <h3 id="311-abstractmethodconfig-配置对象中获取模型对象的调用">3.1.1 AbstractMethodConfig 配置对象中获取模型对象的调用</h3> |
| <p>模块模型(ModuleModel)参数对象的创建 |
| 这个AbstractMethodConfig构造器在初始化的时候调调用了这么一行代码做为参数向父类型传递对象.</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">().</span>getDefaultModule<span style="color:#719e07">()</span> |
| </span></span></code></pre></div><p>默认情况下使用ApplicationModel的静态方法获取默认的模型对象和默认的模块对象</p> |
| <p><strong>ApplicationModel</strong>(应用程序领域模型)类型中获取默认模型对象的方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> ApplicationModel <span style="color:#268bd2">defaultModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// should get from default FrameworkModel, avoid out of sync |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> FrameworkModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">().</span>defaultApplication<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里可以看到要想获取应用程序模型必须先通过框架领域模型来获取层级也是框架领域模型到应用程序领域模型</p> |
| <h3 id="312-使用双重校验锁获取框架模型对象">3.1.2 使用双重校验锁获取框架模型对象</h3> |
| <p>FrameworkModel(框架模型)的默认模型获取工厂方法defaultModel()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 在源码的注释上有这么一句话:在销毁默认的 FrameworkModel 时, FrameworkModel.defaultModel() |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> *或ApplicationModel.defaultModel() 将返回一个损坏的模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> *可能会导致不可预知的问题。建议:尽量避免使用默认模型。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> FrameworkModel <span style="color:#268bd2">defaultModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁的形式创建单例对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FrameworkModel instance <span style="color:#719e07">=</span> defaultInstance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>globalLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重置默认框架模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> resetDefaultFrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultInstance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> defaultInstance <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> defaultInstance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;Default FrameworkModel is null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="313-刷新重置默认框架模型对象">3.1.3 刷新重置默认框架模型对象</h3> |
| <p>FrameworkModel中的重置默认框架模型resetDefaultFrameworkModel</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">resetDefaultFrameworkModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//全局悲观锁,同一个时刻只能有一个线程执行重置操作 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>globalLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//defaultInstance为当前成员变量FrameworkModel类型代表当前默认的FrameworkModel类型的实例对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultInstance <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>defaultInstance<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> FrameworkModel oldDefaultFrameworkModel <span style="color:#719e07">=</span> defaultInstance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在实例模型列表则直接从内存缓存中查后续不需要创建了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>allInstances<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前存在的有FrameworkModel框架实例多个列表则取第一个为默认的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultInstance <span style="color:#719e07">=</span> allInstances<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> defaultInstance <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>oldDefaultFrameworkModel <span style="color:#719e07">!=</span> defaultInstance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Reset global default framework from &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc<span style="color:#719e07">(</span>oldDefaultFrameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc<span style="color:#719e07">(</span>defaultInstance<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面单例做了很多的初始化操作,这里开始调用构造器来创建框架模型对象,如下代码:</p> |
| <h2 id="32--创建frameworkmodel对象">3.2 创建FrameworkModel对象</h2> |
| <p>FrameworkModel()构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">FrameworkModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用父类型ScopeModel传递参数,这个构造器的第一个参数为空代表这是一个顶层的域模型,第二个代表了这个是框架FRAMEWORK域,第三个false不是内部域 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span><span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> ExtensionScope<span style="color:#719e07">.</span>FRAMEWORK<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内部id用于表示模型树的层次结构,如层次结构: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//FrameworkModel(索引=1)-&gt;ApplicationModel(索引=2)-&gt;ModuleModel(索引=1,第一个用户模块) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这个index变量是static类型的为静态全局变量默认值从1开始,如果有多个框架模型对象则internalId编号从1开始依次递增 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>setInternalId<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>index<span style="color:#719e07">.</span>getAndIncrement<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register FrameworkModel instance early |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将当前新创建的框架实例对象添加到容器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>globalLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将当前框架模型实例添加到所有框架模型缓存对象中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> allInstances<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如上面代码所示重置默认的框架模型对象,这里将会是缓存实例列表的第一个,新增了一个刷新默认实例对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> resetDefaultFrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getDesc<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is created&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化框架模型领域对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionScope.FRAMEWORK</p> |
| <h3 id="321-初始化frameworkmodel">3.2.1 初始化FrameworkModel</h3> |
| <p>FrameworkModel框架模型的初始化方法initialize()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里初始化之前先调用下父类型ScopeModel的初始化方法我们在下面来看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用TypeDefinitionBuilder的静态方法initBuilders来初始化类型构建器TypeBuilder类型集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> TypeDefinitionBuilder<span style="color:#719e07">.</span>initBuilders<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//框架服务存储仓库对象,可以用于快速查询服务提供者信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FrameworkServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取ScopeModelInitializer类型(域模型初始化器)的扩展加载器ExtensionLoader,每个扩展类型都会创建一个扩展加载器缓存起来 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取ScopeModelInitializer类型的支持的扩展集合,这里当前版本存在8个扩展类型实现 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历这些扩展实现调用他们的initializeFrameworkModel方法来传递FrameworkModel类型对象,细节我们待会再详细说下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ScopeModelInitializer initializer <span style="color:#719e07">:</span> initializers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> initializer<span style="color:#719e07">.</span>initializeFrameworkModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个内部的ApplicationModel类型,细节下面说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建ApplicationConfig类型对象同时传递应用程序模型对象internalApplicationModel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取ConfigManager类型对象,然后设置添加当前应用配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">().</span>setApplication<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span>internalApplicationModel<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置公开的模块名字为常量DUBBO_INTERNAL_APPLICATION |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续上面代码的调用链路,我们来看 |
| FrameworkModel的super.initialize();方法 调用父类型ScopeModel的initialize()方法</p> |
| <h3 id="322-初始化scopemodel">3.2.2 初始化ScopeModel</h3> |
| <p>ScopeModel类型的初始化方法initialize():</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化ExtensionDirector是一个作用域扩展加载程序管理器。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//ExtensionDirector支持多个级别,子级可以继承父级的扩展实例。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//查找和创建扩展实例的方法类似于Java classloader。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ExtensionDirector<span style="color:#719e07">(</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent<span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> scope<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个参考了Spring的生命周期回调思想,添加一个扩展初始化的前后调用的处理器,在扩展初始化之前或之后调用的后处理器,参数类型为ExtensionPostProcessor |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector<span style="color:#719e07">.</span>addExtensionPostProcessor<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ScopeModelAwareExtensionProcessor<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个内部共享的域工厂对象,用于注册Bean,创建Bean,获取Bean,初始化Bean等 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>beanFactory <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ScopeBeanFactory<span style="color:#719e07">(</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> extensionDirector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用数据结构链表,创建销毁监听器容器,一般用于关闭进程,重置应用程序对象等操作时候调用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>destroyListeners <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用ConcurrentHashMap属性集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>attributes <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用ConcurrentHashSet存储当前域下的类加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>classLoaders <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashSet<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Add Framework&#39;s ClassLoader by default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将当前类的加载器存入加载器集合classLoaders中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ClassLoader dubboClassLoader <span style="color:#719e07">=</span> ScopeModel<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>dubboClassLoader <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>addClassLoader<span style="color:#719e07">(</span>dubboClassLoader<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="323-初始类型定义构建器">3.2.3 初始类型定义构建器</h3> |
| <p>TypeDefinitionBuilder的初始化类型构造器方法initBuilders</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initBuilders</span><span style="color:#719e07">(</span>FrameworkModel model<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>TypeBuilder<span style="color:#719e07">&gt;</span> tbs <span style="color:#719e07">=</span> model<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>TypeBuilder<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> BUILDERS <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>tbs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3231-服务存储仓库对象的创建">3.2.3.1 服务存储仓库对象的创建</h4> |
| <p>FrameworkServiceRepository对象的初始化</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">FrameworkServiceRepository</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>frameworkModel <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="324--域模型初始化器的获取与初始化回调">3.2.4 域模型初始化器的获取与初始化回调</h3> |
| <p>域模型初始化器的获取与初始化(ScopeModelInitializer类型和initializeFrameworkModel方法) |
| 加载到的ScopeModelInitializer类型的SPI扩展实现</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取ScopeModelInitializer类型的支持的扩展集合,这里当前版本存在8个扩展类型实现 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历这些扩展实现调用他们的initializeFrameworkModel方法来传递FrameworkModel类型对象,细节我们待会再详细说下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ScopeModelInitializer initializer <span style="color:#719e07">:</span> initializers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> initializer<span style="color:#719e07">.</span>initializeFrameworkModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>通过Debug查到域模型初始化器的SPI扩展类型有如下8个:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/3-initextent.png" alt="在这里插入图片描述"></p> |
| <p>这里我随机找两个说一下吧: |
| 容错域模型初始化器:ClusterScopeModelInitializer的initializeFrameworkModel方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ClusterScopeModelInitializer</span> <span style="color:#268bd2">implements</span> ScopeModelInitializer <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initializeFrameworkModel</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ScopeBeanFactory beanFactory <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> beanFactory<span style="color:#719e07">.</span>registerBean<span style="color:#719e07">(</span>RouterSnapshotSwitcher<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">CommonScopeModelInitializer</span> <span style="color:#268bd2">implements</span> ScopeModelInitializer <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initializeFrameworkModel</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ScopeBeanFactory beanFactory <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> beanFactory<span style="color:#719e07">.</span>registerBean<span style="color:#719e07">(</span>FrameworkExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ConfigScopeModelInitializer</span> <span style="color:#268bd2">implements</span> ScopeModelInitializer <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initializeFrameworkModel</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> frameworkModel<span style="color:#719e07">.</span>addDestroyListener<span style="color:#719e07">(</span><span style="color:#719e07">new</span> FrameworkModelCleaner<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="325-将内部应用配置对象创建与添加至应用模型中">3.2.5 将内部应用配置对象创建与添加至应用模型中</h3> |
| <p>创建ApplicationConfig对象让后将其添加至应用模型中 |
| 内部应用程序模型,这里为应用配置管理器设置一个应用配置对象,将这个应用配置的模块名字配置名字设置为DUBBO_INTERNAL_APPLICATION,应用配置记录着我们常见的应用配置信息,如下面表格所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//获取ConfigManager类型对象,然后设置添加当前应用配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">().</span>setApplication<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span>internalApplicationModel<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置公开的模块名字为常量DUBBO_INTERNAL_APPLICATION |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>来自官网目前版本的配置解释: |
| 官网当前的配置描述知道到了元数据类型,后面我再补充几个</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>name</td> |
| <td>application</td> |
| <td>string</td> |
| <td>必填</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样,此参数不是匹配条件,你当前项目叫什么名字就填什么,和提供者消费者角色无关,比如:kylin应用调用了morgan应用的服务,则kylin项目配成kylin,morgan项目配成morgan,可能kylin也提供其它服务给别人使用,但kylin项目永远配成kylin,这样注册中心将显示kylin依赖于morgan</td> |
| <td>1.0.16以上版本</td> |
| </tr> |
| <tr> |
| <td>version</td> |
| <td>application.version</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>当前应用的版本</td> |
| <td>2.2.0以上版本</td> |
| </tr> |
| <tr> |
| <td>owner</td> |
| <td>owner</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>应用负责人,用于服务治理,请填写负责人公司邮箱前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>organization</td> |
| <td>organization</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>组织名称(BU或部门),用于注册中心区分服务来源,此配置项建议不要使用autoconfig,直接写死在配置中,比如china,intl,itu,crm,asc,dw,aliexpress等</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>architecture</td> |
| <td>architecture</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>用于服务分层对应的架构。如,intl、china。不同的架构使用不同的分层。</td> |
| <td>2.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>environment</td> |
| <td>environment</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>应用环境,如:develop/test/product,不同环境使用不同的缺省值,以及作为只用于开发测试功能的限制条件</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>compiler</td> |
| <td>compiler</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>javassist</td> |
| <td>性能优化</td> |
| <td>Java字节码编译器,用于动态类的生成,可选:jdk或javassist</td> |
| <td>2.1.0以上版本</td> |
| </tr> |
| <tr> |
| <td>logger</td> |
| <td>logger</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>slf4j</td> |
| <td>性能优化</td> |
| <td>日志输出方式,可选:slf4j,jcl,log4j,log4j2,jdk</td> |
| <td>2.2.0以上版本</td> |
| </tr> |
| <tr> |
| <td>metadata-type</td> |
| <td>metadata-type</td> |
| <td>String</td> |
| <td>可选</td> |
| <td>local</td> |
| <td>服务治理</td> |
| <td>metadata 传递方式,是以 Provider 视角而言的,Consumer 侧配置无效,可选值有:remote - Provider 把 metadata 放到远端注册中心,Consumer 从注册中心获取local - Provider 把 metadata 放在本地,Consumer 从 Provider 处直接获取</td> |
| <td>2.7.6以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>当前在Dubbo3.0.7中还有一些的配置我下面列举下:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>register-consumer</td> |
| <td>register-consumer</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>是否注册使用者实例,默认为false。</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>register-mode</td> |
| <td>register-mode</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>all</td> |
| <td>服务治理</td> |
| <td>将interface/instance/all 地址注册到注册中心,默认为all。</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>enable-empty-protection</td> |
| <td>enable-empty-protection</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>在空地址通知上启用空保护,默认为true</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td>protocol</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>服务治理</td> |
| <td>此应用程序的首选协议(名称)</td> |
| <td></td> |
| </tr> |
| </tbody> |
| </table> |
| <h2 id="33-创建applicationmodel对象">3.3 创建ApplicationModel对象</h2> |
| <p>ApplicationModel对象的初始化调用 |
| 在前面 3.2.4 FrameworkModel框架模型的初始化方法initialize() 章节中,我们看到了代码ApplicationModel对象的初始化调用如下代码,这里我们来详细说一下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> internalApplicationModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> internalApplicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">().</span>setApplication<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span>internalApplicationModel<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> internalApplicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><h3 id="331-applicationmodel的构造器">3.3.1 ApplicationModel的构造器</h3> |
| <p>ApplicationModel(FrameworkModel frameworkModel, boolean isInternal) |
| 刚刚3.2.9那个地方我们看到了使用代码<strong>new ApplicationModel(this, true)</strong> 来创建对象这里我们详细看下代码细节:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ApplicationModel</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> isInternal<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用父类型ScopeModel传递参数,这个构造器的传递没与前面看到的FrameworkModel构造器的中的调用参数有些不同第一个参数我们为frameworkModel代表父域模型,第二个参数标记域为应用程序级别APPLICATION,第三个参数我们传递的为true代表为内部域 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>frameworkModel<span style="color:#719e07">,</span> ExtensionScope<span style="color:#719e07">.</span>APPLICATION<span style="color:#719e07">,</span> isInternal<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>frameworkModel<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;FrameworkModel can not be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用程序域成员变量记录frameworkModel对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>frameworkModel <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//frameworkModel对象添加当前应用程序域对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> frameworkModel<span style="color:#719e07">.</span>addApplication<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getDesc<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is created&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化应用程序 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3311-将applicationmodel添加至frameworkmodel容器中">3.3.1.1 将ApplicationModel添加至FrameworkModel容器中</h4> |
| <p>FrameworkModel的添加应用程序方法addApplication:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#dc322f">void</span> <span style="color:#268bd2">addApplication</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// can not add new application if it&#39;s destroying |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//检查FrameworkModel对象是否已经被标记为销毁状态,如果已经被销毁了则抛出异常无需执行逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>instLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果还未添加过当前参数传递应用模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModels<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为当前应用模型生成内部id |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationModel<span style="color:#719e07">.</span>setInternalId<span style="color:#719e07">(</span>buildInternalId<span style="color:#719e07">(</span>getInternalId<span style="color:#719e07">(),</span> appIndex<span style="color:#719e07">.</span>getAndIncrement<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//添加到成员变量集合applicationModels中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModels<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果非内部的则也向公开应用模型集合pubApplicationModels中添加一下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>applicationModel<span style="color:#719e07">.</span>isInternal<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>pubApplicationModels<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> resetDefaultAppModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>内部id生成算法buildInternalId方法代码如下: |
| 看代码胜过,文字解释</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> String <span style="color:#268bd2">buildInternalId</span><span style="color:#719e07">(</span>String parentInternalId<span style="color:#719e07">,</span> <span style="color:#dc322f">long</span> childIndex<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// FrameworkModel 1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// ApplicationModel 1.1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// ModuleModel 1.1.1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span>parentInternalId<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> parentInternalId <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.&#34;</span> <span style="color:#719e07">+</span> childIndex<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">+</span> childIndex<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>重置默认的应用模型对象</strong> |
| FrameworkModel 重置默认的应用模型对象 resetDefaultAppModel()方法 |
| 与默认框架模型设置方式类似取集合的第一个,这里应用模型需要使用公开的应用模型的第一个做为默认应用模型,代码如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">resetDefaultAppModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>instLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//取第一个公开的应用模型做为默认应用模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationModel oldDefaultAppModel <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>pubApplicationModels<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel <span style="color:#719e07">=</span> pubApplicationModels<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultInstance <span style="color:#719e07">==</span> <span style="color:#719e07">this</span> <span style="color:#719e07">&amp;&amp;</span> oldDefaultAppModel <span style="color:#719e07">!=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Reset global default application from &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc<span style="color:#719e07">(</span>oldDefaultAppModel<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="332-初始化applicationmodel">3.3.2 初始化ApplicationModel</h3> |
| <p>ApplicationModel的初始化initialize()方法 |
| 在前面<strong>3.2.10 ApplicationModel的构造器ApplicationModel(FrameworkModel frameworkModel, boolean isInternal)</strong> 中的最后一行开始初始化应用模型我们还未详细说明下面可以来看下</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个是调用域模型来初始化基础信息如扩展访问器等,可以参考 3.2.5 ScopeModel类型的初始化方法initialize()章节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个内部的模块模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalModule <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个独立服务存储对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用程序初始化监听器ApplicationInitListener扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ApplicationInitListener<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationInitListener<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果存在应用程序初始化监听器扩展则执行这个初始化方法,在当前的版本还未看到有具体的扩展实现类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> listenerNames <span style="color:#719e07">=</span> extensionLoader<span style="color:#719e07">.</span>getSupportedExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String listenerName <span style="color:#719e07">:</span> listenerNames<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> extensionLoader<span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>listenerName<span style="color:#719e07">).</span>init<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化扩展(这个是应用程序生命周期的方法调用,这里调用初始化方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initApplicationExts<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取域模型初始化器扩展对象列表,然后执行初始化方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ScopeModelInitializer initializer <span style="color:#719e07">:</span> initializers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> initializer<span style="color:#719e07">.</span>initializeApplicationModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="334-initapplicationexts--初始化应用程序扩展方法">3.3.4 initApplicationExts() 初始化应用程序扩展方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initApplicationExts</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个扩展实现一共有两个可以看下面那个图扩展类型为ConfigManager和Environment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ApplicationExt<span style="color:#719e07">&gt;</span> exts <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationExt<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ApplicationExt ext <span style="color:#719e07">:</span> exts<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ext<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/3-extension.png" alt="在这里插入图片描述"></p> |
| <h4 id="3341-configmanager类型的initialize方法">3.3.4.1 ConfigManager类型的initialize方法</h4> |
| <p>先简单说下ConfigManager的作用,无锁配置管理器(通过ConcurrentHashMap),用于快速读取操作。写入操作锁带有配置类型的子配置映射,用于安全检查和添加新配置。 |
| 其实ConfigManager实现类中并没有这个初始化方法initialize,不过ConfigManager的父类型AbstractConfigManager中是有initialize方法的,如下所示:</p> |
| <p>AbstractConfigManager的初始化方法initialize</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//乐观锁判断是否初始化过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从模块环境中获取组合配置,目前Environment中有6种重要的配置,我们后面详细说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> CompositeConfiguration configuration <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">.</span>getModelEnvironment<span style="color:#719e07">().</span>getConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// dubbo.config.mode获取配置模式,配置模式对应枚举类型ConfigMode,目前有这么几个STRICT,OVERRIDE,OVERRIDE_ALL,OVERRIDE_IF_ABSENT,IGNORE,这个配置决定了属性覆盖的顺序,当有同一个配置key多次出现时候,以最新配置为准,还是以最老的那个配置为准,还是配置重复则抛出异常,默认值为严格模式STRICT重复则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String configModeStr <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> configuration<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span>configModeStr<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>configMode <span style="color:#719e07">=</span> ConfigMode<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>configModeStr<span style="color:#719e07">.</span>toUpperCase<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String msg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Illegal &#39;&#34;</span> <span style="color:#719e07">+</span> ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;&#39; config value [&#34;</span> <span style="color:#719e07">+</span> configModeStr <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;], available values &#34;</span> <span style="color:#719e07">+</span> Arrays<span style="color:#719e07">.</span>toString<span style="color:#719e07">(</span>ConfigMode<span style="color:#719e07">.</span>values<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>msg<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span>msg<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// dubbo.config.ignore-duplicated-interface |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//忽略重复的接口(服务/引用)配置。默认值为false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String ignoreDuplicatedInterfaceStr <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> configuration |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ignoreDuplicatedInterfaceStr <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>ignoreDuplicatedInterface <span style="color:#719e07">=</span> Boolean<span style="color:#719e07">.</span>parseBoolean<span style="color:#719e07">(</span>ignoreDuplicatedInterfaceStr<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// print 打印配置信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> map <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE<span style="color:#719e07">,</span> configMode<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>ignoreDuplicatedInterface<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Config settings: &#34;</span> <span style="color:#719e07">+</span> map<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3342-environment类型的initialize方法">3.3.4.2 Environment类型的initialize方法</h4> |
| <p>这是一个与环境配置有关系的类型,我们先来简单了解下它的初始化方法,后期再详细说明:</p> |
| <p>Environment类型的initialize方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//乐观锁判断是否进行过初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//PropertiesConfiguration从系统属性和dubbo.properties中获取配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>propertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> PropertiesConfiguration<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//SystemConfiguration获取的是JVM参数 启动命令中-D指定的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>systemConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SystemConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//EnvironmentConfiguration是从环境变量中获取的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>environmentConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> EnvironmentConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//外部的Global配置config-center global/default config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>externalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;ExternalConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//外部的应用配置如:config-center中的应用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>appExternalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;AppExternalConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地应用配置 , 如Spring Environment/PropertySources/application.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>appConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;AppConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务迁移配置加载 dubbo2升级dubbo3的一些配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadMigrationRule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务迁移配置加载 JVM &gt; env &gt; 代码路径dubbo-migration.yaml |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadMigrationRule</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件路径配置的key dubbo.migration.file |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// JVM参数中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_MIGRATION_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//env环境变量中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_MIGRATION_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//类路径下获取文件dubbo-migration.yaml |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> CommonConstants<span style="color:#719e07">.</span>DEFAULT_DUBBO_MIGRATION_FILE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>localMigrationRule <span style="color:#719e07">=</span> ConfigUtils<span style="color:#719e07">.</span>loadMigrationRule<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">.</span>getClassLoaders<span style="color:#719e07">(),</span> path<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>ConfigUtils中读取迁移规则配置文件loadMigrationRule</strong> |
| 这个我们不细说了,贴一下代码感兴趣可以了解下,这个代码主要是读取文件到内存字符串:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> String <span style="color:#268bd2">loadMigrationRule</span><span style="color:#719e07">(</span>Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders<span style="color:#719e07">,</span> String fileName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String rawRule <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>checkFileNameExist<span style="color:#719e07">(</span>fileName<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>FileInputStream input <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FileInputStream<span style="color:#719e07">(</span>fileName<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> readString<span style="color:#719e07">(</span>input<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoadersToLoad <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>ClassUtils<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> urls <span style="color:#719e07">:</span> ClassLoaderResourceLoader<span style="color:#719e07">.</span>loadResources<span style="color:#719e07">(</span>fileName<span style="color:#719e07">,</span> classLoadersToLoad<span style="color:#719e07">).</span>values<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>URL url <span style="color:#719e07">:</span> urls<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> InputStream is <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>openStream<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>is <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> readString<span style="color:#719e07">(</span>is<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> rawRule<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> String <span style="color:#268bd2">readString</span><span style="color:#719e07">(</span>InputStream is<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> StringBuilder stringBuilder <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> StringBuilder<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">char</span><span style="color:#719e07">[]</span> buffer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> <span style="color:#dc322f">char</span><span style="color:#719e07">[</span>10<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>BufferedReader reader <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> BufferedReader<span style="color:#719e07">(</span><span style="color:#719e07">new</span> InputStreamReader<span style="color:#719e07">(</span>is<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> n<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> <span style="color:#719e07">((</span>n <span style="color:#719e07">=</span> reader<span style="color:#719e07">.</span>read<span style="color:#719e07">(</span>buffer<span style="color:#719e07">))</span> <span style="color:#719e07">!=</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>n <span style="color:#719e07">&lt;</span> 10<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> buffer <span style="color:#719e07">=</span> Arrays<span style="color:#719e07">.</span>copyOf<span style="color:#719e07">(</span>buffer<span style="color:#719e07">,</span> n<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> stringBuilder<span style="color:#719e07">.</span>append<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>buffer<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> buffer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> <span style="color:#dc322f">char</span><span style="color:#719e07">[</span>10<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>IOException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Read migration file error.&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> stringBuilder<span style="color:#719e07">.</span>toString<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * check if the fileName can be found in filesystem |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param fileName |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">checkFileNameExist</span><span style="color:#719e07">(</span>String fileName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> File file <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File<span style="color:#719e07">(</span>fileName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> file<span style="color:#719e07">.</span>exists<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p></p> |
| <h2 id="34-创建modulemodel对象">3.4 创建ModuleModel对象</h2> |
| <p>前面ApplicationModel对象初始化的时候创建了ModuleModel如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>internalModule <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这里我们来看下这个它所对应的构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ModuleModel</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> isInternal<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用ScopeModel传递3个参数父模型,模型域为模块域,是否为内部模型参数为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> ExtensionScope<span style="color:#719e07">.</span>MODULE<span style="color:#719e07">,</span> isInternal<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;ApplicationModel can not be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化成员变量applicationModel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将模块模型添加至应用模型中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationModel<span style="color:#719e07">.</span>addModule<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> isInternal<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getDesc<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is created&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>serviceRepository<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;ModuleServiceRepository can not be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>moduleConfigManager<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;ModuleConfigManager can not be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>assertTrue<span style="color:#719e07">(</span>moduleConfigManager<span style="color:#719e07">.</span>isInitialized<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;ModuleConfigManager can not be initialized&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// notify application check state |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取应用程序发布对象,通知检查状态 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationDeployer applicationDeployer <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>applicationDeployer <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer<span style="color:#719e07">.</span>notifyModuleChanged<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> DeployState<span style="color:#719e07">.</span>PENDING<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="341-将模块模型添加至应用模型中">3.4.1 将模块模型添加至应用模型中</h3> |
| <p>如上面代码所示调用如下代码将模块模型添加到应用模型中:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>addModule<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> isInternal<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这里我们来看下添加过程</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#dc322f">void</span> <span style="color:#268bd2">addModule</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> isInternal<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>moduleLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不存在则添加 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleModels<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查应用模型是否已销毁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//添加至模块模型成员变量中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleModels<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置模块模型内部id,这个内部id生成过程与上面将应用模型添加到框架模型中的方式是一致的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//可以参考 3.3.2 将ApplicationModel添加至FrameworkModel容器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> moduleModel<span style="color:#719e07">.</span>setInternalId<span style="color:#719e07">(</span>buildInternalId<span style="color:#719e07">(</span>getInternalId<span style="color:#719e07">(),</span> moduleIndex<span style="color:#719e07">.</span>getAndIncrement<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果不是内部模型则添加到公开模块模型中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isInternal<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> pubModuleModels<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="342-初始化模块模型">3.4.2 初始化模块模型</h3> |
| <p>前面ModuleModel构造器中通过initialize()方法来进行初始化操作如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用域模型ScopeModel的初始化,可以参考 3.2.5 ScopeModel类型的初始化方法initialize()章节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建模块服务存储库对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建模块配置管理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleConfigManager<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块配置管理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块配置扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initModuleExt<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化域模型扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ScopeModelInitializer initializer <span style="color:#719e07">:</span> initializers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> initializer<span style="color:#719e07">.</span>initializeModuleModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3421-模块服务存储库的创建">3.4.2.1 模块服务存储库的创建</h4> |
| <p>ModuleServiceRepository是模块模型中用来存储服务的通过如下代码调用</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//创建模块服务存储库对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>这里我们来看下模块服务存储库的构造器代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ModuleServiceRepository</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleModel <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> frameworkServiceRepository <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getFrameworkModel<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">).</span>getServiceRepository<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ModuleServiceRepository存储库中使用框架存储库frameworkServiceRepository来间接存储 |
| 这里我们看下怎么通过模块模型获取框架服务存储库frameworkServiceRepository:通过代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ScopeModelUtil<span style="color:#719e07">.</span>getFrameworkModel<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">).</span>getServiceRepository<span style="color:#719e07">()</span> |
| </span></span></code></pre></div><p>ScopeModelUtil工具类获取getFrameworkModel代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> FrameworkModel <span style="color:#268bd2">getFrameworkModel</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> FrameworkModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过成员变量获取(构造器初始化的时候将FrameworkModel赋值给了ApplicationModel的成员变量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">instanceof</span> ApplicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//直接获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">((</span>ApplicationModel<span style="color:#719e07">)</span> scopeModel<span style="color:#719e07">).</span>getFrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">instanceof</span> ModuleModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ModuleModel moduleModel <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ModuleModel<span style="color:#719e07">)</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//间接通过ApplicationModel获取,不越级获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getFrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">instanceof</span> FrameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>FrameworkModel<span style="color:#719e07">)</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unable to get FrameworkModel from &#34;</span> <span style="color:#719e07">+</span> scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3422-模块配置管理器对象的创建与初始化">3.4.2.2 模块配置管理器对象的创建与初始化</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//创建模块配置管理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleConfigManager<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块配置管理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>ModuleConfigManager的构造器代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ModuleConfigManager</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//向抽象的配置管理器AbstractConfigManager传递参数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//模块模型参数,模块支持的配置类型集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">,</span> Arrays<span style="color:#719e07">.</span>asList<span style="color:#719e07">(</span>ModuleConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> ServiceConfigBase<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> ReferenceConfigBase<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> ProviderConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> ConsumerConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用程序配置管理器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationConfigManager <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getApplicationConfigManager<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>ModuleConfigManager类型的初始化方法代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取组合配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> CompositeConfiguration configuration <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">.</span>getModelEnvironment<span style="color:#719e07">().</span>getConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// dubbo.config.mode |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//3.3.4.1提到过这里再重复一次 dubbo.config.mode获取配置模式,配置模式对应枚举类型ConfigMode,目前有这么几个STRICT,OVERRIDE,OVERRIDE_ALL,OVERRIDE_IF_ABSENT,IGNORE,这个配置决定了属性覆盖的顺序,当有同一个配置key多次出现时候,以最新配置为准,还是以最老的那个配置为准,还是配置重复则抛出异常,默认值为严格模式STRICT重复则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String configModeStr <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> configuration<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span>configModeStr<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>configMode <span style="color:#719e07">=</span> ConfigMode<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>configModeStr<span style="color:#719e07">.</span>toUpperCase<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String msg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Illegal &#39;&#34;</span> <span style="color:#719e07">+</span> ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;&#39; config value [&#34;</span> <span style="color:#719e07">+</span> configModeStr <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;], available values &#34;</span> <span style="color:#719e07">+</span> Arrays<span style="color:#719e07">.</span>toString<span style="color:#719e07">(</span>ConfigMode<span style="color:#719e07">.</span>values<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>msg<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span>msg<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// dubbo.config.ignore-duplicated-interface |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//忽略重复的接口(服务/引用)配置。默认值为false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String ignoreDuplicatedInterfaceStr <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> configuration |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ignoreDuplicatedInterfaceStr <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>ignoreDuplicatedInterface <span style="color:#719e07">=</span> Boolean<span style="color:#719e07">.</span>parseBoolean<span style="color:#719e07">(</span>ignoreDuplicatedInterfaceStr<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// print 打印配置信息到控制台 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> map <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE<span style="color:#719e07">,</span> configMode<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>ignoreDuplicatedInterface<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Config settings: &#34;</span> <span style="color:#719e07">+</span> map<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3423-模块配置扩展的初始化">3.4.2.3 模块配置扩展的初始化</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>initModuleExt<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initModuleExt</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//目前这里的扩展只支持有一个类型ModuleEnvironment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ModuleExt<span style="color:#719e07">&gt;</span> exts <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ModuleExt<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ModuleExt ext <span style="color:#719e07">:</span> exts<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ext<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ModuleEnvironment的初始化</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>orderedPropertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> OrderedPropertiesConfiguration<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>OrderedPropertiesConfiguration对象的创建</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">OrderedPropertiesConfiguration</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleModel <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refresh</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//有序的配置提供器扩展获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>OrderedPropertiesProvider<span style="color:#719e07">&gt;</span> propertiesProviderExtensionLoader <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>OrderedPropertiesProvider<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> propertiesProviderNames <span style="color:#719e07">=</span> propertiesProviderExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>propertiesProviderNames<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>OrderedPropertiesProvider<span style="color:#719e07">&gt;</span> orderedPropertiesProviders <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String propertiesProviderName <span style="color:#719e07">:</span> propertiesProviderNames<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> orderedPropertiesProviders<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>propertiesProviderExtensionLoader<span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>propertiesProviderName<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//order the propertiesProvider according the priority descending |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//根据优先级进行排序,值越小优先级越高 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> orderedPropertiesProviders<span style="color:#719e07">.</span>sort<span style="color:#719e07">((</span>a<span style="color:#719e07">,</span> b<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> b<span style="color:#719e07">.</span>priority<span style="color:#719e07">()</span> <span style="color:#719e07">-</span> a<span style="color:#719e07">.</span>priority<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//override the properties. 目前没看到有具体的扩展实现 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>OrderedPropertiesProvider orderedPropertiesProvider <span style="color:#719e07">:</span> orderedPropertiesProviders<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>putAll<span style="color:#719e07">(</span>orderedPropertiesProvider<span style="color:#719e07">.</span>initProperties<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/3-kuang-jia-ying-yong-cheng-xu-mo-kuai-ling-yu-mo-xing-model-dui-xiang-de-chu-shi-hua/">&laquo;框架,应用程序,模块领域模型Model对象的初始化&raquo;</a></p></description></item><item><title>Blog: 02-启动服务前服务配置ServiceConfig类型是如何初始化的?</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/02/02-%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1%E5%89%8D%E6%9C%8D%E5%8A%A1%E9%85%8D%E7%BD%AEserviceconfig%E7%B1%BB%E5%9E%8B%E6%98%AF%E5%A6%82%E4%BD%95%E5%88%9D%E5%A7%8B%E5%8C%96%E7%9A%84/</link><pubDate>Tue, 02 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/02/02-%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1%E5%89%8D%E6%9C%8D%E5%8A%A1%E9%85%8D%E7%BD%AEserviceconfig%E7%B1%BB%E5%9E%8B%E6%98%AF%E5%A6%82%E4%BD%95%E5%88%9D%E5%A7%8B%E5%8C%96%E7%9A%84/</guid><description> |
| <h1 id="2-启动服务前服务配置serviceconfig类型是如何初始化的">2-启动服务前服务配置ServiceConfig类型是如何初始化的?</h1> |
| <h2 id="21-示例源码回顾">2.1 示例源码回顾:</h2> |
| <p>为了方便我们理解记忆,这里先来回顾下上一章我们说的示例代码,如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">Application</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DemoServiceImpl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面这几行代码虽然看似简单,仅仅几行的启动,但是完全掌握也得下一翻大功夫,接下来我们重点看启动代码中的第一行,创建一个服务配置对象:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span></code></pre></div><h2 id="22-了解一下服务配置的建模">2.2 了解一下服务配置的建模</h2> |
| <p>下面是一个简单的UML继承关系图,当然这个图很是简单的,这里仅仅列出了当前服务提供者的相关服务配置继承关系, 服务提供者独有的配置标注颜色为蓝色,一些可能与服务引用配置所共有的父类型我们用红色背景,当然这里为了简便起见不会提起服务引用相关的配置类型,这里列举了如下服务提供者类型,他们各司其职: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/2-ServiceConfig.png" alt="在这里插入图片描述"></p> |
| <center>图2.1 服务引用类继承关系UML</center> |
| <ul> |
| <li>AbstractConfig |
| <ul> |
| <li><strong>抽象的配置类型</strong>,也是最顶层的服务配置类型,封装着解析配置的实用方法和公共方法,比如服务id的设置,服务标签名字的处理,服务参数的添加,属性的提取等等</li> |
| </ul> |
| </li> |
| <li>AbstractMethodConfig |
| <ul> |
| <li><strong>抽象的方法配置</strong>,同样这个类型也是见名知意,服务方法的相关配置处理,这个类型主要用于对服务方法的一些配置信息建模比如服务方法的调用超时时间,重试次数,最大并发调用数,负载均衡策略,是否异步调用,是否确认异步发送等等配置信息.</li> |
| </ul> |
| </li> |
| <li>AbstractInterfaceConfig |
| <ul> |
| <li><strong>抽象的接口配置</strong>,与前面介绍的方法配置类似,这个类型是对服务接口的建模,主要的配置信息有暴漏服务的接口名字,服务接口的版本号,客户/提供方将引用的远程服务分组,<strong>服务元数据</strong>,服务接口的本地impl类名,服务监控配置,对于生成动态代理的策略,可以选择两种策略:jdk和javassist,容错类型等等配置</li> |
| </ul> |
| </li> |
| <li>AbstractServiceConfig |
| - <strong>抽象的服务配置</strong>,这个就与我们的服务提供者有了具体的关系了,主要记录了一些服务提供者的公共配置,如服务版本,服务分组,服务延迟注册毫秒数,是否暴漏服务,服务权重,是否为动态服务,服务协议类型,是否注册等等.</li> |
| <li>ServiceConfigBase |
| - <strong>服务的基础配置类</strong>,这个类型仍旧是个抽象的类型提取了一些基础的配置:导出服务的接口类,服务名称,接口实现的引用类型,提供者配置,是否是通用服务GenericService</li> |
| <li>ServiceConfig |
| - <strong>服务配置实现类</strong>, 上面的类型都是抽象类型不能做为具体存在的事物,这个类型是我们出现的第一个服务配置实现类型,服务配置实现类已经从父类型中继承了这么多的属性,这里主要为实现服务提供了一些配置如服务的协议配置,服务的代理工厂JavassistProxyFactory是将生成导出服务代理的ProxyFactory实现,是其默认实现,服务提供者模型,是否导出服务,导出的服务列表,服务监听器等等.</li> |
| <li>ServiceBean |
| - <strong>服务工厂Bean</strong> ,这个主要是Spring模块来简化配置的一个服务工厂Bean这里就先不详细介绍Spring相关的配置. -</li> |
| </ul> |
| <h2 id="23-serviceconfig构造器的初始化调用链">2.3 ServiceConfig构造器的初始化调用链</h2> |
| <p>有了上面的类型继承关系我们就比较好分析了,接下来我们开始创建服务配置对象如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span></code></pre></div><p>根据Java基础的构造器知识,在每个构造器的第一行都会有个super方法来调用父类的构造器,当前这个super方法我们可以不写但是Java编译器底层还是会为我们默认加上这么一行super()代码来调用父类构造器的.</p> |
| <p>对于上面我提到的这几个构造器<strong>根据代码被调用的先后顺序</strong>,这里重点说几个重要的,这里我仍旧按代码执行的先后顺序来说:</p> |
| <h3 id="231-父类型abstractmethodconfig构造器的初始化">2.3.1 父类型AbstractMethodConfig构造器的初始化</h3> |
| <p>根据super调用链这里先来看AbstractMethodConfig抽象方法配置</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractMethodConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">().</span>getDefaultModule<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在这个构造器中只有个super方法用来调用父类型的构造器,但是在调用之前会先使用代码 <strong>ApplicationModel.defaultModel().getDefaultModule()</strong> 创建一个模块模型对象<strong>ModuleModel</strong> |
| 关于模型对象的细节我们会在下个章节来说,这里我们继续来看调用链</p> |
| <h3 id="232-最顶层类型abstractconfig构造器的初始化">2.3.2 最顶层类型AbstractConfig构造器的初始化</h3> |
| <p><strong>AbstractConfig</strong>的构造器初始化一共有两个,第一个步骤就是创建一个应用程序模型对象<strong>ApplicationModel</strong>,刚刚我们在<strong>AbstractMethodConfig</strong>的构造器中了解到使用这个代码<strong>ApplicationModel.defaultModel().getDefaultModule()<strong>创建了个模块模型对象</strong>ModuleModel</strong>,具体他们细节我们下一章细说,了解了子类型<strong>AbstractMethodConfig</strong>的构造器是带参数的那我们就直接看第二个构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">(</span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>将会调用第二个构造器初始化域模型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractConfig</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>当前类型设置ScopeModel类型对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setScopeModel</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次初始化的当前成员变量是空的可以设置变量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel <span style="color:#719e07">!=</span> scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查参数是否合法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ScopeModel oldScopeModel <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// reinitialize spi extension and change referenced config&#39;s scope model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//被子类重写的方法,根据多态会调用具体子类型的这个方法我们下面来看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//子类应该重写此方法以初始化其SPI扩展并更改引用的配置的范围模型。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>postProcessAfterScopeModelChanged<span style="color:#719e07">(</span>oldScopeModel<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>检查ScopeModel参数是否合法,合法的参数是不能为空并且必须是ApplicationModel类型或者子类型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">checkScopeModel</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;scopeModel cannot be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!(</span>scopeModel <span style="color:#719e07">instanceof</span> ApplicationModel<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Invalid scope model, expect to be a ApplicationModel but got: &#34;</span> <span style="color:#719e07">+</span> scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="2321-重写的postprocessafterscopemodelchanged调用逻辑">2.3.2.1 重写的postProcessAfterScopeModelChanged调用逻辑</h4> |
| <p>当ScopeModel模型对象发生了改变,上面调用了postProcessAfterScopeModelChanged方法来通知模型对象改变的时候要执行的操作,根据多态重写的逻辑我们从实现类的postProcessAfterScopeModelChanged来看,在下面的调用链路中部分父类型并未实现postProcessAfterScopeModelChanged方法我们就直接忽略了</p> |
| <p>第一个被调用到的是<strong>ServiceConfig</strong>类型的postProcessAfterScopeModelChanged方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">postProcessAfterScopeModelChanged</span><span style="color:#719e07">(</span>ScopeModel oldScopeModel<span style="color:#719e07">,</span> ScopeModel newScopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>postProcessAfterScopeModelChanged<span style="color:#719e07">(</span>oldScopeModel<span style="color:#719e07">,</span> newScopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化当前协议对象,通过扩展机制获取协议Protocol类型的对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> protocolSPI <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Protocol<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化当前代理工厂对象,通过扩展机制获取ProxyFactory类型的对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> proxyFactory <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ProxyFactory<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>第二个被调用到的方法为<strong>ServiceConfigBase</strong>的postProcessAfterScopeModelChanged方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">postProcessAfterScopeModelChanged</span><span style="color:#719e07">(</span>ScopeModel oldScopeModel<span style="color:#719e07">,</span> ScopeModel newScopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>postProcessAfterScopeModelChanged<span style="color:#719e07">(</span>oldScopeModel<span style="color:#719e07">,</span> newScopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当服务提供者配置对象不为空时候为服务提供者对象设置域模型,这里服务提供者对象仍旧为空,这个一般用在兼容Dubbo低版本 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>provider <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>provider<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>provider<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>第三个被调用到的是<strong>AbstractInterfaceConfig</strong>类型的postProcessAfterScopeModelChanged方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">postProcessAfterScopeModelChanged</span><span style="color:#719e07">(</span>ScopeModel oldScopeModel<span style="color:#719e07">,</span> ScopeModel newScopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>postProcessAfterScopeModelChanged<span style="color:#719e07">(</span>oldScopeModel<span style="color:#719e07">,</span> newScopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// remove this config from old ConfigManager |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// if (oldScopeModel != null &amp;&amp; oldScopeModel instanceof ModuleModel) { |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// ((ModuleModel)oldScopeModel).getConfigManager().removeConfig(this); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// } |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// change referenced config&#39;s scope model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取应用程序模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationModel applicationModel <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为配置中心对象设置ApplicationModel类型对象(当前阶段配置中心配置对象为空) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>configCenter <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>configCenter<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>configCenter<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为元数据配置对象设置ApplicationModel类型对象(当前阶段数据配置配置对象为空) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为MonitorConfig服务监控配置对象设置ApplicationModel类型对象(当前阶段数据配置配置对象为空) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>monitor <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>monitor<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>monitor<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个if判断和上面的上面是重复的估计是写代码人加班加的太久了,没注意看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果注册中心配置列表不为空则为每个注册中心配置设置一个ApplicationModel类型对象(当前注册中心对象都为空) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>registries<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>registries<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>registryConfig <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>最后被调用到的是最顶层父类型<strong>AbstractConfig</strong>的postProcessAfterScopeModelChanged方法 |
| 这个方法什么也没干只是在父类型创建的模版方法让子类型来重写用的</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">postProcessAfterScopeModelChanged</span><span style="color:#719e07">(</span>ScopeModel oldScopeModel<span style="color:#719e07">,</span> ScopeModel newScopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// remove this config from old ConfigManager |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// if (oldScopeModel != null &amp;&amp; oldScopeModel instanceof ApplicationModel) { |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// ((ApplicationModel)oldScopeModel).getApplicationConfigManager().removeConfig(this); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// } |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="233-serviceconfigbase构造器的初始化">2.3.3 ServiceConfigBase构造器的初始化</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ServiceConfigBase</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务元数据对象创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceMetadata<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为服务元数据对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>addAttribute<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;ORIGIN_CONFIG&#34;</span><span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>注意,</strong> <strong>ServiceMetadata</strong>这个类目前在Dubbo中没有用法。与服务级别相关的数据,例如名称、版本、业务服务的类加载器、安全信息等,还带有用于扩展的AttributeMap。</p> |
| <p><strong>服务配置对象的创建过程就这样结束了</strong>,当然有一些细节会<strong>放到后面来写</strong> |
| 上面主要顺序是按照代码执行的顺序来写的部分地方可能稍微做了调整,如果有条件的同学一定要<strong>自己进行DEBUG</strong>了解下细节.</p> |
| <p>关于服务配置官网提供了xml的配置信息这里我拷贝过来,可以做为参考: |
| 当然这个配置不是最新的比如服务配置的<strong>标签配置tag</strong>, |
| <strong>warmup 预热时间</strong>单位毫秒,暂时还没有说明</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>interface</td> |
| <td></td> |
| <td>class</td> |
| <td>必填</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务接口名</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>ref</td> |
| <td></td> |
| <td>object</td> |
| <td>必填</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务对象实现引用</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>version</td> |
| <td>version</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>0.0.0</td> |
| <td>服务发现</td> |
| <td>服务版本,建议使用两位数字版本,如:1.0,通常在接口不兼容时版本号才需要升级</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>group</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务分组,当一个接口有多个实现,可以用分组区分</td> |
| <td>1.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>path</td> |
| <td><code>&lt;path&gt;</code></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省为接口名</td> |
| <td>服务发现</td> |
| <td>服务路径 (注意:1.0不支持自定义路径,总是使用接口名,如果有1.0调2.0,配置服务路径可能不兼容)</td> |
| <td>1.0.12以上版本</td> |
| </tr> |
| <tr> |
| <td>delay</td> |
| <td>delay</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>延迟注册服务时间(毫秒) ,设为-1时,表示延迟到Spring容器初始化完成时暴露服务</td> |
| <td>1.0.14以上版本</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>timeout</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>1000</td> |
| <td>性能调优</td> |
| <td>远程服务调用超时时间(毫秒)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>retries</td> |
| <td>retries</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>2</td> |
| <td>性能调优</td> |
| <td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>connections</td> |
| <td>connections</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>100</td> |
| <td>性能调优</td> |
| <td>对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>loadbalance</td> |
| <td>loadbalance</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>random</td> |
| <td>性能调优</td> |
| <td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>async</td> |
| <td>async</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>性能调优</td> |
| <td>是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>local</td> |
| <td>local</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>设为true,表示使用缺省代理类名,即:接口名 + Local后缀,已废弃,请使用stub</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>stub</td> |
| <td>stub</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>设为true,表示使用缺省代理类名,即:接口名 + Stub后缀,服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceStub(XxxService xxxService)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>mock</td> |
| <td>mock</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>设为true,表示使用缺省Mock类名,即:接口名 + Mock后缀,服务接口调用失败Mock实现类,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>token</td> |
| <td>token</td> |
| <td>string/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>令牌验证,为空表示不开启,如果为true,表示随机生成动态令牌,否则使用静态令牌,令牌的作用是防止消费者绕过注册中心直接访问,保证注册中心的授权功能有效,如果使用点对点调用,需关闭令牌功能</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>registry</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省向所有registry注册</td> |
| <td>配置关联</td> |
| <td>向指定注册中心注册,在多个注册中心时使用,值为<a href="dubbo:registry">dubbo:registry</a>的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>provider</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省使用第一个provider配置</td> |
| <td>配置关联</td> |
| <td>指定provider,值为<a href="dubbo:provider">dubbo:provider</a>的id属性</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>deprecated</td> |
| <td>deprecated</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>服务是否过时,如果设为true,消费方引用时将打印服务过时警告error日志</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>dynamic</td> |
| <td>dynamic</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。 2.0.5以上版本</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>accesslog</td> |
| <td>accesslog</td> |
| <td>string/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>owner</td> |
| <td>owner</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务负责人,用于服务治理,请填写负责人公司邮箱前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>document</td> |
| <td>document</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务文档URL</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>weight</td> |
| <td>weight</td> |
| <td>int</td> |
| <td>可选</td> |
| <td></td> |
| <td>性能调优</td> |
| <td>服务权重</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>executes</td> |
| <td>executes</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>服务提供者每服务每方法最大可并行执行请求数</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>proxy</td> |
| <td>proxy</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>javassist</td> |
| <td>性能调优</td> |
| <td>生成动态代理方式,可选:jdk/javassist</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>cluster</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>failover</td> |
| <td>性能调优</td> |
| <td>集群方式,可选:failover/failfast/failsafe/failback/forking</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>filter</td> |
| <td>service.filter</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>default</td> |
| <td>性能调优</td> |
| <td>服务提供方远程调用过程拦截器名称,多个名称用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>listener</td> |
| <td>exporter.listener</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>default</td> |
| <td>性能调优</td> |
| <td>服务提供方导出服务监听器名称,多个名称用逗号分隔</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>配置关联</td> |
| <td>使用指定的协议暴露服务,在多协议时使用,值为<a href="dubbo:protocol">dubbo:protocol</a>的id属性,多个协议ID用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>layer</td> |
| <td>layer</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>服务治理</td> |
| <td>服务提供者所在的分层。如:biz、dao、intl:web、china:acton。</td> |
| <td>2.0.7以上版本</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>register</td> |
| <td>register</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>该协议的服务是否注册到注册中心</td> |
| <td>2.0.8以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/2-qi-dong-fu-wu-qian-fu-wu-pei-zhi-serviceconfig-lei-xing-shi-ru-he-chu-shi-hua-de/">&laquo;ServiceConfig对象的建模&raquo;</a></p></description></item><item><title>Blog: 01 从一个服务提供者的Demo说起</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/01/01-%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</link><pubDate>Mon, 01 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/01/01-%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</guid><description> |
| <h1 id="1-从一个服务提供者的demo说起">1 从一个服务提供者的Demo说起</h1> |
| <p>为了更方便了解原理,我们先来编写一个Demo,从例子中来看源码实现:</p> |
| <h2 id="11-启动zookeeper">1.1 启动Zookeeper</h2> |
| <p>为了Demo可以正常启动,需要我们先在本地启动一个Zookeeper如下图所示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/1-zookeeper.png" alt="在这里插入图片描述"></p> |
| <h2 id="12-服务提供者">1.2 服务提供者</h2> |
| <p>接下来给大家贴一下示例源码,这个源码来源于Dubbo源码目录的 dubbo-demo/dubbo-demo-api 目录下面的dubbo-demo-api-provider子项目,这里我做了删减,方便看核心代码: |
| 首先我们定义一个服务接口如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">import</span> java.util.concurrent.CompletableFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">DemoService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 同步处理的服务方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 用于异步处理的服务方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture<span style="color:#719e07">.</span>completedFuture<span style="color:#719e07">(</span>sayHello<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>服务实现类如下: |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.rpc.RpcContext<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.Logger<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.LoggerFactory<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> java.util.concurrent.CompletableFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceImpl</span> <span style="color:#268bd2">implements</span> DemoService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> Logger logger <span style="color:#719e07">=</span> LoggerFactory<span style="color:#719e07">.</span>getLogger<span style="color:#719e07">(</span>DemoServiceImpl<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, request from consumer: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getServiceContext<span style="color:#719e07">().</span>getRemoteAddress<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, response from provider: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getServiceContext<span style="color:#719e07">().</span>getLocalAddress<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="13-启用服务">1.3 启用服务</h2> |
| <p>有了服务接口之后我们来启用服务,启用服务的源码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.common.constants.CommonConstants<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ApplicationConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.MetadataReportConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ProtocolConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.RegistryConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ServiceConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.bootstrap.DubboBootstrap<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.demo.DemoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">Application</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DemoServiceImpl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="14-启用服务后写入zookeeper的节点数据">1.4 启用服务后写入Zookeeper的节点数据</h2> |
| <p>启动服务,这个时候我们打开Zookeeper图形化客户端来看看这个服务在Zookeeper上面写入来哪些数据,如下图: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/1-zookeeper-data.png" alt="在这里插入图片描述"> |
| 写入Zookeper上的节点用于服务在分布式场景下的协调,这些节点是比较重要的。</p> |
| <p>如果了解过Dubbo的同学,应该会知道Dubbo在低版本的时候会向注册中心中写入服务接口,具体路径在上面的 <strong>dubbo目录下</strong> ,然后在 <strong>/dubbo/服务接口/</strong> 路径下写入如下信息:</p> |
| <ul> |
| <li><strong>服务提供者</strong>配置信息URL形式</li> |
| <li><strong>服务消费者</strong>的配置信息URL形式</li> |
| <li>服务<strong>路由信息</strong></li> |
| <li><strong>配置信息</strong></li> |
| </ul> |
| <p>上面这个图就是Dubbo3的注册信息了,后面我们也会围绕细节来说明下,这里可以看下新增了:</p> |
| <ul> |
| <li>/dubbo/metadata <strong>元数据信息</strong></li> |
| <li>/dubbo/mapping 服务和应用的<strong>映射信息</strong></li> |
| <li>/dubbo/config <strong>注册中心配置</strong></li> |
| <li>/services目录<strong>应用信息</strong></li> |
| </ul> |
| <p>在这里可以大致了解下,在后面会有更详细的源码解析这个示例代码.通过透析代码来看透Dubbo3服务注册原理,服务提供原理。</p> |
| <p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">&laquo;从一个服务提供者的Demo说起&raquo;</a></p></description></item><item><title>Blog: Rest 协议</title><link>https://dubbo.apache.org/zh-cn/blog/2022/07/26/rest-%E5%8D%8F%E8%AE%AE/</link><pubDate>Tue, 26 Jul 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/07/26/rest-%E5%8D%8F%E8%AE%AE/</guid><description> |
| <h1 id="dubborestprotocol设计文档">Dubbo RestProtocol 设计文档</h1> |
| <h2 id="原版本dubborest">原版本dubbo rest</h2> |
| <p>consumer</p> |
| <p>restClient支持 依赖resteasy 不支持spring mvc </p> |
| <p>provider(较重)</p> |
| <p>依赖web container (tomcat,jetty,)servlet 模式,jaxrs netty server</p> |
| <h3 id="改版dubborest">改版dubbo rest </h3> |
| <p>方向:</p> |
| <p>更加轻量,具有dubbo风格的rest,微服务体系互通(Springcloud Alibaba)</p> |
| <p>1.注解解析</p> |
| <p>2.报文编解码</p> |
| <p>3.restClient</p> |
| <p>4.restServer(netty)</p> |
| <p>支持程度:</p> |
| <p>content-type text json xml form(后续会扩展)</p> |
| <p>注解</p> |
| <p>param,header,body,pathvariable (spring mvc &amp; resteasy)</p> |
| <h2 id="http协议报文">Http 协议报文</h2> |
| <pre><code>POST /test/path? HTTP/1.1 |
| Host: localhost:8080 |
| Connection: keep-alive |
| Content-type: application/json |
| {&quot;name&quot;:&quot;dubbo&quot;,&quot;age&quot;:10,&quot;address&quot;:&quot;hangzhou&quot;} |
| </code></pre> |
| <h3 id="dubbohttpheader">dubbo http(header)</h3> |
| <pre><code>// service key header |
| path: com.demo.TestInterface |
| group: demo |
| port: 80 |
| version: 1.0.0 |
| // 保证长连接 |
| Keep-Alive,Connection: keep-alive |
| Keep-alive: 60 |
| // RPCContext Attachment |
| userId: 123456 |
| </code></pre> |
| <h2 id="目前支持粒度">目前支持粒度:</h2> |
| <table> |
| <thead> |
| <tr> |
| <th>数据位置</th> |
| <th>content-type</th> |
| <th>spring注解</th> |
| <th>resteasy注解</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>body</td> |
| <td>无要求</td> |
| <td>ReuqestBody</td> |
| <td> 无注解即为body</td> |
| </tr> |
| <tr> |
| <td>querystring(?test=demo)</td> |
| <td>无要求</td> |
| <td>RequestParam</td> |
| <td>QueryParam</td> |
| </tr> |
| <tr> |
| <td>header</td> |
| <td>无要求</td> |
| <td>RequestHeader</td> |
| <td>PathParam</td> |
| </tr> |
| <tr> |
| <td>form</td> |
| <td>application/x-www-form-urlencoded</td> |
| <td>RequestParam ReuqestBody</td> |
| <td>FormParam</td> |
| </tr> |
| <tr> |
| <td>path</td> |
| <td>无要求</td> |
| <td>PathVariable</td> |
| <td>PathParam</td> |
| </tr> |
| <tr> |
| <td>method</td> |
| <td>无要求</td> |
| <td>PostMapping GetMapping</td> |
| <td>GET POST</td> |
| </tr> |
| <tr> |
| <td>url</td> |
| <td></td> |
| <td>PostMapping GetMapping path属性</td> |
| <td>Path</td> |
| </tr> |
| <tr> |
| <td>content-type</td> |
| <td></td> |
| <td>PostMapping GetMapping consumers属性</td> |
| <td>Consumers</td> |
| </tr> |
| <tr> |
| <td>Accept</td> |
| <td></td> |
| <td>PostMapping GetMapping produces属性</td> |
| <td>Produces</td> |
| </tr> |
| </tbody> |
| </table> |
| <h2 id="rest注解解析servicerestmetadataresolver">rest注解解析(ServiceRestMetadataResolver)</h2> |
| <pre><code>JAXRSServiceRestMetadataResolver |
| SpringMvcServiceRestMetadataResolver |
| </code></pre> |
| <p>ServiceRestMetadata</p> |
| <pre><code>public class ServiceRestMetadata implements Serializable { |
| private String serviceInterface; // com.demo.TestInterface |
| private String version;// 1.0.0 |
| private String group;// demo |
| private Set&lt;RestMethodMetadata&gt; meta;// method 元信息 |
| private int port;// 端口 for provider service key |
| private boolean consumer;// consumer 标志 |
| /** |
| * make a distinction between mvc &amp; resteasy |
| */ |
| private Class codeStyle;// |
| /** |
| * for provider |
| */ |
| private Map&lt;PathMatcher, RestMethodMetadata&gt; pathToServiceMap; |
| /** |
| * for consumer |
| */ |
| private Map&lt;String, Map&lt;ParameterTypesComparator, RestMethodMetadata&gt;&gt; methodToServiceMa |
| </code></pre> |
| <p>RestMethodMetadata</p> |
| <pre><code>public class RestMethodMetadata implements Serializable { |
| private MethodDefinition method; // method 定义信息(name ,pramType,returnType) |
| private RequestMetadata request;// 请求元信息 |
| private Integer urlIndex; |
| private Integer bodyIndex; |
| private Integer headerMapIndex; |
| private String bodyType; |
| private Map&lt;Integer, Collection&lt;String&gt;&gt; indexToName; |
| private List&lt;String&gt; formParams; |
| private Map&lt;Integer, Boolean&gt; indexToEncoded; |
| private ServiceRestMetadata serviceRestMetadata; |
| private List&lt;ArgInfo&gt; argInfos; |
| private Method reflectMethod; |
| /** |
| * make a distinction between mvc &amp; resteasy |
| */ |
| private Class codeStyle; |
| </code></pre> |
| <p>ArgInfo</p> |
| <pre><code>public class ArgInfo { |
| /** |
| * method arg index 0,1,2,3 |
| */ |
| private int index; |
| /** |
| * method annotation name or name |
| */ |
| private String annotationNameAttribute; |
| /** |
| * param annotation type |
| */ |
| private Class paramAnnotationType; |
| /** |
| * param Type |
| */ |
| private Class paramType; |
| /** |
| * param name |
| */ |
| private String paramName; |
| /** |
| * url split(&quot;/&quot;) String[n] index |
| */ |
| private int urlSplitIndex; |
| private Object defaultValue; |
| private boolean formContentType; |
| </code></pre> |
| <p>RequestMeatadata</p> |
| <pre><code>public class RequestMetadata implements Serializable { |
| private static final long serialVersionUID = -240099840085329958L; |
| private String method;// 请求method |
| private String path;// 请求url |
| private Map&lt;String, List&lt;String&gt;&gt; params // param参数?拼接 |
| private Map&lt;String, List&lt;String&gt;&gt; headers// header; |
| private Set&lt;String&gt; consumes // content-type; |
| private Set&lt;String&gt; produces // Accept; |
| </code></pre> |
| <h3 id="consumer代码">Consumer 代码:</h3> |
| <p>refer:</p> |
| <pre><code> @Override |
| protected &lt;T&gt; Invoker&lt;T&gt; protocolBindingRefer(final Class&lt;T&gt; type, final URL url) throws RpcException { |
| // restClient spi创建 |
| ReferenceCountedClient&lt;? extends RestClient&gt; refClient = |
| clients.computeIfAbsent(url.getAddress(), key -&gt; createReferenceCountedClient(url, clients)); |
| refClient.retain(); |
| // resolve metadata |
| Map&lt;String, Map&lt;ParameterTypesComparator, RestMethodMetadata&gt;&gt; metadataMap = MetadataResolver.resolveConsumerServiceMetadata(type, url); |
| ReferenceCountedClient&lt;? extends RestClient&gt; finalRefClient = refClient; |
| Invoker&lt;T&gt; invoker = new AbstractInvoker&lt;T&gt;(type, url, new String[]{INTERFACE_KEY, GROUP_KEY, TOKEN_KEY}) { |
| @Override |
| protected Result doInvoke(Invocation invocation) { |
| try { |
| // 获取 method的元信息 |
| RestMethodMetadata restMethodMetadata = metadataMap.get(invocation.getMethodName()).get(ParameterTypesComparator.getInstance(invocation.getParameterTypes())); |
| RequestTemplate requestTemplate = new RequestTemplate(invocation, restMethodMetadata.getRequest().getMethod(), url.getAddress(), getContextPath(url)); |
| HttpConnectionCreateContext httpConnectionCreateContext = new HttpConnectionCreateContext(); |
| // TODO dynamic load config |
| httpConnectionCreateContext.setConnectionConfig(new HttpConnectionConfig()); |
| httpConnectionCreateContext.setRequestTemplate(requestTemplate); |
| httpConnectionCreateContext.setRestMethodMetadata(restMethodMetadata); |
| httpConnectionCreateContext.setInvocation(invocation); |
| httpConnectionCreateContext.setUrl(url); |
| // http 信息构建拦截器 |
| for (HttpConnectionPreBuildIntercept intercept : httpConnectionPreBuildIntercepts) { |
| intercept.intercept(httpConnectionCreateContext); |
| } |
| CompletableFuture&lt;RestResult&gt; future = finalRefClient.getClient().send(requestTemplate); |
| CompletableFuture&lt;AppResponse&gt; responseFuture = new CompletableFuture&lt;&gt;(); |
| AsyncRpcResult asyncRpcResult = new AsyncRpcResult(responseFuture, invocation); |
| // response 处理 |
| future.whenComplete((r, t) -&gt; { |
| if (t != null) { |
| responseFuture.completeExceptionally(t); |
| } else { |
| AppResponse appResponse = new AppResponse(); |
| try { |
| int responseCode = r.getResponseCode(); |
| MediaType mediaType = MediaType.TEXT_PLAIN; |
| if (400 &lt; responseCode &amp;&amp; responseCode &lt; 500) { |
| throw new HttpClientException(r.getMessage()); |
| } else if (responseCode &gt;= 500) { |
| throw new RemoteServerInternalException(r.getMessage()); |
| } else if (responseCode &lt; 400) { |
| mediaType = MediaTypeUtil.convertMediaType(r.getContentType()); |
| } |
| Object value = HttpMessageCodecManager.httpMessageDecode(r.getBody(), |
| restMethodMetadata.getReflectMethod().getReturnType(), mediaType); |
| appResponse.setValue(value); |
| Map&lt;String, String&gt; headers = r.headers() |
| .entrySet() |
| .stream() |
| .collect(Collectors.toMap(Map.Entry::getKey, e -&gt; e.getValue().get(0))); |
| appResponse.setAttachments(headers); |
| responseFuture.complete(appResponse); |
| } catch (Exception e) { |
| responseFuture.completeExceptionally(e); |
| } |
| } |
| }); |
| return asyncRpcResult; |
| } catch (RpcException e) { |
| if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) { |
| e.setCode(getErrorCode(e.getCause())); |
| } |
| throw e; |
| } |
| } |
| @Override |
| public void destroy() { |
| super.destroy(); |
| invokers.remove(this); |
| destroyInternal(url); |
| } |
| }; |
| invokers.add(invoker); |
| return invoker; |
| </code></pre> |
| <h3 id="provider代码">provider 代码:</h3> |
| <p>export:</p> |
| <pre><code> public &lt;T&gt; Exporter&lt;T&gt; export(final Invoker&lt;T&gt; invoker) throws RpcException { |
| URL url = invoker.getUrl(); |
| final String uri = serviceKey(url); |
| Exporter&lt;T&gt; exporter = (Exporter&lt;T&gt;) exporterMap.get(uri); |
| if (exporter != null) { |
| // When modifying the configuration through override, you need to re-expose the newly modified service. |
| if (Objects.equals(exporter.getInvoker().getUrl(), invoker.getUrl())) { |
| return exporter; |
| } |
| } |
| // TODO addAll metadataMap to RPCInvocationBuilder metadataMap |
| Map&lt;PathMatcher, RestMethodMetadata&gt; metadataMap = MetadataResolver.resolveProviderServiceMetadata(url.getServiceModel().getProxyObject().getClass(),url); |
| PathAndInvokerMapper.addPathAndInvoker(metadataMap, invoker); |
| final Runnable runnable = doExport(proxyFactory.getProxy(invoker, true), invoker.getInterface(), invoker.getUrl()); |
| exporter = new AbstractExporter&lt;T&gt;(invoker) { |
| @Override |
| public void afterUnExport() { |
| exporterMap.remove(uri); |
| if (runnable != null) { |
| try { |
| runnable.run(); |
| } catch (Throwable t) { |
| logger.warn(PROTOCOL_UNSUPPORTED, &quot;&quot;, &quot;&quot;, t.getMessage(), t); |
| } |
| } |
| } |
| }; |
| exporterMap.put(uri, exporter); |
| return exporter; |
| } |
| </code></pre> |
| <p>RestHandler</p> |
| <pre><code> private class RestHandler implements HttpHandler&lt;HttpServletRequest, HttpServletResponse&gt; { |
| @Override |
| public void handle(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException { |
| // 有servlet reuqest 和nettyRequest |
| RequestFacade request = RequestFacadeFactory.createRequestFacade(servletRequest); |
| RpcContext.getServiceContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort()); |
| // dispatcher.service(request, servletResponse); |
| Pair&lt;RpcInvocation, Invoker&gt; build = null; |
| try { |
| // 根据请求信息创建 RPCInvocation |
| build = RPCInvocationBuilder.build(request, servletRequest, servletResponse); |
| } catch (PathNoFoundException e) { |
| servletResponse.setStatus(404); |
| } |
| Invoker invoker = build.getSecond(); |
| Result invoke = invoker.invoke(build.getFirst()); |
| // TODO handling exceptions |
| if (invoke.hasException()) { |
| servletResponse.setStatus(500); |
| } else { |
| try { |
| Object value = invoke.getValue(); |
| String accept = request.getHeader(RestConstant.ACCEPT); |
| MediaType mediaType = MediaTypeUtil.convertMediaType(accept); |
| // TODO write response |
| HttpMessageCodecManager.httpMessageEncode(servletResponse.getOutputStream(), value, invoker.getUrl(), mediaType); |
| servletResponse.setStatus(200); |
| } catch (Exception e) { |
| servletResponse.setStatus(500); |
| } |
| } |
| // TODO add Attachment header |
| } |
| } |
| </code></pre> |
| <p>RPCInvocationBuilder</p> |
| <pre><code>{ |
| private static final ParamParserManager paramParser = new ParamParserManager(); |
| public static Pair&lt;RpcInvocation, Invoker&gt; build(RequestFacade request, Object servletRequest, Object servletResponse) { |
| // 获取invoker |
| Pair&lt;Invoker, RestMethodMetadata&gt; invokerRestMethodMetadataPair = getRestMethodMetadata(request); |
| RpcInvocation rpcInvocation = createBaseRpcInvocation(request, invokerRestMethodMetadataPair.getSecond()); |
| ProviderParseContext parseContext = createParseContext(request, servletRequest, servletResponse, invokerRestMethodMetadataPair.getSecond()); |
| // 参数构建 |
| Object[] args = paramParser.providerParamParse(parseContext); |
| rpcInvocation.setArguments(args); |
| return Pair.make(rpcInvocation, invokerRestMethodMetadataPair.getFirst()); |
| } |
| private static ProviderParseContext createParseContext(RequestFacade request, Object servletRequest, Object servletResponse, RestMethodMetadata restMethodMetadata) { |
| ProviderParseContext parseContext = new ProviderParseContext(request); |
| parseContext.setResponse(servletResponse); |
| parseContext.setRequest(servletRequest); |
| Object[] objects = new Object[restMethodMetadata.getArgInfos().size()]; |
| parseContext.setArgs(Arrays.asList(objects)); |
| parseContext.setArgInfos(restMethodMetadata.getArgInfos()); |
| return parseContext; |
| } |
| private static RpcInvocation createBaseRpcInvocation(RequestFacade request, RestMethodMetadata restMethodMetadata) { |
| RpcInvocation rpcInvocation = new RpcInvocation(); |
| int localPort = request.getLocalPort(); |
| String localAddr = request.getLocalAddr(); |
| int remotePort = request.getRemotePort(); |
| String remoteAddr = request.getRemoteAddr(); |
| String HOST = request.getHeader(RestConstant.HOST); |
| String GROUP = request.getHeader(RestConstant.GROUP); |
| String PATH = request.getHeader(RestConstant.PATH); |
| String VERSION = request.getHeader(RestConstant.VERSION); |
| String METHOD = restMethodMetadata.getMethod().getName(); |
| String[] PARAMETER_TYPES_DESC = restMethodMetadata.getMethod().getParameterTypes(); |
| rpcInvocation.setParameterTypes(restMethodMetadata.getReflectMethod().getParameterTypes()); |
| rpcInvocation.setMethodName(METHOD); |
| rpcInvocation.setAttachment(RestConstant.GROUP, GROUP); |
| rpcInvocation.setAttachment(RestConstant.METHOD, METHOD); |
| rpcInvocation.setAttachment(RestConstant.PARAMETER_TYPES_DESC, PARAMETER_TYPES_DESC); |
| rpcInvocation.setAttachment(RestConstant.PATH, PATH); |
| rpcInvocation.setAttachment(RestConstant.VERSION, VERSION); |
| rpcInvocation.setAttachment(RestConstant.HOST, HOST); |
| rpcInvocation.setAttachment(RestConstant.REMOTE_ADDR, remoteAddr); |
| rpcInvocation.setAttachment(RestConstant.LOCAL_ADDR, localAddr); |
| rpcInvocation.setAttachment(RestConstant.REMOTE_PORT, remotePort); |
| rpcInvocation.setAttachment(RestConstant.LOCAL_PORT, localPort); |
| Enumeration&lt;String&gt; attachments = request.getHeaders(RestConstant.DUBBO_ATTACHMENT_HEADER); |
| while (attachments != null &amp;&amp; attachments.hasMoreElements()) { |
| String s = attachments.nextElement(); |
| String[] split = s.split(&quot;=&quot;); |
| rpcInvocation.setAttachment(split[0], split[1]); |
| } |
| // TODO set path,version,group and so on |
| return rpcInvocation; |
| } |
| private static Pair&lt;Invoker, RestMethodMetadata&gt; getRestMethodMetadata(RequestFacade request) { |
| String path = request.getRequestURI(); |
| String version = request.getHeader(RestConstant.VERSION); |
| String group = request.getHeader(RestConstant.GROUP); |
| int port = request.getIntHeader(RestConstant.REST_PORT); |
| return PathAndInvokerMapper.getRestMethodMetadata(path, version, group, port); |
| } |
| } |
| </code></pre> |
| <h2 id="编码示例">编码示例</h2> |
| <p>API</p> |
| <p>mvc:</p> |
| <pre><code>@RestController() |
| @RequestMapping(&quot;/demoService&quot;) |
| public interface DemoService { |
| @RequestMapping(value = &quot;/hello&quot;, method = RequestMethod.GET) |
| Integer hello(@RequestParam Integer a, @RequestParam Integer b); |
| @RequestMapping(value = &quot;/error&quot;, method = RequestMethod.GET) |
| String error(); |
| @RequestMapping(value = &quot;/say&quot;, method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) |
| String sayHello(@RequestBody String name); |
| } |
| </code></pre> |
| <p>resteasy:</p> |
| <pre><code>@Path(&quot;/demoService&quot;) |
| public interface RestDemoService { |
| @GET |
| @Path(&quot;/hello&quot;) |
| Integer hello(@QueryParam(&quot;a&quot;)Integer a,@QueryParam(&quot;b&quot;) Integer b); |
| @GET |
| @Path(&quot;/error&quot;) |
| String error(); |
| @POST |
| @Path(&quot;/say&quot;) |
| @Consumes({MediaType.TEXT_PLAIN}) |
| String sayHello(String name); |
| boolean isCalled(); |
| } |
| </code></pre> |
| <p>impl(service)</p> |
| <pre><code>@DubboService() |
| public class RestDemoServiceImpl implements RestDemoService { |
| private static Map&lt;String, Object&gt; context; |
| private boolean called; |
| @Override |
| public String sayHello(String name) { |
| called = true; |
| return &quot;Hello, &quot; + name; |
| } |
| public boolean isCalled() { |
| return called; |
| } |
| @Override |
| public Integer hello(Integer a, Integer b) { |
| context = RpcContext.getServerAttachment().getObjectAttachments(); |
| return a + b; |
| } |
| @Override |
| public String error() { |
| throw new RuntimeException(); |
| } |
| public static Map&lt;String, Object&gt; getAttachments() { |
| return context; |
| } |
| } |
| </code></pre> |
| <h2 id="流程图">流程图</h2> |
| <p><strong>Consumer</strong> </p> |
| <p><img src="https://static.dingtalk.com/media/lQLPJxLOtqTxs9TNA5rNBQCwci8F2QYiGAYD5sSyd4BVAA_1280_922.png" alt="image"></p> |
| <p><strong>Provider(RestServer)</strong></p> |
| <p><img src="https://static.dingtalk.com/media/lQLPJxZcNUm4M9TNA1_NBMuwZUu6IC3FeYAD5sSydYADAA_1227_863.png" alt="image"></p> |
| <h2 id="场景">场景 :</h2> |
| <p><strong>非dubbo体系互通(Springcloud alibaba 互通)</strong></p> |
| <p>互通条件:</p> |
| <table> |
| <thead> |
| <tr> |
| <th></th> |
| <th>协议</th> |
| <th>Dubbo</th> |
| <th>SpringCloud Alibaba</th> |
| <th>互通</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>通信协议</td> |
| <td>rest</td> |
| <td>spring web/resteasy 编码风格</td> |
| <td>集成feignclient,ribbon (spring web 编码风格)</td> |
| <td>是</td> |
| </tr> |
| <tr> |
| <td></td> |
| <td>triple</td> |
| <td></td> |
| <td></td> |
| <td></td> |
| </tr> |
| <tr> |
| <td></td> |
| <td>dubbo</td> |
| <td></td> |
| <td></td> |
| <td></td> |
| </tr> |
| <tr> |
| <td></td> |
| <td>grpc</td> |
| <td></td> |
| <td></td> |
| <td></td> |
| </tr> |
| <tr> |
| <td></td> |
| <td>hessian</td> |
| <td></td> |
| <td></td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>注册中心</td> |
| <td>zookeeper</td> |
| <td></td> |
| <td></td> |
| <td></td> |
| </tr> |
| <tr> |
| <td></td> |
| <td>nacos</td> |
| <td>支持</td> |
| <td>支持</td> |
| <td>应用级别注册</td> |
| </tr> |
| </tbody> |
| </table> |
| <h3 id="2dubbo双注册">2.dubbo 双注册 </h3> |
| <p> 完成应用级别注册,(dubo2-dubbo3 过度),dubbo版本升级</p> |
| <p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/LvBPlNAjAmw3OdG8/img/0ceca951-f467-4ab3-9b71-8e7d52e5e7d1.png" alt="image"></p> |
| <p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/LvBPlNAjAmw3OdG8/img/6bcc7aed-1d22-470f-b185-efbab32df1e5.png" alt="image"></p> |
| <h3 id="3多协议发布">3.多协议发布</h3> |
| <p>配置:</p> |
| <pre><code>&lt;dubbo:service interface=&quot;org.apache.dubbo.samples.DemoService&quot; protocol=&quot;dubbo, grpc,rest&quot;/&gt; |
| </code></pre> |
| <h3 id="4跨语言">4.跨语言</h3> |
| <p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/LvBPlNAjAmw3OdG8/img/1bdf8f91-9666-4c20-9aea-8396c745f554.png" alt="image"></p> |
| <h3 id="5多协议交互">5.多协议交互</h3> |
| <p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/LvBPlNAjAmw3OdG8/img/af72e3df-05d5-42a2-a333-618be7ec6cb8.png" alt="image"></p> |
| <h3 id="6协议迁移">6.协议迁移</h3> |
| <p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/LvBPlNAjAmw3OdG8/img/36d30183-8d5f-494c-8ebb-b57403c88661.png" alt="image"></p> |
| <p>rest编码风格</p> |
| <p>Http协议更通用跨语言调用</p> |
| <p>dubbo rest 对其他http服务 进行调用</p> |
| <p>其他httpclient 对dubbo rest进行调用</p> |
| <p>dubbo restServer 可以与其他web服务,浏览器等客户端直接进行http交互</p> |
| <h2 id="consumertodolist功能已经初步实现可以调通解析response">consumer TODOLIST(功能已经初步实现,可以调通解析response)</h2> |
| <p>1. org/apache/dubbo/rpc/protocol/rest/RestProtocol.java:157 dynamic load config</p> |
| <p>2.org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java:50 load config HttpClientConfig</p> |
| <p>3.org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java:52 support Dubbo style service</p> |
| <p>4.org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java:120 TODO config</p> |
| <p>5.org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java:140 TODO close judge</p> |
| <p>6.org/apache/dubbo/rpc/protocol/rest/message/decode/MultiValueCodec.java:35 TODO java bean get set convert</p> |
| <h2 id="providertodolist待实现">provider TODOLIST(待实现)</h2> |
| <p>基于netty实现支持http协议的NettyServer</p> |
| <p>无注解协议定义</p> |
| <p>官网场景补充</p> |
| <h2 id="rest使用说明文档及demo">Rest使用说明文档及demo:</h2></description></item><item><title>Blog: 浅析 Dubbo 3.0 中接口级地址推送性能的优化</title><link>https://dubbo.apache.org/zh-cn/blog/2022/06/23/%E6%B5%85%E6%9E%90-dubbo-3.0-%E4%B8%AD%E6%8E%A5%E5%8F%A3%E7%BA%A7%E5%9C%B0%E5%9D%80%E6%8E%A8%E9%80%81%E6%80%A7%E8%83%BD%E7%9A%84%E4%BC%98%E5%8C%96/</link><pubDate>Thu, 23 Jun 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/06/23/%E6%B5%85%E6%9E%90-dubbo-3.0-%E4%B8%AD%E6%8E%A5%E5%8F%A3%E7%BA%A7%E5%9C%B0%E5%9D%80%E6%8E%A8%E9%80%81%E6%80%A7%E8%83%BD%E7%9A%84%E4%BC%98%E5%8C%96/</guid><description> |
| <h2 id="url-简介">URL 简介</h2> |
| <p>在阐述地址推送性能的具体优化之前,我们有必要先了解一下与之息息相关的内容 &mdash; URL。</p> |
| <h3 id="定义">定义</h3> |
| <p>在不谈及 dubbo 时,我们大多数人对 URL 这个概念并不会感到陌生。统一资源定位器 (<a href="https://www.ietf.org/rfc/rfc1738.txt">RFC1738</a>――Uniform Resource Locators (URL))应该是最广为人知的一个 RFC 规范,它的定义也非常简单。</p> |
| <blockquote> |
| <p>因特网上的可用资源可以用简单字符串来表示,该文档就是描述了这种字符串的语法和语 义。而这些字符串则被称为:“统一资源定位器”(URL)</p> |
| </blockquote> |
| <p><strong>一个标准的 URL 格式</strong>至多可以包含如下的几个部分</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>protocol://username:password@host:port/path?key=value&amp;key=value |
| </span></span></code></pre></div><p><strong>一些典型 URL</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>http://www.facebook.com/friends?param1=value1&amp;amp;param2=value2 |
| </span></span><span style="display:flex;"><span>https://username:password@10.20.130.230:8080/list?version=1.0.0 |
| </span></span><span style="display:flex;"><span>ftp://username:password@192.168.1.7:21/1/read.txt |
| </span></span></code></pre></div><p>当然,也有一些<strong>不太符合常规的 URL</strong>,也被归类到了 URL 之中</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>192.168.1.3:20880 |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = 192.168.1.3, port = 20880, url path = null |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:///home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = null, url path = home/user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file://home/user1/router.js?type=script&lt;br&gt; |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = home, url path = user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:///D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = null, url path = D:/1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:/D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span>同上 file:///D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>/home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = null, url path = home/user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = home, url path = user1/router.js |
| </span></span></code></pre></div><h3 id="dubbo-中的-url">Dubbo 中的 URL</h3> |
| <p>在 dubbo 中,也使用了类似的 URL,主要用于在各个扩展点之间传递数据,组成此 URL 对象的具体参数如下:</p> |
| <ul> |
| <li> |
| <p>protocol:一般是 dubbo 中的各种协议 如:dubbo thrift http zk</p> |
| </li> |
| <li> |
| <p>username/password:用户名/密码</p> |
| </li> |
| <li> |
| <p>host/port:主机/端口</p> |
| </li> |
| <li> |
| <p>path:接口名称</p> |
| </li> |
| <li> |
| <p>parameters:参数键值对</p> |
| <p><strong>一些典型的 Dubbo URL</strong></p> |
| </li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000 |
| </span></span><span style="display:flex;"><span>描述一个 dubbo 协议的服务 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.RegistryService&amp;pid=1214&amp;qos.port=33333&amp;timestamp=1545721981946 |
| </span></span><span style="display:flex;"><span>描述一个 zookeeper 注册中心 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>consumer://30.5.120.217/org.apache.dubbo.demo.DemoService?application=demo-consumer&amp;category=consumers&amp;check=false&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=1209&amp;qos.port=33333&amp;side=consumer&amp;timestamp=1545721827784 |
| </span></span><span style="display:flex;"><span>描述一个消费者 |
| </span></span></code></pre></div><p>可以说,任意的一个领域中的一个实现都可以认为是一类 URL,dubbo 使用 URL 来统一描述了元数据,配置信息,贯穿在整个框架之中。</p> |
| <h2 id="dubbo-27">Dubbo 2.7</h2> |
| <h3 id="url-结构">URL 结构</h3> |
| <p>在 Dubbo 2.7 中,URL 的结构非常简单,一个类就涵盖了所有内容,如下图所示。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-1.png" alt="Dubbo2 URL类图.png"></p> |
| <h3 id="地址推送模型">地址推送模型</h3> |
| <p>接下来我们再来看看 Dubbo 2.7 中的地址推送模型方案,主要性能问题由下列过程引起。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-2.png" alt="Dubbo2 地址推送模型.png"></p> |
| <p>上图中主要的流程为 |
| 1、用户新增/删除DemoService的某个具体Provider实例(常见于扩容缩容、网络波动等原因) |
| 2、ZooKeeper将DemoService下所有实例推送给Consumer端 |
| 3、Consumer端根据Zookeeper推送的数据重新全量生成URL |
| 根据该方案可以看出在Provider实例数量较小时,Consumer端的影响比较小,但当某个接口有大量Provider实例时,便会有大量不必要的URL创建过程。 |
| 而Dubbo 3.0中则主要针对上述推送流程进行了一系列的优化,接下来我们便对其进行具体的讲解。</p> |
| <h2 id="dubbo-30">Dubbo 3.0</h2> |
| <h3 id="url-结构-1">URL 结构</h3> |
| <p>当然,地址推送模型的优化依然离不开 URL 的优化,下图是Dubbo 3.0中优化地址推送模型的过程中使用的新的URL结构。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-3.png" alt="Dubbo3 URL类图.png"></p> |
| <p>根据上图我们可以看出,在 Dubbo 2.7 的 URL 中的几个重要属性在 Dubbo 3.0 中已经不存在了,取而代之的是 URLAddress 和 URLParam 两个类。原来的 parameters 属性被移动到了 URLParam 中的 params,其他的属性则移动到了 URLAddress 及其子类中。 |
| 再来介绍 URL 新增的 3 个子类,其中 InstanceAddressURL 属于应用级接口地址,本篇章中不做介绍。 |
| 而 ServiceConfigURL 及 ServiceAddressURL 主要的差别就是,ServiceConfigURL 是程序读取配置文件时生成的 URL。而 ServiceAddressURL 则是注册中心推送一些信息(如 providers)过来时生成的 URL。 |
| 在这里我们顺便提一下为什么会有 DubboServiceAddressURL 这个子类,按照目前的结构来看,ServiceAddressURL 只有这一个子类,所以完全可以将他们两个的属性全都放到 ServiceAddressURL 中,那么为什么还要有这个子类呢?其实是 Dubbo 3.0 为了兼容 HSF 框架所设计的,抽象出了一个 ServiceAddressURL,而 HSF 框架则可以继承这个类,使用 HSFServiceAddressURL,当然,这个类目前没有体现出来,所以此处我们简单一提,不过多讲解。 |
| 那么,我们接下来就讨论一下 Dubbo 3.0 为什么要改为此种数据结构,并且该结构和地址推送模型的优化有何关联性吧!</p> |
| <h3 id="地址推送模型的优化">地址推送模型的优化</h3> |
| <h4 id="url-结构上的优化">URL 结构上的优化</h4> |
| <p>我们在上小节中的类图里看到虽然原来的属性都被移到了 URLAddress 和 URLParam 里,但是 URL 的子类依然多了几个属性,这几个属性自然也是为了优化而新增的,那么这里就讲讲这几个属性的作用。 |
| <strong>ServiceConfigURL</strong>:这个子类中新增了 attribute 这个属性,这个属性主要是针对 URLParam 的 params 做了冗余,仅仅只是将 value 的类型从 String 改为了 Object,减少了代码中每次获取 parameters 的格式转换消耗。 |
| <strong>ServiceAddressURL</strong>:这个子类及其对应的其他子类中则新增了 overrideURL 和 consumerURL 属性。其中 consumerURL 是针对 consumer 端的配置信息,overrideURL 则是在 Dubbo Admin 上进行动态配置时写入的值,当我们调用 URL 的 getParameter() 方法时,优先级为 <code>overrideURL &gt; consumerURL &gt; urlParam</code>。在 Dubbo 2.7 时,动态配置属性会替换 URL 中的属性,及当你有大量 URL 时消耗也是不可忽视的,而此处的 overrideURL 则避免了这种消耗,因为所有 URL 都会共同使用同一个对象。</p> |
| <h4 id="多级缓存">多级缓存</h4> |
| <p>缓存是 Dubbo 3.0 在 URL 上做的优化的重点,同时这部分也是直接针对地址推送模型所做的优化,那么接下来我们就开始来介绍一下多级缓存的具体实现。 |
| 首先,多级缓存主要体现在 CacheableFailbackRegistry 这个类之中,它直接继承于 FailbackRegistry,以 Zookeeper 为例,我们看看 Dubbo 2.7 和 Dubbo 3.0 继承结构的区别。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-4.png" alt="Dubbo3 CacheableFailbackRegistry缓存.png"></p> |
| <p>可以看到在 CacheableFailbackRegistry 缓存中,我们新增了 3 个缓存属性 <code>stringAddress</code>,<code>stringParam</code> 和 <code>stringUrls</code>。我们通过下图来描述这 3 个缓存的具体使用场景。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-5.png" alt="多级缓存.png"></p> |
| <p>在该方案下,我们使用了 3 个纬度的缓存数据(URL 字符串缓存、URL 地址缓存、URL 参数缓存),这样一来,在大部分情况下都能有效利用到缓存中的数据,减少了 Zookeeper 重复通知的消耗。</p> |
| <h4 id="延迟通知">延迟通知</h4> |
| <p>除了上面提到的优化之外,其实另外还有两个小小的优化。 |
| 第一个是解析 URL 时可以直接使用编码后的 URL 字符串字节进行解析,而在 Dubbo 2.7 中,所有编码后的 URL 字符串都需要经过解码才可以正常解析为 URL 对象。该方式也直接减少了 URL 解码过程的开销。 |
| 第二个则是 URL 变更后的通知机制增加了延迟,下图以Zookeeper为例讲解了实现细节。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-6.png" alt="延迟通知.png"></p> |
| <p>在该方案中,当 Consumer 接收 Zookeeper 的变更通知后会主动休眠一段时间,而这段时间内的变更在休眠结束后只会保留最后一次变更,Consumer 便会使用最后一次变更来进行监听实例的更新,以此方法来减少大量 URL 的创建开销。</p> |
| <h4 id="字符串重用">字符串重用</h4> |
| <p>在旧版本实现中,不同的 URL 中属性相同的字符串会存储在堆内不同的地址中,如 protocol、path 等,当有大量 provider 的情况下,Consumer 端的堆内会存在大量的重复字符串,导致内存利用率低下,所以此处提供了另一个优化方式,即字符串重用。 |
| 而它的实现方式也非常的简单,让我们来看看对应的代码片段。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">URLItemCache</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> PATH_CACHE <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LRUCache<span style="color:#719e07">&lt;&gt;(</span>10000<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> PROTOCOL_CACHE <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 省略无关代码片段 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> String <span style="color:#268bd2">checkProtocol</span><span style="color:#719e07">(</span>String _protocol<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>_protocol <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> _protocol<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> String cachedProtocol <span style="color:#719e07">=</span> PROTOCOL_CACHE<span style="color:#719e07">.</span>putIfAbsent<span style="color:#719e07">(</span>_protocol<span style="color:#719e07">,</span> _protocol<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedProtocol <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cachedProtocol<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> _protocol<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> String <span style="color:#268bd2">checkPath</span><span style="color:#719e07">(</span>String _path<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>_path <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> _path<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> String cachedPath <span style="color:#719e07">=</span> PATH_CACHE<span style="color:#719e07">.</span>putIfAbsent<span style="color:#719e07">(</span>_path<span style="color:#719e07">,</span> _path<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedPath <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cachedPath<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> _path<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>由如上代码片段可以得知,字符串重用即为简单地使用了 Map 来存储对应的缓存值,当你使用了相同的字符串时,便会从 Map 中获取早已存在的对象返回给调用方,由此便可以减少堆内存中重复的字符串数以达到优化的效果。</p> |
| <h3 id="优化结果">优化结果</h3> |
| <p>这里优化结果我引用了<a href="https://zhuanlan.zhihu.com/p/345626851">《Dubbo 3.0 前瞻:服务发现支持百万集群,带来可伸缩微服务架构》</a>这篇文章中的两副图来说明,下图模拟了在<strong>220万</strong>个 Provider 接口的情况下,接口数据不断变更导致的 Consumer 端的消耗,我们看到整个 Consumer 端几乎被 Full GC 占满了,严重影响了性能。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-7.png" alt="Dubbo2 接口级地址模型.png"></p> |
| <p>那么我们再来看看 Dubbo 3.0 中对 URL 进行优化后同一个环境下的压测结果,如下图所示。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-8.png" alt="Dubbo3 接口级地址模型.png"></p> |
| <p>我们明显可以看到 Full GC 的频率减少到了只有 3 次,大大提升了性能。当然,该文章中还有其他方面的对比,此处便不一一引用了,感兴趣的读者可以自行去阅读该文章。</p></description></item><item><title>Blog: 如何通过 Apache ShenYu 网关代理 Dubbo 服务</title><link>https://dubbo.apache.org/zh-cn/blog/2022/05/04/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-apache-shenyu-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</link><pubDate>Wed, 04 May 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/05/04/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-apache-shenyu-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</guid><description> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/ApacheShenYu-Dubbo-zh.png" alt="img"></p> |
| <h2 id="1-介绍">1. 介绍</h2> |
| <ul> |
| <li>Apache ShenYu</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/shenyu.png" alt="img"></p> |
| <p><a href="https://shenyu.apache.org/zh/docs/index">Apache ShenYu(Incubating)</a> 是一个异步的,高性能的,跨语言的,响应式的 <code>API</code> 网关。兼容各种主流框架体系,支持热插拔,用户可以定制化开发,满足用户各种场景的现状和未来需求,经历过大规模场景的锤炼。</p> |
| <p>2021年5月,<code>ShenYu</code>捐献给 <code>Apache</code> 软件基金会,Apache 基金会全票通过,顺利进入孵化器。</p> |
| <ul> |
| <li>Apache Dubbo</li> |
| </ul> |
| <p><code>Apache Dubbo</code> 是一款微服务开发框架,它提供了 <code>RPC</code> 通信 与 微服务治理 两大关键能力。这意味着,使用 <code>Dubbo</code> 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 <code>Dubbo</code> 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。</p> |
| <h2 id="2-dubbo快速开始">2. Dubbo快速开始</h2> |
| <p>本小节介绍如何将<code>Dubbo</code>服务接入到<code>ShenYu</code>网关,您可以直接在工程下找到本小节的<a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">示例代码</a> 。</p> |
| <h3 id="21-启动shenyu-admin">2.1 启动shenyu-admin</h3> |
| <p><code>shenyu-admin</code>是<code>Apache ShenYu</code>后台管理系统, 启动的方式有多种,本文通过 <code>[本地部署](https://shenyu.apache.org/zh/docs/deployment/deployment-local)</code> 的方式启动。启动成功后,需要在基础配置<code>-&gt;</code>插件管理中,把<code>dubbo</code> 插件设置为开启,并设置你的注册地址,请确保注册中心已经开启。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-enable-zh.png" alt="img"></p> |
| <h3 id="22-启动shenyu网关">2.2 启动shenyu网关</h3> |
| <p>在这里通过 <a href="https://github.com/apache/incubator-shenyu/tree/master/shenyu-bootstrap">源码</a> 的方式启动,直接运行<code>shenyu-bootstrap</code>中的<code>ShenyuBootstrapApplication</code>。</p> |
| <p>在启动前,请确保网关已经引入相关依赖。如果客户端是<code>apache dubbo</code>,注册中心使用<code>zookeeper</code>,请参考如下配置:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#719e07">&lt;!--</span> apache shenyu apache dubbo plugin start<span style="color:#719e07">--&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>shenyu<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>shenyu<span style="color:#719e07">-</span>spring<span style="color:#719e07">-</span>boot<span style="color:#719e07">-</span>starter<span style="color:#719e07">-</span>plugin<span style="color:#719e07">-</span>apache<span style="color:#719e07">-</span>dubbo<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>$<span style="color:#719e07">{</span>project<span style="color:#719e07">.</span>version<span style="color:#719e07">}&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>dubbo<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>2<span style="color:#719e07">.</span>7<span style="color:#719e07">.</span>5<span style="color:#719e07">&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;!--</span> Dubbo zookeeper registry dependency start <span style="color:#719e07">--&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>curator<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>curator<span style="color:#719e07">-</span>client<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>4<span style="color:#719e07">.</span>0<span style="color:#719e07">.</span>1<span style="color:#719e07">&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>exclusions<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>exclusion<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>log4j<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>log4j<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>exclusion<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>exclusions<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>curator<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>curator<span style="color:#719e07">-</span>framework<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>4<span style="color:#719e07">.</span>0<span style="color:#719e07">.</span>1<span style="color:#719e07">&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>groupId<span style="color:#719e07">&gt;</span>org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>curator<span style="color:#719e07">&lt;/</span>groupId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>artifactId<span style="color:#719e07">&gt;</span>curator<span style="color:#719e07">-</span>recipes<span style="color:#719e07">&lt;/</span>artifactId<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;</span>version<span style="color:#719e07">&gt;</span>4<span style="color:#719e07">.</span>0<span style="color:#719e07">.</span>1<span style="color:#719e07">&lt;/</span>version<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;/</span>dependency<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;!--</span> Dubbo zookeeper registry dependency end <span style="color:#719e07">--&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&lt;!--</span> apache dubbo plugin end<span style="color:#719e07">--&gt;</span> |
| </span></span></code></pre></div><h3 id="23-启动shenyu-examples-dubbo">2.3 启动shenyu-examples-dubbo</h3> |
| <p>以官网提供的例子为例 <a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">shenyu-examples-dubbo</a> 。 假如<code>dubbo</code>服务定义如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;beans</span> /* ...... * <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:application</span> name=<span style="color:#2aa198">&#34;test-dubbo-service&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;${dubbo.registry.address}&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:protocol</span> name=<span style="color:#2aa198">&#34;dubbo&#34;</span> port=<span style="color:#2aa198">&#34;20888&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:service</span> timeout=<span style="color:#2aa198">&#34;10000&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.shenyu.examples.dubbo.api.service.DubboTestService&#34;</span> ref=<span style="color:#2aa198">&#34;dubboTestService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/beans&gt;</span> |
| </span></span></code></pre></div><p>声明应用服务名称,注册中心地址,使用<code>dubbo</code>协议,声明服务接口,对应接口实现类:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * DubboTestServiceImpl. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Service</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubboTestService&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DubboTestServiceImpl</span> <span style="color:#268bd2">implements</span> DubboTestService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@ShenyuDubboClient</span><span style="color:#719e07">(</span>path <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;/findById&#34;</span><span style="color:#719e07">,</span> desc <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Query by Id&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboTest <span style="color:#268bd2">findById</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> String id<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> DubboTest<span style="color:#719e07">(</span>id<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;hello world shenyu Apache, findById&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在接口实现类中,使用注解<code>@ShenyuDubboClient</code>向<code>shenyu-admin</code>注册服务。</p> |
| <p>在配置文件<code>application.yml</code>中的配置信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">server</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">port</span>: <span style="color:#2aa198">8011</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">address</span>: <span style="color:#2aa198">0.0.0.0</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">servlet</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">context-path</span>: / |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spring</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">main</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">allow-bean-definition-overriding</span>: <span style="color:#cb4b16">true</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">dubbo</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">registry</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">address</span>: zookeeper://localhost:2181 <span style="color:#586e75"># dubbo使用的注册中心</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">shenyu</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">register</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">registerType</span>: http <span style="color:#586e75">#注册方式</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">serverLists</span>: http://localhost:9095 <span style="color:#586e75">#注册地址</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">props</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">username</span>: admin |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">password</span>: <span style="color:#2aa198">123456</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">client</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">dubbo</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">props</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">contextPath</span>: /dubbo |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">appName</span>: dubbo |
| </span></span></code></pre></div><p>在配置文件中,声明<code>dubbo</code>使用的注册中心地址,<code>dubbo</code>服务向<code>shenyu-admin</code>注册,使用的方式是<code>http</code>,注册地址是<code>http://localhost:9095</code>。</p> |
| <p>关于注册方式的使用,请参考 <code>[应用客户端接入](https://shenyu.apache.org/docs/design/register-center-design/)</code> 。</p> |
| <h3 id="24-调用dubbo服务">2.4 调用dubbo服务</h3> |
| <p><code>shenyu-examples-dubbo</code>项目成功启动之后会自动把加 <code>@ShenyuDubboClient</code> 注解的接口方法注册到网关。</p> |
| <p>打开 <code>插件列表 -&gt; Proxy -&gt; dubbo</code> 可以看到插件规则配置列表:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-service-list-zh.png" alt="img"></p> |
| <p>注册成功的选择器信息:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-selector-zh.png" alt="img"></p> |
| <p>注册成功的规则信息:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-rule-zh.png" alt="img"></p> |
| <blockquote> |
| <p>选择器和规则是 <code>Apache ShenYu</code> 网关中最灵魂的东西。掌握好它,你可以对任何流量进行管理。对应为选择器与规则里面的匹配条件(conditions),根据不同的流量筛选规则,我们可以处理各种复杂的场景。流量筛选可以从<code>Header</code>, <code>URI</code>, <code>Query</code>, <code>Cookie</code> 等等Http请求获取数据。</p> |
| <p>然后可以采用 <code>Match</code>,<code>=</code>,<code>Regex</code>,<code>Groovy</code>,<code>Exclude</code>等匹配方式,匹配出你所预想的数据。多组匹配添加可以使用<code>And/Or</code>的匹配策略。</p> |
| <p>具体的介绍与使用请看: <code>[选择器与规则管理](https://shenyu.apache.org/zh/docs/user-guide/admin-usage/selector-and-rule)</code> 。</p> |
| </blockquote> |
| <p>发起<code>GET</code>请求,通过<code>ShenYu</code>网关调用<code>dubbo</code>服务:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>GET http://localhost:9195/dubbo/findById?id=100 |
| </span></span><span style="display:flex;"><span>Accept: application/json |
| </span></span></code></pre></div><p>成功响应之后,结果如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> &#34;name&#34;: &#34;hello world shenyu Apache, findById&#34;, |
| </span></span><span style="display:flex;"><span> &#34;id&#34;: &#34;100&#34; |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>至此,就成功的通过<code>http</code>请求访问<code>dubbo</code>服务了,<code>ShenYu</code>网关通过<code>shenyu-plugin-dubbo</code>模块将<code>http</code>协议转成了<code>dubbo</code>协议。</p> |
| <h2 id="3-深入理解dubbo插件">3. 深入理解Dubbo插件</h2> |
| <p>在运行上述<code>demo</code>的过程中,是否存在一些疑问:</p> |
| <ul> |
| <li><code>dubbo</code>服务是如何注册到<code>shenyu-admin</code>?</li> |
| <li><code>shenyu-admin</code>是如何将数据同步到<code>ShenYu</code>网关?</li> |
| <li><code>DubboPlugin</code>是如何将<code>http</code>协议转换到到dubbo协议?</li> |
| </ul> |
| <p>带着这些疑问,来深入理解<code>dubbo</code>插件。</p> |
| <h3 id="31-应用客户端接入">3.1 应用客户端接入</h3> |
| <p>应用客户端接入是指将微服务接入到<code>Apache ShenYu</code>网关,当前支持<code>Http</code>、 <code>Dubbo</code>、 <code>Spring Cloud</code>、 <code>gRPC</code>、 <code>Motan</code>、 <code>Sofa</code>、 <code>Tars</code>等协议的接入。</p> |
| <p>将应用客户端接入到<code>Apache ShenYu</code>网关是通过注册中心来实现的,涉及到客户端注册和服务端同步数据。注册中心支持<code>Http</code>、<code>Zookeeper</code>、<code>Etcd</code>、<code>Consul</code>和<code>Nacos</code>。默认是通过<code>Http</code>方式注册。</p> |
| <p>客户端接入的相关配置请参考 <code>[客户端接入配置](https://shenyu.apache.org/zh/docs/user-guide/register-center-access)</code> 。</p> |
| <h4 id="311-客户端注册">3.1.1 客户端注册</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/register-client.png" alt="img"></p> |
| <p>在你的微服务配置中声明注册中心客户端类型,如<code>Http</code>或<code>Zookeeper</code>。 |
| 应用程序启动时使用<code>SPI</code>方式加载并初始化对应注册中心客户端,通过实现<code>Spring Bean</code>相关的后置处理器接口,在其中获取需要进行注册的服务接口信息,将获取的信息放入<code>Disruptor</code>中。</p> |
| <p>注册中心客户端从<code>Disruptor</code>中读取数据,并将接口信息注册到<code>shenyu-admin</code>,<code>Disruptor</code>在其中起数据与操作解耦的作用,利于扩展。</p> |
| <h4 id="312-服务端注册">3.1.2 服务端注册</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/register-server.png" alt="img"></p> |
| <p>在<code>shenyu-admin</code>配置中声明注册中心服务端类型,如<code>Http</code>或<code>Zookeeper</code>。当<code>shenyu-admin</code>启动时,读取配置类型,加载并初始化对应的注册中心服务端,注册中心服务端收到<code>shenyu-client</code>注册的接口信息后,将其放入<code>Disruptor</code>中,然后会触发注册处理逻辑,将服务接口信息更新并发布同步事件。</p> |
| <p><code>Disruptor</code>在其中起到数据与操作解耦,利于扩展。如果注册请求过多,导致注册异常,也有数据缓冲作用。</p> |
| <h3 id="32-数据同步原理">3.2 数据同步原理</h3> |
| <p>数据同步是指在 <code>shenyu-admin</code> 后台操作数据以后,使用何种策略将数据同步到 <code>Apache ShenYu</code> 网关。<code>Apache ShenYu</code> 网关当前支持<code>ZooKeeper</code>、<code>WebSocket</code>、<code>Http长轮询</code>、<code>Nacos</code> 、<code>Etcd</code> 和 <code>Consul</code> 进行数据同步。默认是通过<code>WebSocket</code>进行数据同步。</p> |
| <p>数据同步的相关配置请参考 <code>[数据同步配置](https://shenyu.apache.org/zh/docs/user-guide/use-data-sync)</code> 。</p> |
| <h4 id="321-数据同步的意义">3.2.1 数据同步的意义</h4> |
| <p>网关是流量请求的入口,在微服务架构中承担了非常重要的角色,网关高可用的重要性不言而喻。在使用网关的过程中,为了满足业务诉求,经常需要变更配置,比如流控规则、路由规则等等。因此,网关动态配置是保障网关高可用的重要因素。</p> |
| <p>当前数据同步特性如下:</p> |
| <ul> |
| <li>所有的配置都缓存在 <code>Apache ShenYu</code> 网关内存中,每次请求都使用本地缓存,速度非常快。</li> |
| <li>用户可以在 <code>shenyu-admin</code> 后台任意修改数据,并马上同步到网关内存。</li> |
| <li>支持 <code>Apache ShenYu</code> 的插件、选择器、规则数据、元数据、签名数据等数据同步。</li> |
| <li>所有插件的选择器,规则都是动态配置,立即生效,不需要重启服务。</li> |
| <li>数据同步方式支持 <code>Zookeeper</code>、<code>Http 长轮询</code>、<code>Websocket</code>、<code>Nacos</code>、<code>Etcd</code> 和 <code>Consul</code>。</li> |
| </ul> |
| <h4 id="322-数据同步原理分析">3.2.2 数据同步原理分析</h4> |
| <p>下图展示了 <code>Apache ShenYu</code> 数据同步的流程,<code>Apache ShenYu</code> 网关在启动时,会从配置服务同步配置数据,并且支持推拉模式获取配置变更信息,然后更新本地缓存。管理员可以在管理后台(<code>shenyu-admin</code>),变更用户权限、规则、插件、流量配置,通过推拉模式将变更信息同步给 <code>Apache ShenYu</code> 网关,具体是 <code>push</code> 模式,还是 <code>pull</code> 模式取决于使用哪种同步方式。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/data-sync.png" alt="img"></p> |
| <p>在最初的版本中,配置服务依赖 <code>Zookeeper</code> 实现,管理后台将变更信息 <code>push</code> 给网关。而现在可以支持 <code>WebSocket</code>、<code>Http长轮询</code>、<code>Zookeeper</code>、<code>Nacos</code>、<code>Etcd</code> 和 <code>Consul</code>,通过在配置文件中设置 <code>shenyu.sync.${strategy}</code> 指定对应的同步策略,默认使用 <code>webosocket</code> 同步策略,可以做到秒级数据同步。但是,有一点需要注意的是,<code>Apache ShenYu</code>网关 和 <code>shenyu-admin</code> 必须使用相同的同步策略。</p> |
| <p>如下图所示,<code>shenyu-admin</code> 在用户发生配置变更之后,会通过 <code>EventPublisher</code> 发出配置变更通知,由 <code>EventDispatcher</code> 处理该变更通知,然后根据配置的同步策略(<code>http、weboscket、zookeeper、naocs、etcd、consul</code>),将配置发送给对应的事件处理器。</p> |
| <ul> |
| <li>如果是 <code>websocket</code> 同步策略,则将变更后的数据主动推送给 <code>shenyu-web</code>,并且在网关层,会有对应的 <code>WebsocketDataHandler</code> 处理器来处理 <code>shenyu-admin</code> 的数据推送。</li> |
| <li>如果是 <code>zookeeper</code> 同步策略,将变更数据更新到 <code>zookeeper</code>,而 <code>ZookeeperSyncCache</code> 会监听到 <code>zookeeper</code> 的数据变更,并予以处理。</li> |
| <li>如果是 <code>http</code> 同步策略,由网关主动发起长轮询请求,默认有 <code>90s</code> 超时时间,如果 <code>shenyu-admin</code> 没有数据变更,则会阻塞 <code>http</code> 请求,如果有数据发生变更则响应变更的数据信息,如果超过 <code>60s</code> 仍然没有数据变更则响应空数据,网关层接到响应后,继续发起 <code>http</code> 请求,反复同样的请求。</li> |
| </ul> |
| <h3 id="33-流程分析">3.3 流程分析</h3> |
| <p>流程分析是从源码的角度,展示服务注册流程,数据同步流程和服务调用流程。</p> |
| <h4 id="331-服务注册流程">3.3.1 服务注册流程</h4> |
| <ul> |
| <li>读取dubbo服务</li> |
| </ul> |
| <p>使用注解<code>@ShenyuDubboClient</code>标记需要注册到网关的<code>dubbo</code>服务。</p> |
| <p>注解扫描通过<code>ApacheDubboServiceBeanListener</code>完成,它实现了<code>ApplicationListener&lt;ContextRefreshedEvent&gt;</code>接口,在<code>Spring</code>容器启动过程中,发生上下文刷新事件时,开始执行事件处理方法<code>onApplicationEvent()</code>。在重写的方法逻辑中,读取<code>Dubbo</code>服务<code>ServiceBean</code>,构建元数据对象和<code>URI</code>对象,并向<code>shenyu-admin</code>注册。</p> |
| <p>具体的注册逻辑由注册中心实现,请参考 <code>[客户端接入原理](https://shenyu.apache.org/zh/docs/design/register-center-design/)</code> 。</p> |
| <ul> |
| <li>处理注册信息</li> |
| </ul> |
| <p>客户端通过注册中心注册的元数据和<code>URI</code>数据,在<code>shenyu-admin</code>端进行处理,负责存储到数据库和同步给<code>shenyu</code>网关。<code>Dubbo</code>插件的客户端注册处理逻辑在<code>ShenyuClientRegisterDubboServiceImpl</code>中。继承关系如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/ShenyuClientRegisterDubboServiceImpl.png" alt="img"></p> |
| <ul> |
| <li>ShenyuClientRegisterService:客户端注册服务,顶层接口;</li> |
| <li>FallbackShenyuClientRegisterService:注册失败,提供重试操作;</li> |
| <li>AbstractShenyuClientRegisterServiceImpl:抽象类,实现部分公共注册逻辑;</li> |
| <li>ShenyuClientRegisterDubboServiceImpl:实现<code>Dubbo</code>插件的注册;</li> |
| </ul> |
| <p>注册信息包括选择器,规则和元数据。</p> |
| <p>整体的<code>dubbo</code>服务注册流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-register-zh.png" alt="img"></p> |
| <h4 id="332-数据同步流程">3.3.2 数据同步流程</h4> |
| <ul> |
| <li>admin更新数据</li> |
| </ul> |
| <p>假设在在后台管理系统中,新增一条选择器数据,请求会进入<code>SelectorController</code>类中的<code>createSelector()</code>方法,它负责数据的校验,添加或更新数据,返回结果信息。</p> |
| <p>在<code>SelectorServiceImpl</code>类中通过<code>createOrUpdate()</code>方法完成数据的转换,保存到数据库,发布事件,更新<code>upstream</code>。</p> |
| <p>在<code>Service</code>类完成数据的持久化操作,即保存数据到数据库。发布变更数据通过<code>eventPublisher.publishEvent()</code>完成,这个<code>eventPublisher</code>对象是一个<code>ApplicationEventPublisher</code>类,这个类的全限定名是<code>org.springframework.context.ApplicationEventPublisher</code>,发布数据的功能正是是通过<code>Spring</code>相关的功能来完成的。</p> |
| <p>当事件发布完成后,会自动进入到<code>DataChangedEventDispatcher</code>类中的<code>onApplicationEvent()</code>方法,根据不同数据类型和数据同步方式进行事件处理。</p> |
| <ul> |
| <li>网关数据同步</li> |
| </ul> |
| <p>网关在启动时,根据指定的数据同步方式加载不同的配置类,初始化数据同步相关类。</p> |
| <p>在接收到数据后,进行反序列化操作,读取数据类型和操作类型。不同的数据类型,有不同的数据处理方式,所以有不同的实现类。但是它们之间也有相同的处理逻辑,所以可以通过模板方法设计模式来实现。相同的逻辑放在抽象类<code>AbstractDataHandler</code>中的<code>handle()</code>方法中,不同逻辑就交给各自的实现类。</p> |
| <p>新增一条选择器数据,是新增操作,会进入到<code>SelectorDataHandler.doUpdate()</code>具体的数据处理逻辑中。</p> |
| <p>在通用插件数据订阅者<code>CommonPluginDataSubscriber</code>,负责处理所有插件、选择器和规则信息</p> |
| <p>将数据保存到网关的内存中,<code>BaseDataCache</code>是最终缓存数据的类,通过单例模式实现。选择器数据就存到了<code>SELECTOR_MAP</code>这个<code>Map</code>中。在后续使用的时候,也是从这里拿数据。</p> |
| <p>上述逻辑用流程图表示如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/data-sync-seq-zh.png" alt="img"></p> |
| <h4 id="333-服务调用流程">3.3.3 服务调用流程</h4> |
| <p>在<code>Dubbo</code>插件体系中,类继承关系如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/ApacheDubboPlugin.png" alt="img"></p> |
| <blockquote> |
| <p>ShenyuPlugin:顶层接口,定义接口方法;</p> |
| <p>AbstractShenyuPlugin:抽象类,实现插件共有逻辑;</p> |
| <p>AbstractDubboPlugin:dubbo插件抽象类,实现<code>dubbo</code>共有逻辑(ShenYu网关支持ApacheDubbo和AlibabaDubbo);</p> |
| <p>ApacheDubboPlugin:ApacheDubbo插件。</p> |
| </blockquote> |
| <ul> |
| <li>org.apache.shenyu.web.handler.ShenyuWebHandler.DefaultShenyuPluginChain#execute()</li> |
| </ul> |
| <p>通过<code>ShenYu</code>网关代理后,请求入口是<code>ShenyuWebHandler</code>,它实现了<code>org.springframework.web.server.WebHandler</code>接口,通过责任链设计模式将所有插件连接起来。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.base.AbstractShenyuPlugin#execute()</li> |
| </ul> |
| <p>当请求到网关时,判断某个插件是否执行,是通过指定的匹配逻辑来完成。在<code>execute()</code>方法中执行选择器和规则的匹配逻辑。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.global.GlobalPlugin#execute()</li> |
| </ul> |
| <p>最先被执行的是<code>GlobalPlugin</code> ,它是一个全局插件,在<code>execute()</code>方法中构建上下文信息。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.base.RpcParamTransformPlugin#execute()</li> |
| </ul> |
| <p>接着被执行的是<code>RpcParamTransformPlugin</code> , 它负责从<code>http</code>请求中读取参数,保存到<code>exchange</code>中,传递给<code>rpc</code>服务。在<code>execute()</code>方法中,执行该插件的核心逻辑:从<code>exchange</code>中获取请求信息,根据请求传入的内容形式处理参数。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.dubbo.common.AbstractDubboPlugin</li> |
| </ul> |
| <p>然后被执行的是<code>DubboPlugin</code> 。在<code>doExecute()</code>方法中,主要是检查元数据和参数。在<code>doDubboInvoker()</code>方法中设置特殊的上下文信息,然后开始<code>dubbo</code>的泛化调用。</p> |
| <p>在<code>genericInvoker()</code>方法中:</p> |
| <ul> |
| <li>获取<code>ReferenceConfig</code>对象;</li> |
| <li>获取泛化服务<code>GenericService</code>对象;</li> |
| <li>构造请求参数<code>pair</code>对象;</li> |
| <li>发起异步的泛化调用。</li> |
| </ul> |
| <p>通过泛化调用就可以实现在网关调用<code>dubbo</code>服务了。</p> |
| <p><code>ReferenceConfig</code>对象是支持泛化调用的关键对象 ,它的初始化操作是在数据同步的时候完成的。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.response.ResponsePlugin#execute()</li> |
| </ul> |
| <p>最后被执行的是<code>ResponsePlugin</code> ,它统一处理网关的响应结果信息。处理类型由<code>MessageWriter</code>决定,类继承关系如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/MessageWriter.png" alt="img"></p> |
| <blockquote> |
| <p>MessageWriter:接口,定义消息处理方法;</p> |
| <p>NettyClientMessageWriter:处理<code>Netty</code>调用结果;</p> |
| <p>RPCMessageWriter:处理<code>RPC</code>调用结果;</p> |
| <p>WebClientMessageWriter:处理<code>WebClient</code>调用结果;</p> |
| </blockquote> |
| <p><code>Dubbo</code>服务调用,处理结果是<code>RPCMessageWriter</code>。</p> |
| <ul> |
| <li>org.apache.shenyu.plugin.response.strategy.RPCMessageWriter#writeWith()</li> |
| </ul> |
| <p>在<code>writeWith()</code>方法中处理响应结果,获取结果或处理异常。</p> |
| <p>分析至此,关于<code>Dubbo</code>插件的源码分析就完成了,分析流程图如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-execute-zh.png" alt="img"></p> |
| <h2 id="4-小结">4. 小结</h2> |
| <p>本文从实际案例出发,由浅入深分析了<code>ShenYu</code>网关对Dubbo服务的代理过程。涉及到的主要知识点如下:</p> |
| <ul> |
| <li>通过责任链设计模式执行插件;</li> |
| <li>使用模板方法设计模式实现<code>AbstractShenyuPlugin</code>,处理通用的操作类型;</li> |
| <li>使用单例设计模式实现缓存数据类<code>BaseDataCache</code>;</li> |
| <li>通过<code>springboot starter</code>即可引入不同的注册中心和数同步方式,扩展性很好;</li> |
| <li>通过<code>admin</code>支持规则热更新,方便流量管控;</li> |
| <li><code>Disruptor</code>队列是为了数据与操作解耦,以及数据缓冲。</li> |
| </ul></description></item><item><title>Blog: 从原理到操作,让你在 Apache APISIX 中代理 Dubbo 服务更便捷</title><link>https://dubbo.apache.org/zh-cn/blog/2022/01/18/%E4%BB%8E%E5%8E%9F%E7%90%86%E5%88%B0%E6%93%8D%E4%BD%9C%E8%AE%A9%E4%BD%A0%E5%9C%A8-apache-apisix-%E4%B8%AD%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1%E6%9B%B4%E4%BE%BF%E6%8D%B7/</link><pubDate>Tue, 18 Jan 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/01/18/%E4%BB%8E%E5%8E%9F%E7%90%86%E5%88%B0%E6%93%8D%E4%BD%9C%E8%AE%A9%E4%BD%A0%E5%9C%A8-apache-apisix-%E4%B8%AD%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1%E6%9B%B4%E4%BE%BF%E6%8D%B7/</guid><description> |
| <h2 id="背景">背景</h2> |
| <p><a href="https://dubbo.apache.org/zh-cn/">Apache Dubbo</a> 是由阿里巴巴开源并捐赠给 Apache 的微服务开发框架,它提供了 RPC 通信与微服务治理两大关键能力。不仅经过了阿里电商场景中海量流量的验证,也在国内的技术公司中被广泛落地。</p> |
| <p>在实际应用场景中,Apache Dubbo 一般会作为后端系统间 RPC 调用的实现框架,当需要提供 HTTP 接口给到前端时,会通过一个「胶水层」将 Dubbo Service 包装成 HTTP 接口,再交付到前端系统。</p> |
| <p><a href="https://apisix.apache.org/">Apache APISIX</a> 是 Apache 软件基金会的顶级开源项目,也是当前最活跃的开源网关项目。作为一个动态、实时、高性能的开源 API 网关,Apache APISIX 提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。</p> |
| <p>得益于 Apache Dubbo 的应用场景优势,Apache APISIX 基于开源项目 tengine/mod_dubbo 模块为 Apache Dubbo 服务配备了HTTP 网关能力。通过 dubbo-proxy 插件,可以轻松地将 Dubbo Service 发布为 HTTP 服务。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/apisix-plugin/1.png" alt="架构图"></p> |
| <h2 id="如何使用">如何使用</h2> |
| <h3 id="入门篇安装使用">入门篇:安装使用</h3> |
| <blockquote> |
| <p>这里我们建议使用 Apache APISIX 2.11 版本镜像进行安装。该版本的 APISIX-Base 中已默认编译了 Dubbo 模块,可直接使用 <code>dubbo-proxy</code> 插件。</p> |
| </blockquote> |
| <p>在接下来的操作中,我们将使用 <a href="https://github.com/apache/dubbo-samples"><code>dubbo-samples</code></a> 项目进行部分展示。该项目是一些使用 Apache Dubbo 实现的 Demo 应用,本文中我们采用其中的一个子模块作为 Dubbo Provider。</p> |
| <p>在进入正式操作前,我们先简单看下 Dubbo 接口的定义、配置以及相关实现。</p> |
| <h4 id="接口实现一览">接口实现一览</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">DemoService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * standard samples dubbo infterace demo |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param context pass http infos |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return Map&lt;String, Object&gt;&lt;/&gt; pass to response http |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> **/</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">apisixDubbo</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> httpRequestContext<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>如上所示,Dubbo 接口的定义是固定的。即方法参数中 <code>Map</code> 表示 APISIX 传递给 Dubbo Provider 关于 HTTP request 的一些信息(如:header、body&hellip;)。而方法返回值的 <code>Map</code> 表示 Dubbo Provider 传递给 APISIX 要如何返回 HTTP response 的一些信息。</p> |
| <p>接口信息配置好之后可通过 XML 配置方式发布 DemoService。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- service implementation, as same as regular local bean --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.provider.DemoServiceImpl&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- declare the service interface to be exported --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.apisix.DemoService&#34;</span> ref=<span style="color:#2aa198">&#34;demoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>通过上述配置后,Consumer 可通过 <code>org.apache.dubbo.samples.apisix.DemoService</code> 访问其中的<code>apisixDubbo</code> 方法。具体接口实现如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceImpl</span> <span style="color:#268bd2">implements</span> DemoService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">apisixDubbo</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> httpRequestContext<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> httpRequestContext<span style="color:#719e07">.</span>entrySet<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Key = &#34;</span> <span style="color:#719e07">+</span> entry<span style="color:#719e07">.</span>getKey<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, Value = &#34;</span> <span style="color:#719e07">+</span> entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> ret <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;();</span> |
| </span></span><span style="display:flex;"><span> ret<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;body&#34;</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;dubbo success\n&#34;</span><span style="color:#719e07">);</span> <span style="color:#586e75">// http response body |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ret<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;status&#34;</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;200&#34;</span><span style="color:#719e07">);</span> <span style="color:#586e75">// http response status |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ret<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;test&#34;</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;123&#34;</span><span style="color:#719e07">);</span> <span style="color:#586e75">// http response header |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> ret<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上述代码中,<code>DemoServiceImpl</code> 会打印接收到的 <code>httpRequestContext</code>,并通过返回包含有指定 Key 的 Map 对象去描述该 Dubbo 请求的 HTTP 响应。</p> |
| <h4 id="操作步骤">操作步骤</h4> |
| <ol> |
| <li>启动 <a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-tengine#install-dubbo"><code>dubbo-samples</code></a>。</li> |
| <li>在 <code>config.yaml</code> 文件中进行 <code>dubbo-proxy</code> 插件启用。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#586e75"># Add this in config.yaml</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">plugins</span>: |
| </span></span><span style="display:flex;"><span> - ... <span style="color:#586e75"># plugin you need</span> |
| </span></span><span style="display:flex;"><span> - dubbo-proxy |
| </span></span></code></pre></div><ol start="3"> |
| <li>创建指向 Dubbo Provider 的 Upstream。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H <span style="color:#2aa198">&#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&#39;</span> -X PUT -d <span style="color:#2aa198">&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">{ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;nodes&#34;: { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;127.0.0.1:20880&#34;: 1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> }, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;type&#34;: &#34;roundrobin&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">}&#39;</span> |
| </span></span></code></pre></div><ol start="4"> |
| <li>为 DemoService 暴露一个 HTTP 路由。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9180/apisix/admin/routes/1 -H <span style="color:#2aa198">&#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&#39;</span> -X PUT -d <span style="color:#2aa198">&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">{ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;host&#34;: &#34;example.org&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;uris&#34;: [ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;/demo&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> ], |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;plugins&#34;: { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;dubbo-proxy&#34;: { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;service_name&#34;: &#34;org.apache.dubbo.samples.apisix.DemoService&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;service_version&#34;: &#34;0.0.0&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;method&#34;: &#34;apisixDubbo&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> } |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> }, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;upstream_id&#34;: 1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">}&#39;</span> |
| </span></span></code></pre></div><ol start="5"> |
| <li>使用 curl 命令请求 Apache APISIX,并查看返回结果。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9080/demo -H <span style="color:#2aa198">&#34;Host: example.org&#34;</span> -X POST --data <span style="color:#2aa198">&#39;{&#34;name&#34;: &#34;hello&#34;}&#39;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>&lt; HTTP/1.1 <span style="color:#2aa198">200</span> OK |
| </span></span><span style="display:flex;"><span>&lt; Date: Sun, <span style="color:#2aa198">26</span> Dec <span style="color:#2aa198">2021</span> 11:33:27 GMT |
| </span></span><span style="display:flex;"><span>&lt; Content-Type: text/plain; <span style="color:#268bd2">charset</span><span style="color:#719e07">=</span>utf-8 |
| </span></span><span style="display:flex;"><span>&lt; Content-Length: <span style="color:#2aa198">14</span> |
| </span></span><span style="display:flex;"><span>&lt; Connection: keep-alive |
| </span></span><span style="display:flex;"><span>&lt; test: <span style="color:#2aa198">123</span> |
| </span></span><span style="display:flex;"><span>&lt; Server: APISIX/2.11.0 |
| </span></span><span style="display:flex;"><span>&lt; |
| </span></span><span style="display:flex;"><span>dubbo success |
| </span></span></code></pre></div><p>:::note 说明 |
| 上述代码返回中包含了 <code>test: 123</code> Header,以及 <code>dubbo success</code> 字符串作为 Body 体。这与我们在 <code>DemoServiceImpl</code> 编码的预期效果一致。 |
| :::</p> |
| <ol start="6"> |
| <li>查看 Dubbo Provider 的日志。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>Key = content-length, Value = 17 |
| </span></span><span style="display:flex;"><span>Key = host, Value = example.org |
| </span></span><span style="display:flex;"><span>Key = content-type, Value = application/x-www-form-urlencoded |
| </span></span><span style="display:flex;"><span>Key = body, Value = [B@70754265 |
| </span></span><span style="display:flex;"><span>Key = accept, Value = */* |
| </span></span><span style="display:flex;"><span>Key = user-agent, Value = curl/7.80.0 |
| </span></span></code></pre></div><p>:::note 说明 |
| 通过 <code>httpRequestContext</code> 可以拿到 HTTP 请求的 Header 和 Body。其中 Header 会作为 Map 元素,而 Body 中 Key 值是固定的字符串&quot;body&quot;,Value 则代表 Byte 数组。 |
| :::</p> |
| <h3 id="进阶篇复杂场景示例">进阶篇:复杂场景示例</h3> |
| <p>在上述的简单用例中可以看出,我们确实通过 Apache APISIX 将 Dubbo Service 发布为一个 HTTP 服务,但是在使用过程中的限制也非常明显。比如:接口的参数和返回值都必须要是 <code>Map&lt;String, Object&gt;</code>。</p> |
| <p>那么,如果项目中出现已经定义好、但又不符合上述限制的接口,该如何通过 Apache APISIX 来暴露 HTTP 服务呢?</p> |
| <h4 id="操作步骤-1">操作步骤</h4> |
| <p>针对上述场景,我们可以通过 HTTP Request Body 描述要调用的 Service 和 Method 以及对应参数,再利用 Java 的反射机制实现目标方法的调用。最后将返回值序列化为 JSON,并写入到 HTTP Response Body 中。</p> |
| <p>这样就可以将 Apache APISIX 的 「HTTP to Dubbo」 能力进一步加强,并应用到所有已存在的 Dubbo Service 中。具体操作可参考下方:</p> |
| <ol> |
| <li>为已有项目增加一个 Dubbo Service 用来统一处理 HTTP to Dubbo 的转化。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DubboInvocationParameter</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String type<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String value<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DubboInvocation</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String service<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String method<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> DubboInvocationParameter<span style="color:#719e07">[]</span> parameters<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">HTTP2DubboService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">invoke</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> context<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Component</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">HTTP2DubboServiceImpl</span> <span style="color:#268bd2">implements</span> HTTP2DubboService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Autowired</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> ApplicationContext appContext<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">invoke</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> context<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> DubboInvocation invocation <span style="color:#719e07">=</span> JSONObject<span style="color:#719e07">.</span>parseObject<span style="color:#719e07">((</span><span style="color:#dc322f">byte</span><span style="color:#719e07">[])</span> context<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;body&#34;</span><span style="color:#719e07">),</span> DubboInvocation<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Object<span style="color:#719e07">[]</span> args <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Object<span style="color:#719e07">[</span>invocation<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">().</span>size<span style="color:#719e07">()];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0<span style="color:#719e07">;</span> i <span style="color:#719e07">&lt;</span> args<span style="color:#719e07">.</span>length<span style="color:#719e07">;</span> i<span style="color:#719e07">++)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> DubboInvocationParameter parameter <span style="color:#719e07">=</span> invocation<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">().</span>get<span style="color:#719e07">(</span>i<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> args<span style="color:#719e07">[</span>i<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> JSONObject<span style="color:#719e07">.</span>parseObject<span style="color:#719e07">(</span>parameter<span style="color:#719e07">.</span>getValue<span style="color:#719e07">(),</span> Class<span style="color:#719e07">.</span>forName<span style="color:#719e07">(</span>parameter<span style="color:#719e07">.</span>getType<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Object svc <span style="color:#719e07">=</span> appContext<span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span>Class<span style="color:#719e07">.</span>forName<span style="color:#719e07">(</span>invocation<span style="color:#719e07">.</span>getService<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> Object result <span style="color:#719e07">=</span> svc<span style="color:#719e07">.</span>getClass<span style="color:#719e07">().</span>getMethod<span style="color:#719e07">(</span>invocation<span style="color:#719e07">.</span>getMethod<span style="color:#719e07">()).</span>invoke<span style="color:#719e07">(</span>args<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> httpResponse <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> httpResponse<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;status&#34;</span><span style="color:#719e07">,</span> 200<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> httpResponse<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;body&#34;</span><span style="color:#719e07">,</span> JSONObject<span style="color:#719e07">.</span>toJSONString<span style="color:#719e07">(</span>result<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> httpResponse<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ol start="2"> |
| <li>通过如下命令请求来发起相关调用。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9080/demo -H <span style="color:#2aa198">&#34;Host: example.org&#34;</span> -X POST --data <span style="color:#2aa198">&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">{ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;service&#34;: &#34;org.apache.dubbo.samples.apisix.DemoService&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;method&#34;: &#34;createUser&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;parameters&#34;: [ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;type&#34;: &#34;org.apache.dubbo.samples.apisix.User&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;value&#34;: &#34;{&#39;</span>name<span style="color:#2aa198">&#39;: &#39;</span>hello<span style="color:#2aa198">&#39;}&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> } |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> ] |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">}&#39;</span> |
| </span></span></code></pre></div><h2 id="总结">总结</h2> |
| <p>本文为大家介绍了如何借助 Apache APISIX 实现 Dubbo Service 的代理,通过引入 <code>dubbo-proxy</code> 插件便可为 Dubbo 框架的后端系统构建更简单更高效的流量链路。</p> |
| <p>希望通过上述操作步骤和用例场景分享,能为大家在相关场景的使用提供借鉴思路。更多关于 <code>dubbo-proxy</code> 插件的介绍与使用可参考<a href="https://apisix.apache.org/docs/apisix/plugins/dubbo-proxy/">官方文档</a>。</p></description></item><item><title>Blog: Dubbo3 应用级服务发现</title><link>https://dubbo.apache.org/zh-cn/blog/2021/06/02/dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</link><pubDate>Wed, 02 Jun 2021 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2021/06/02/dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</guid><description> |
| <h2 id="1-服务发现service-discovery-概述">1 服务发现(Service Discovery) 概述</h2> |
| <p>从 Internet 刚开始兴起,如何动态感知后端服务的地址变化就是一个必须要面对的问题,为此人们定义了 DNS 协议,基于此协议,调用方只需要记住由固定字符串组成的域名,就能轻松完成对后端服务的访问,而不用担心流量最终会访问到哪些机器 IP,因为有代理组件会基于 DNS 地址解析后的地址列表,将流量透明的、均匀的分发到不同的后端机器上。</p> |
| <p>在使用微服务构建复杂的分布式系统时,如何感知 backend 服务实例的动态上下线,也是微服务框架最需要关心并解决的问题之一。业界将这个问题称之为 - 微服务的地址发现(Service Discovery),业界比较有代表性的微服务框架如 SpringCloud、Dubbo 等都抽象了强大的动态地址发现能力,并且为了满足微服务业务场景的需求,绝大多数框架的地址发现都是基于自己设计的一套机制来实现,因此在能力、灵活性上都要比传统 DNS 丰富得多。如 SpringCloud 中常用的 Eureka, Dubbo 中常用的 Zookeeper、Nacos 等,这些注册中心实现不止能够传递地址(IP + Port),还包括一些微服务的 Metadata 信息,如实例序列化类型、实例方法列表、各个方法级的定制化配置等。</p> |
| <p>下图是微服务中 Service Discovery 的基本工作原理图,微服务体系中的实例大概可分为三种角色:服务提供者(Provider)、服务消费者(Consumer)和注册中心(Registry)。而不同框架实现间最主要的区别就体现在注册中心数据的组织:地址如何组织、以什么粒度组织、除地址外还同步哪些数据?</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-1.png" alt="img1"></p> |
| <p>我们今天这篇文章就是围绕这三个角色展开,重点看下 Dubbo 中对于服务发现方案的设计,包括之前老的服务发现方案的优势和缺点,以及 Dubbo 3.0 中正在设计、开发中的全新的<strong>面向应用粒度的地址发现方案</strong>,我们期待这个新的方案能做到:</p> |
| <ul> |
| <li><strong>支持几十万/上百万级集群实例的地址发现</strong></li> |
| <li><strong>与不同的微服务体系(如 Spring Cloud)实现在地址发现层面的互通</strong></li> |
| </ul> |
| <h2 id="2-dubbo-地址发现机制解析">2 Dubbo 地址发现机制解析</h2> |
| <p>我们先以一个 DEMO 应用为例,来快速的看一下 Dubbo “接口粒度”服务发现与“应用粒度”服务发现体现出来的区别。这里我们重点关注 Provider 实例是如何向注册中心注册的,并且,为了体现注册中心数据量变化,我们观察的是两个 Provider 实例的场景。</p> |
| <p><strong>应用 DEMO 提供的服务列表如下:</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService&#34;</span> ref=<span style="color:#2aa198">&#34;demoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.GreetingService&#34;</span> ref=<span style="color:#2aa198">&#34;greetingService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>我们示例注册中心实现采用的是 Zookeeper ,启动 192.168.0.103 和 192.168.0.104 两个实例后,以下是两种模式下注册中心的实际数据</p> |
| <p><strong>1. “接口粒度” 服务发现</strong></p> |
| <p>192.168.0.103 实例注册的数据</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>dubbo://192.168.0.103:20880/org.apache.dubbo.samples.basic.api.DemoService?anyhost=true&amp;application=demo-provider&amp;default=true&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.samples.basic.api.DemoService&amp;methods=testVoid,sayHello&amp;pid=995&amp;release=2.7.7&amp;side=provider&amp;timestamp=1596988171266 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>dubbo://192.168.0.103:20880/org.apache.dubbo.samples.basic.api.GreetingService?anyhost=true&amp;application=demo-provider&amp;default=true&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.samples.basic.api.GreetingService&amp;methods=greeting&amp;pid=995&amp;release=2.7.7&amp;side=provider&amp;timestamp=1596988170816 |
| </span></span></code></pre></div><p>192.168.0.104 实例注册的数据</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>dubbo://192.168.0.104:20880/org.apache.dubbo.samples.basic.api.DemoService?anyhost=true&amp;application=demo-provider&amp;default=true&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.samples.basic.api.DemoService&amp;methods=testVoid,sayHello&amp;pid=995&amp;release=2.7.7&amp;side=provider&amp;timestamp=1596988171266 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>dubbo://192.168.0.104:20880/org.apache.dubbo.samples.basic.api.GreetingService?anyhost=true&amp;application=demo-provider&amp;default=true&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.samples.basic.api.GreetingService&amp;methods=greeting&amp;pid=995&amp;release=2.7.7&amp;side=provider&amp;timestamp=1596988170816 |
| </span></span></code></pre></div><p><strong>2. “应用粒度” 服务发现</strong></p> |
| <p>192.168.0.103 与 192.168.0.104 两个实例共享一份注册中心数据,如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;demo-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;id&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.103:20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;address&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.103&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;port&#34;</span>: <span style="color:#2aa198">20880</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;metadata&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.endpoints&#34;</span>: <span style="color:#2aa198">&#34;[{\&#34;port\&#34;:20880,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}]&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.metadata.storage-type&#34;</span>: <span style="color:#2aa198">&#34;local&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.revision&#34;</span>: <span style="color:#2aa198">&#34;6785535733750099598&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;time&#34;</span>: <span style="color:#2aa198">1583461240877</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;demo-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;id&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.104:20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;address&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.104&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;port&#34;</span>: <span style="color:#2aa198">20880</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;metadata&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.endpoints&#34;</span>: <span style="color:#2aa198">&#34;[{\&#34;port\&#34;:20880,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}]&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.metadata.storage-type&#34;</span>: <span style="color:#2aa198">&#34;local&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.revision&#34;</span>: <span style="color:#2aa198">&#34;7829635812370099387&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;time&#34;</span>: <span style="color:#2aa198">1583461240877</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>对比以上两种不同粒度的服务发现模式,从 “接口粒度” 升级到 “应用粒度” 后我们可以总结出最大的区别是:注册中心数据量不再与接口数成正比,不论应用提供有多少接口,注册中心只有一条实例数据。</p> |
| <p>那么接下来我们详细看下这个变化给 Dubbo 带来了哪些好处。</p> |
| <h2 id="3-dubbo-应用级服务发现的意义">3 Dubbo 应用级服务发现的意义</h2> |
| <p>我们先说结论,应用级服务发现给 Dubbo 带来以下优势:</p> |
| <ol> |
| <li>与业界主流微服务模型对齐,比如 SpringCloud、Kubernetes Native Service等。</li> |
| <li>提升性能与可伸缩性。注册中心数据的重新组织(减少),能最大幅度的减轻注册中心的存储、推送压力,进而减少 Dubbo Consumer 侧的地址计算压力;集群规模也开始变得可预测、可评估(与 RPC 接口数量无关,只与实例部署规模相关)。</li> |
| </ol> |
| <h3 id="31-对齐主流微服务模型">3.1 对齐主流微服务模型</h3> |
| <p>自动、透明的实例地址发现(负载均衡)是所有微服务框架需要解决的事情,这能让后端的部署结构对上游微服务透明,上游服务只需要从收到的地址列表中选取一个,发起调用就可以了。要实现以上目标,涉及两个关键点的自动同步:</p> |
| <ul> |
| <li>实例地址,服务消费方需要知道地址以建立连接</li> |
| <li>RPC 方法定义,服务消费方需要知道 RPC 服务的具体定义,不论服务类型是 rest 或 rmi 等。</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-2.png" alt="img2"></p> |
| <p>对于 RPC 实例间借助注册中心的数据同步,REST 定义了一套非常有意思的成熟度模型,感兴趣的朋友可以参考这里的链接 <a href="https://www.martinfowler.com/articles/richardsonMaturityModel.html">https://www.martinfowler.com/articles/richardsonMaturityModel.html</a>, 按照文章中的 4 级成熟度定义,Dubbo 当前基于接口粒度的模型可以对应到 L4 级别。</p> |
| <p>接下来,我们看看 Dubbo、SpringCloud 以及 Kubernetes 分别是怎么围绕自动化的实例地址发现这个目标设计的。</p> |
| <p><strong>1. Spring Cloud</strong></p> |
| <p>Spring Cloud 通过注册中心只同步了应用与实例地址,消费方可以基于实例地址与服务提供方建立连接,但是消费方对于如何发起 http 调用(SpringCloud 基于 rest 通信)一无所知,比如对方有哪些 http endpoint,需要传入哪些参数等。</p> |
| <p>RPC 服务这部分信息目前都是通过线下约定或离线的管理系统来协商的。这种架构的优缺点总结如下。 |
| 优势:部署结构清晰、地址推送量小; |
| 缺点:地址订阅需要指定应用名, provider 应用变更(拆分)需消费端感知;RPC 调用无法全自动同步。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-3.png" alt="img3"></p> |
| <p><strong>2. Dubbo</strong></p> |
| <p>Dubbo 通过注册中心同时同步了实例地址和 RPC 方法,因此其能实现 RPC 过程的自动同步,面向 RPC 编程、面向 RPC 治理,对后端应用的拆分消费端无感知,其缺点则是地址推送数量变大,和 RPC 方法成正比。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-4.png" alt="img4"></p> |
| <p><strong>3. Dubbo + Kubernetes</strong></p> |
| <p>Dubbo 要支持 Kubernetes native service,相比之前自建注册中心的服务发现体系来说,在工作机制上主要有两点变化:</p> |
| <ul> |
| <li>服务注册由平台接管,provider 不再需要关心服务注册</li> |
| <li>consumer 端服务发现将是 Dubbo 关注的重点,通过对接平台层的 API-Server、DNS 等,Dubbo client 可以通过一个 <a href="https://kubernetes.io/docs/concepts/services-networking/service/">Service Name</a>(通常对应到 Application Name)查询到一组 Endpoints(一组运行 provider 的 pod),通过将 Endpoints 映射到 Dubbo 内部地址列表,以驱动 Dubbo 内置的负载均衡机制工作。</li> |
| </ul> |
| <blockquote> |
| <p>Kubernetes Service 作为一个抽象概念,怎么映射到 Dubbo 是一个值得讨论的点</p> |
| <ul> |
| <li>Service Name - &gt; Application Name,Dubbo 应用和 Kubernetes 服务一一对应,对于微服务运维和建设环节透明,与开发阶段解耦。</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Service |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: provider-app-name |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: provider-app-name |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">protocol</span>: TCP |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">port</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">targetPort</span>: <span style="color:#2aa198">9376</span> |
| </span></span></code></pre></div><ul> |
| <li>Service Name - &gt; Dubbo RPC Service,Kubernetes 要维护调度的服务与应用内建 RPC 服务绑定,维护的服务数量变多。</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>--- |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Service |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: rpc-service-1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: provider-app-name |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: <span style="color:#586e75">##</span> |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span>--- |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Service |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: rpc-service-2 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: provider-app-name |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: <span style="color:#586e75">##</span> |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span>--- |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Service |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: rpc-service-N |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: provider-app-name |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: <span style="color:#586e75">##</span> |
| </span></span><span style="display:flex;"><span>... |
| </span></span></code></pre></div></blockquote> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-5.png" alt="img5"></p> |
| <p>结合以上几种不同微服务框架模型的分析,我们可以发现,Dubbo 与 SpringCloud、Kubernetes 等不同产品在微服务的抽象定义上还是存在很大不同的。SpringCloud 和 Kubernetes 在微服务的模型抽象上还是比较接近的,两者基本都只关心实例地址的同步,如果我们去关心其他的一些服务框架产品,会发现它们绝大多数也是这么设计的;</p> |
| <blockquote> |
| <p>即 REST 成熟度模型中的 L3 级别。</p> |
| </blockquote> |
| <p>对比起来 Dubbo 则相对是比较特殊的存在,更多的是从 RPC 服务的粒度去设计的。</p> |
| <blockquote> |
| <p>对应 REST 成熟度模型中的 L4 级别。</p> |
| </blockquote> |
| <p>如我们上面针对每种模型做了详细的分析,每种模型都有其优势和不足。而我们最初决定 Dubbo 要做出改变,往其他的微服务发现模型上的对齐,是我们最早在确定 Dubbo 的云原生方案时,我们发现要让 Dubbo 去支持 Kubernetes Native Service,模型对齐是一个基础条件;另一点是来自用户侧对 Dubbo 场景化的一些工程实践的需求,得益于 Dubbo 对多注册、多协议能力的支持,使得 Dubbo 联通不同的微服务体系成为可能,而服务发现模型的不一致成为其中的一个障碍,这部分的场景描述请参见以下文章:<a href="https://developer.aliyun.com/article/740260">Dubbo 如何成为连接异构微服务体系的最佳服务开发框架</a></p> |
| <h3 id="32-更大规模的微服务集群---解决性能瓶颈">3.2 更大规模的微服务集群 - 解决性能瓶颈</h3> |
| <p>这部分涉及到和注册中心、配置中心的交互,关于不同模型下注册中心数据的变化,之前原理部分我们简单分析过。为更直观的对比服务模型变更带来的推送效率提升,我们来通过一个示例看一下不同模型注册中心的对比:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-6.png" alt="img6"></p> |
| <p>图中左边是微服务框架的一个典型工作流程,Provider 和 Consumer 通过注册中心实现自动化的地址通知。其中,Provider 实例的信息如图中表格所示: |
| 应用 DEMO 包含三个接口 DemoService 1 2 3,当前实例的 ip 地址为 10.210.134.30。</p> |
| <ul> |
| <li>对于 Spring Cloud 和 Kubernetes 模型,注册中心只会存储一条 <code>DEMO - 10.210.134.30+metadata</code> 的数据;</li> |
| <li>对于老的 Dubbo 模型,注册中心存储了三条接口粒度的数据,分别对应三个接口 DemoService 1 2 3,并且很多的址数据都是重复的;</li> |
| </ul> |
| <p>可以总结出,基于应用粒度的模型所存储和推送的数据量是和应用、实例数成正比的,只有当我们的应用数增多或应用的实例数增长时,地址推送压力才会上涨。 |
| 而对于基于接口粒度的模型,数据量是和接口数量正相关的,鉴于一个应用通常发布多个接口的现状,这个数量级本身比应用粒度是要乘以倍数的;另外一个关键点在于,接口粒度导致的集群规模评估的不透明,相对于实例、应用增长都通常是在运维侧的规划之中,接口的定义更多的是业务侧的内部行为,往往可以绕过评估给集群带来压力。</p> |
| <p>以 Consumer 端服务订阅举例,根据我对社区部分 Dubbo 中大规模头部用户的粗略统计,根据受统计公司的实际场景,一个 Consumer 应用要消费(订阅)的 Provier 应用数量往往要超过 10 个,而具体到其要消费(订阅)的的接口数量则通常要达到 30 个,平均情况下 Consumer 订阅的 3 个接口来自同一个 Provider 应用,如此计算下来,如果以应用粒度为地址通知和选址基本单位,则平均地址推送和计算量将下降 60% 还要多, |
| 而在极端情况下,也就是当 Consumer 端消费的接口更多的来自同一个应用时,这个地址推送与内存消耗的占用将会进一步得到降低,甚至可以超过 80% 以上。</p> |
| <p>一个典型的极端场景即是 Dubbo 体系中的网关型应用,有些网关应用消费(订阅)达 100+ 应用,而消费(订阅)的服务有 1000+ ,平均有 10 个接口来自同一个应用,如果我们把地址推送和计算的粒度改为应用,则地址推送量从原来的 n * 1000 变为 n * 100,地址数量降低可达近 90%。</p> |
| <h2 id="4-应用级服务发现工作原理">4 应用级服务发现工作原理</h2> |
| <h3 id="41-设计原则">4.1 设计原则</h3> |
| <p>上面一节我们从<strong>服务模型</strong>及<strong>支撑大规模集群</strong>的角度分别给出了 Dubbo 往应用级服务发现靠拢的好处和原因,但这么做的同时接口粒度的服务治理能力还是要继续保留,这是 Dubbo 框架编程模型易用性、服务治理能力优势的基础。 |
| 以下是我认为我们做服务模型迁移仍要坚持的设计原则</p> |
| <ul> |
| <li>新的服务发现模型要实现对原有 Dubbo 消费端开发者的无感知迁移,即 Dubbo 继续面向 RPC 服务编程、面向 RPC 服务治理,做到对用户侧完全无感知。</li> |
| <li>建立 Consumer 与 Provider 间的自动化 RPC 服务元数据协调机制,解决传统微服务模型无法同步 RPC 级接口配置的缺点。</li> |
| </ul> |
| <h3 id="42-基本原理详解">4.2 基本原理详解</h3> |
| <p>应用级服务发现作为一种新的服务发现机制,和以前 Dubbo 基于 RPC 服务粒度的服务发现在核心流程上基本上是一致的:即服务提供者往注册中心注册地址信息,服务消费者从注册中心拉取&amp;订阅地址信息。</p> |
| <p>这里主要的不同有以下两点:</p> |
| <h4 id="421-注册中心数据以应用---实例列表格式组织不再包含-rpc-服务信息">4.2.1 注册中心数据以“应用 - 实例列表”格式组织,不再包含 RPC 服务信息</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-7.png" alt="img7"></p> |
| <p>以下是每个 Instance metadata 的示例数据,总的原则是 metadata 只包含当前 instance 节点相关的信息,不涉及 RPC 服务粒度的信息。</p> |
| <p>总体信息概括如下:实例地址、实例各种环境标、metadata service 元数据、其他少量必要属性。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;provider-app-name&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;id&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.102:20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;address&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.102&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;port&#34;</span>: <span style="color:#2aa198">20880</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;sslPort&#34;</span>: <span style="color:#cb4b16">null</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;payload&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;id&#34;</span>: <span style="color:#cb4b16">null</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;provider-app-name&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;metadata&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;metadataService&#34;</span>: <span style="color:#2aa198">&#34;{\&#34;dubbo\&#34;:{\&#34;version\&#34;:\&#34;1.0.0\&#34;,\&#34;dubbo\&#34;:\&#34;2.0.2\&#34;,\&#34;release\&#34;:\&#34;2.7.5\&#34;,\&#34;port\&#34;:\&#34;20881\&#34;}}&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;endpoints&#34;</span>: <span style="color:#2aa198">&#34;[{\&#34;port\&#34;:20880,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}]&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;storage-type&#34;</span>: <span style="color:#2aa198">&#34;local&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;revision&#34;</span>: <span style="color:#2aa198">&#34;6785535733750099598&#34;</span>, |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;registrationTimeUTC&#34;</span>: <span style="color:#2aa198">1583461240877</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;serviceType&#34;</span>: <span style="color:#2aa198">&#34;DYNAMIC&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;uriSpec&#34;</span>: <span style="color:#cb4b16">null</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h4 id="422-client--server-自行协商-rpc-方法信息">4.2.2 Client – Server 自行协商 RPC 方法信息</h4> |
| <p>在注册中心不再同步 RPC 服务信息后,服务自省在服务消费端和提供端之间建立了一条内置的 RPC 服务信息协商机制,这也是“服务自省”这个名字的由来。服务端实例会暴露一个预定义的 MetadataService RPC 服务,消费端通过调用 MetadataService 获取每个实例 RPC 方法相关的配置信息。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-8.png" alt="img8"></p> |
| <p>当前 MetadataService 返回的数据格式如下,</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo://192.168.0.102:20880/org.apache.dubbo.demo.DemoService?anyhost=true&amp;application=demo-provider&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=9585&amp;release=2.7.5&amp;side=provider&amp;timestamp=1583469714314&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo://192.168.0.102:20880/org.apache.dubbo.demo.HelloService?anyhost=true&amp;application=demo-provider&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=9585&amp;release=2.7.5&amp;side=provider&amp;timestamp=1583469714314&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo://192.168.0.102:20880/org.apache.dubbo.demo.WorldService?anyhost=true&amp;application=demo-provider&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=9585&amp;release=2.7.5&amp;side=provider&amp;timestamp=1583469714314&#34;</span> |
| </span></span><span style="display:flex;"><span>] |
| </span></span></code></pre></div><blockquote> |
| <p>熟悉 Dubbo 基于 RPC 服务粒度的服务发现模型的开发者应该能看出来,服务自省机制机制将以前注册中心传递的 URL 一拆为二:</p> |
| <ul> |
| <li>一部分和实例相关的数据继续保留在注册中心,如 ip、port、机器标识等。</li> |
| <li>另一部分和 RPC 方法相关的数据从注册中心移除,转而通过 MetadataService 暴露给消费端。</li> |
| </ul> |
| <p><strong>理想情况下是能达到数据按照实例、RPC 服务严格区分开来,但明显可以看到以上实现版本还存在一些数据冗余,有些数据也还未合理划分。尤其是 MetadataService 部分,其返回的数据还只是简单的 URL 列表组装,这些 URL其实是包含了全量的数据。</strong></p> |
| </blockquote> |
| <p>以下是服务自省的一个完整工作流程图,详细描述了服务注册、服务发现、MetadataService、RPC 调用间的协作流程。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-9.png" alt="img9"></p> |
| <ul> |
| <li>服务提供者启动,首先解析应用定义的“普通服务”并依次注册为 RPC 服务,紧接着注册内建的 MetadataService 服务,最后打开 TCP 监听端口。</li> |
| <li>启动完成后,将实例信息注册到注册中心(仅限 ip、port 等实例相关数据),提供者启动完成。</li> |
| <li>服务消费者启动,首先依据其要“消费的 provider 应用名”到注册中心查询地址列表,并完成订阅(以实现后续地址变更自动通知)。</li> |
| <li>消费端拿到地址列表后,紧接着对 MetadataService 发起调用,返回结果中包含了所有应用定义的“普通服务”及其相关配置信息。</li> |
| <li>至此,消费者可以接收外部流量,并对提供者发起 Dubbo RPC 调用</li> |
| </ul> |
| <blockquote> |
| <p>在以上流程中,我们只考虑了一切顺利的情况,但在更详细的设计或编码实现中,我们还需要严格约定一些异常场景下的框架行为。比如,如果消费者 MetadataService 调用失败,则在重试直到成功之前,消费者将不可以接收外部流量。</p> |
| </blockquote> |
| <h3 id="43-服务自省中的关键机制">4.3 服务自省中的关键机制</h3> |
| <h4 id="431-元数据同步机制">4.3.1 元数据同步机制</h4> |
| <p>Client 与 Server 间在收到地址推送后的配置同步是服务自省的关键环节,目前针对元数据同步有两种具体的可选方案,分别是:</p> |
| <ul> |
| <li>内建 MetadataService。</li> |
| <li>独立的元数据中心,通过中心化的元数据集群协调数据。</li> |
| </ul> |
| <p><strong>1. 内建 MetadataService</strong> |
| MetadataService 通过标准的 Dubbo 协议暴露,根据查询条件,会将内存中符合条件的“普通服务”配置返回给消费者。这一步发生在消费端选址和调用前。</p> |
| <p><strong>2. 元数据中心</strong> |
| 复用 2.7 版本中引入的元数据中心,provider 实例启动后,会尝试将内部的 RPC 服务组织成元数据的格式同步到元数据中心,而 consumer 则在每次收到注册中心推送更新后,主动查询元数据中心。</p> |
| <blockquote> |
| <p>注意 consumer 端查询元数据中心的时机,是等到注册中心的地址更新通知之后。也就是通过注册中心下发的数据,我们能明确的知道何时某个实例的元数据被更新了,此时才需要去查元数据中心。</p> |
| </blockquote> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-10.png" alt="img10"></p> |
| <h4 id="432-rpc-服务-----应用映射关系">4.3.2 RPC 服务 &lt; - &gt; 应用映射关系</h4> |
| <p>回顾上文讲到的注册中心关于“应用 - 实例列表”结构的数据组织形式,这个变动目前对开发者并不是完全透明的,业务开发侧会感知到查询/订阅地址列表的机制的变化。具体来说,相比以往我们基于 RPC 服务来检索地址,现在 consumer 需要通过指定 provider 应用名才能实现地址查询或订阅。</p> |
| <p>老的 Consumer 开发与配置示例:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 框架直接通过 RPC Service 1/2/N 去注册中心查询或订阅地址列表 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service 1&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service 2&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service N&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>新的 Consumer 开发与配置示例:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 框架需要通过额外的 provided-by=&#34;provider-app-x&#34; 才能在注册中心查询或订阅到地址列表 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181?registry-type=service&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service 1&#34;</span> provided-by=<span style="color:#2aa198">&#34;provider-app-x&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service 2&#34;</span> provided-by=<span style="color:#2aa198">&#34;provider-app-x&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service N&#34;</span> provided-by=<span style="color:#2aa198">&#34;provider-app-y&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>以上指定 provider 应用名的方式是 Spring Cloud 当前的做法,需要 consumer 端的开发者显示指定其要消费的 provider 应用。</p> |
| <p>以上问题的根源在于注册中心不知道任何 RPC 服务相关的信息,因此只能通过应用名来查询。</p> |
| <p>为了使整个开发流程对老的 Dubbo 用户更透明,同时避免指定 provider 对可扩展性带来的影响(参见下方说明),我们设计了一套 <code>RPC 服务到应用名</code>的映射关系,以尝试在 consumer 端自动完成 RPC 服务到 provider 应用名的转换。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-11.png" alt="img11"></p> |
| <blockquote> |
| <p>Dubbo 之所以选择建立一套“接口-应用”的映射关系,主要是考虑到 service - app 映射关系的不确定性。一个典型的场景即是应用/服务拆分,如上面提到的配置<code>&lt;dubbo:reference interface=&quot;RPC Service 2&quot; provided-by=&quot;provider-app-x&quot; /&gt;</code>,PC Service 2 是定义于 provider-app-x 中的一个服务,未来它随时可能会被开发者分拆到另外一个新的应用如 provider-app-x-1 中,这个拆分要被所有的 PC Service 2 消费方感知到,并对应用进行修改升级,如改为<code>&lt;dubbo:reference interface=&quot;RPC Service 2&quot; provided-by=&quot;provider-app-x-1&quot; /&gt;</code>,这样的升级成本不可否认还是挺高的。 |
| 到底是 Dubbo 框架帮助开发者透明的解决这个问题,还是交由开发者自己去解决,当然这只是个策略选择问题,并且 Dubbo 2.7.5+ 版本目前是都提供了的。其实我个人更倾向于交由业务开发者通过组织上的约束来做,这样也可进一步降低 Dubbo 框架的复杂度,提升运行态的稳定性。</p> |
| </blockquote> |
| <h2 id="5-总结与展望">5 总结与展望</h2> |
| <p>应用级服务发现机制是 Dubbo 面向云原生走出的重要一步,它帮 Dubbo 打通了与其他微服务体系之间在地址发现层面的鸿沟,也成为 Dubbo 适配 Kubernetes Native Service 等基础设施的基础。我们期望 Dubbo 在新模型基础上,能继续保留在编程易用性、服务治理能力等方面强大的优势。但是我们也应该看到应用粒度的模型一方面带来了新的复杂性,需要我们继续去优化与增强;另一方面,除了地址存储与推送之外,应用粒度在帮助 Dubbo 选址层面也有进一步挖掘的潜力。</p></description></item><item><title>Blog: Dubbo-Api-Docs -- Apache Dubbo文档展示&测试工具</title><link>https://dubbo.apache.org/zh-cn/blog/2020/12/22/dubbo-api-docs--apache-dubbo%E6%96%87%E6%A1%A3%E5%B1%95%E7%A4%BA%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/</link><pubDate>Tue, 22 Dec 2020 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2020/12/22/dubbo-api-docs--apache-dubbo%E6%96%87%E6%A1%A3%E5%B1%95%E7%A4%BA%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/</guid><description> |
| <h1 id="dubbo-api-docs">Dubbo-Api-Docs</h1> |
| <h2 id="背景">背景</h2> |
| <p>Swagger 是一个规范和完整的前端框架,用于生成,描述,调用和可视化 RESTful 风格的 Web 服务. |
| Swagger 规范也逐渐发展成为了 OpenAPI 规范.</p> |
| <p>Springfox 是一个集成了Swagger,基于 Sring MVC/Spring Webflux 实现的一个 Swagger 描述文件生成框架,通过使用它定义的 |
| 一些描述接口的注解自动生成Swagger的描述文件, 使 Swagger 能够展示并调用接口.</p> |
| <p>相信很多人都听说和使用过Swagger和Springfox, 这里就不再赘述了.</p> |
| <p>Dubbo-Admin中有接口测试功能,但是缺少接口描述的文档,所以该测试功能比较适合接口开发人员用于测试接口.而其他人想要使用该功能就必须 |
| 先通过接口开发者编写的文档或者其他方式了解清楚接口信息才能使用该功能测试接口. |
| Dubbo这边有没有集合文档展示和测试功能,能不用写文档就能把接口直接给调用方,类似Swagger/Springfox的工具呢? |
| 之前做过一些调研,找到一些类似的工具:</p> |
| <ul> |
| <li>有些是基于Springfox做的,直接一个文本域放JSON, 与目前Admin中的测试功能大同小异</li> |
| <li>有些是直接基于Swagger的Java版OpenApi规范生成工具做的,能把一些基础数据类型的简单参数作为表单项展示</li> |
| </ul> |
| <p>它们都有一个共同点: 会把你的提供者变为Web项目. 当然有些提供者是通过web容器加载启动的,甚至也有和web工程在一起的,那就无所谓了. |
| 但也有非web的提供者. 为了文档我得把它变为web项目吗?(还要引入一堆Web框架的依赖?比如Spring MVC)或者说生产环境打包时删除它的引用 |
| 和代码里的相关注解? 有没有简单点的方式呢?</p> |
| <p>OpenAPI中没有RPC的规范,Swagger是OpenAPI的实现,所以也不支持RPC相关调用.Springfox是通过Swagger实现的 RESTful API的工具, |
| 而RESTful又是基于Web的,Dubbo没法直接使用.我们最终选择了自己实现:</p> |
| <ul> |
| <li>提供一些描述接口信息的简单注解</li> |
| <li>在提供者启动时解析注解并缓存解析结果</li> |
| <li>在提供者增加几个Dubbo-Api-Docs使用的获取接口信息的接口</li> |
| <li>在Dubbo Admin侧通过Dubbo泛化调用实现Http方式调用Dubbo接口的网关</li> |
| <li>在Dubbo Admin侧实现接口信息展示和调用接口功能</li> |
| <li>下列情况中的参数直接展示为表单项,其他的展示为JSON: |
| <ul> |
| <li>方法参数为基础数据类型的</li> |
| <li>方法参数为一个Bean,Bena中属性为基础数据类型的</li> |
| </ul> |
| </li> |
| <li>很少的第三方依赖,甚至大部分都是你项目里本身就使用的</li> |
| <li>可以通过profile决定是否加载, 打包时简单的修改profile就能区分生产和测试,甚至profile你本来就使用了</li> |
| </ul> |
| <blockquote> |
| <p>今天,我很高兴的宣布: Dubbo 用户也可以享受类似Swagger的体验了 &ndash; Dubbo-Api-Docs发布了.</p> |
| </blockquote> |
| <h2 id="简介">简介</h2> |
| <p>Dubbo-Api-Docs 是一个展示dubbo接口文档,测试接口的工具.</p> |
| <p>使用 Dubbo-Api-Docs 分为两个主要步骤:</p> |
| <ol> |
| <li>在dubbo项目引入Dubbo-Api-Docs 相关jar包,并增加类似Swagger的注解.</li> |
| <li>在 Dubbo-Admin 中查看接口描述并测试.</li> |
| </ol> |
| <p>通过以上两个步骤即可享受类似Swagger的体验, 并且可以在生产环境中关闭Dubbo-Api-Docs的扫描.</p> |
| <p>Dubbo-Api-Docs 目前通过直连服务节点的方式获取该服务的接口列表. 测试接口时可以直连也可以通过注册中心.未来会增加通过注册中心获取服务列表的方式.并根据Dubbo的升级规划增加新的功能支持.也会根据社区的需求增加功能.</p> |
| <p>Dubbo-Api-Docs 会在服务提供者启动完毕后扫描docs相关注解并将处理结果缓存.并增加一些Dubbo-Api-Docs相关的Dubbo提供者接口. 缓存的数据在将来可能会放到Dubbo元数据中心中.</p> |
| <h2 id="当前版本-2781">当前版本: 2.7.8.1</h2> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.dubbo<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>dubbo-api-docs-annotations<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${dubbo-version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.dubbo<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>dubbo-api-docs-core<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${dubbo-version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span></code></pre></div><h2 id="快速入门">快速入门</h2> |
| <h3 id="1dubbo提供者项目的方法参数中加上-dubbo-api-docs-注解">1.dubbo提供者项目的方法参数中加上 Dubbo-Api-Docs 注解</h3> |
| <ul> |
| <li>如果 dubbo提供者的接口和方法参数在一个单独的jar项目中,则在该项目中引入: dubbo-api-docs-annotations</li> |
| <li>dubbo提供者项目引入 dubbo-api-docs-core</li> |
| <li>在提供者项目的项目启动类(标注了@SpringBootApplication的类)或者配制类(标注了@Configuration的类)中增加注解 @EnableDubboApiDocs 以启用Dubbo Api Docs功能</li> |
| </ul> |
| <blockquote> |
| <p>为避免增加生产环境中的资源占用, 建议单独创建一个配制类用于启用Dubbo-Api-Docs, 并配合 @Profile(&ldquo;dev&rdquo;) 注解使用 |
| 当然, Dubbo-Api-Docs 仅在项目启动时多消耗了点CPU资源, 并使用了一点点内存用于缓存, 将来会考虑将缓存中的内容放到元数据中心.</p> |
| </blockquote> |
| <h4 id="下面以dubbo-api-docs-exampleshttpsgithubcomapachedubbo-spi-extensionstree27xdubbo-api-docsdubbo-api-docs-examples项目中的部分服务接口为例">下面以<a href="https://github.com/apache/dubbo-spi-extensions/tree/2.7.x/dubbo-api-docs/dubbo-api-docs-examples">dubbo-api-docs-examples</a>项目中的部分服务接口为例:</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone -b 2.7.x https://github.com/apache/dubbo-spi-extensions.git |
| </span></span></code></pre></div><p>进入 dubbo-spi-extensions/dubbo-api-docs/dubbo-api-docs-examples 目录</p> |
| <p>dubbo-api-docs-examples 中有两个子模块:</p> |
| <ul> |
| <li>examples-api: 一个jar包项目,其中包含服务的接口和接口参数Bean</li> |
| <li>examples-provider: 提供者服务端,包含spring boot启动器和服务的实现</li> |
| </ul> |
| <p>下面我们在这两个子模块中增加Dubbo-Api-Docs</p> |
| <blockquote> |
| <p>examples-api:</p> |
| </blockquote> |
| <p>maven引入:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.dubbo<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>dubbo-api-docs-annotations<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>2.7.8<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span></code></pre></div><p>org.apache.dubbo.apidocs.examples.params 中有两个Bean,我们来为它们添加docs注解</p> |
| <ul> |
| <li>QuickStartRequestBean 作为参数Bean, 添加 @RequestParam</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">QuickStartRequestBean</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@RequestParam</span><span style="color:#719e07">(</span>value <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;You name&#34;</span><span style="color:#719e07">,</span> required <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> description <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;please enter your full name&#34;</span><span style="color:#719e07">,</span> example <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Zhang San&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String name<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@RequestParam</span><span style="color:#719e07">(</span>value <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;You age&#34;</span><span style="color:#719e07">,</span> defaultValue <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;18&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">int</span> age<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@RequestParam</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Are you a main?&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">boolean</span> man<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// getter/setter略... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ul> |
| <li>QuickStartRespBean 作为响应Bean,添加 @ResponseProperty</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">QuickStartRespBean</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@ResponseProperty</span><span style="color:#719e07">(</span>value <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Response code&#34;</span><span style="color:#719e07">,</span> example <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;500&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">int</span> code<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@ResponseProperty</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Response message&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String msg<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// getter/setter略... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>由于我们只挑选了部分接口作为演示,到此这些接口涉及的docs注解添加完毕</p> |
| <blockquote> |
| <p>examples-provider:</p> |
| </blockquote> |
| <p>maven引入:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.dubbo<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>dubbo-api-docs-core<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>2.7.8<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span></code></pre></div><p>我们挑选一个接口作为演示:</p> |
| <p>org.apache.dubbo.apidocs.examples.api.impl.QuickStartDemoImpl 中的 quickStart 方法</p> |
| <p>QuickStartDemoImpl 实现了 api包中的 org.apache.dubbo.apidocs.examples.api.IQuickStartDemo 接口</p> |
| <ul> |
| <li>在 QuickStartDemoImpl 中:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@DubboService</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@ApiModule</span><span style="color:#719e07">(</span>value <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;quick start demo&#34;</span><span style="color:#719e07">,</span> apiInterface <span style="color:#719e07">=</span> IQuickStartDemo<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> version <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;v0.1&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">QuickStartDemoImpl</span> <span style="color:#268bd2">implements</span> IQuickStartDemo <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@ApiDoc</span><span style="color:#719e07">(</span>value <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;quick start demo&#34;</span><span style="color:#719e07">,</span> version <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;v0.1&#34;</span><span style="color:#719e07">,</span> description <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;this api is a quick start demo&#34;</span><span style="color:#719e07">,</span> responseClassDescription<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;A quick start response bean&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> QuickStartRespBean <span style="color:#268bd2">quickStart</span><span style="color:#719e07">(</span><span style="color:#268bd2">@RequestParam</span><span style="color:#719e07">(</span>value <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;strParam&#34;</span><span style="color:#719e07">,</span> required <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> String strParam<span style="color:#719e07">,</span> QuickStartRequestBean beanParam<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> QuickStartRespBean<span style="color:#719e07">(</span>200<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;hello &#34;</span> <span style="color:#719e07">+</span> beanParam<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, &#34;</span> <span style="color:#719e07">+</span> beanParam<span style="color:#719e07">.</span>toString<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>到此docs相关注解已添加完毕,下面我们来开启 Dubbo-Api-Docs. 新增一个配制类,位置任意,只要能被spring boot扫描到就行.</p> |
| <p>我们在 org.apache.dubbo.apidocs.examples.cfg 包中新增一个配制类 DubboDocConfig :</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Configuration</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Profile</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dev&#34;</span><span style="color:#719e07">)</span> <span style="color:#586e75">// 配合 Profile 一起使用, 在 profile 为 dev 时才会加载该配制类 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@EnableDubboApiDocs</span> <span style="color:#586e75">// 开启 Dubbo-Api-Docs |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DubboDocConfig</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>到此 Dubbo-Api-Docs 相关的东西已经添加完毕. |
| <a href="https://github.com/apache/dubbo-spi-extensions/tree/2.7.x/dubbo-api-docs/dubbo-api-docs-examples">dubbo-api-docs-examples</a> |
| 中有更多更为详尽的例子.下文中有注解的详细说明.下面我们来看一下增加 Dubbo-Api-Docs 后的效果图.</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/api-docs/quickStart.png" alt="demoApi2"></p> |
| <h3 id="2启动提供者项目">2.启动提供者项目</h3> |
| <ul> |
| <li>示例使用nacos作为注册中心,<a href="https://nacos.io">下载并启动nacos</a></li> |
| <li>在上面的例子中,我们启动 examples-provider 项目中的 org.apache.dubbo.apidocs.examples.ExampleApplication. |
| 在examples-provider目录中:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mvn spring-boot:run |
| </span></span></code></pre></div><h3 id="3下载-dubbo-admin">3.下载 dubbo-admin</h3> |
| <p><a href="https://github.com/apache/dubbo-admin">dubbo-admin仓库</a></p> |
| <blockquote> |
| <p>dubbo-admin 需要下载 develop 分支源码启动</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone -b develop https://github.com/apache/dubbo-admin.git |
| </span></span></code></pre></div></blockquote> |
| <h3 id="4启动访问-dubbo-admin">4.启动访问 dubbo-admin</h3> |
| <p>参考 dubbo-admin 里的说明启动:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>1. 在 dubbo-admin-server/src/main/resources/application.properties 中修改注册中心地址 |
| </span></span><span style="display:flex;"><span>2. 编译 mvn clean package |
| </span></span><span style="display:flex;"><span>3. 启动: |
| </span></span><span style="display:flex;"><span>mvn --projects dubbo-admin-server spring-boot:run |
| </span></span><span style="display:flex;"><span>或者 |
| </span></span><span style="display:flex;"><span>cd dubbo-admin-distribution/target; java -jar dubbo-admin-0.1.jar |
| </span></span><span style="display:flex;"><span>4. 浏览器访问: http://localhost:8080 |
| </span></span><span style="display:flex;"><span>5. 默认帐号密码都是: root |
| </span></span></code></pre></div><h3 id="5进入接口文档模块">5.进入&quot;接口文档&quot;模块</h3> |
| <ul> |
| <li>在&quot;dubbo提供者IP&quot;和&quot;dubbo提供者端口&quot;中分别输入提供者所在机器IP和端口, 点击右侧 &quot; 加载接口列表&quot; 按钮</li> |
| <li>左侧接口列表中加载出接口列表,点击任意接口,右边展示出该接口信息及参数表单.</li> |
| <li>填入表单内容后,点击最下方测试按钮</li> |
| <li>响应部分展示了响应示例及实际响应结果</li> |
| </ul> |
| <h2 id="源码仓库">源码仓库</h2> |
| <p>Dubbo-Api-Docs 根据功能拆分,分别在两个仓库中:</p> |
| <h3 id="dubbo-spi-extensions">dubbo-spi-extensions</h3> |
| <blockquote> |
| <p><a href="https://github.com/apache/dubbo-spi-extensions">dubbo-spi-extensions仓库地址</a></p> |
| </blockquote> |
| <p>该仓库存放dubbo的一些非核心功能的扩展, Dubbo-Api-Docs 作为该仓库中的一个子模块,由于该仓库属于Dubbo 3.0中规划的一部分,而Dubbo-Api-Docs是基于Dubbo 2.7.x 开发的,所以在该仓库中增加了<a href="https://github.com/apache/dubbo-spi-extensions/tree/2.7.x/dubbo-api-docs">2.7.x分支,Dubbo-Api-Docs就在该分支下</a>. |
| 该仓库中包含了 Dubbo-Api-Docs 的文档相关注解、注解扫描能力和使用示例:</p> |
| <ul> |
| <li>dubbo-api-docs-annotations: 文档生成的相关注解.考虑到实际情况中 dubbo api 的接口类和接口参数会规划为一个单独的jar包, 所以注解也独立为一个jar包.本文后面会对注解做详细说明.</li> |
| <li>dubbo-api-docs-core: 负责解析注解,生成文档信息并缓存. 前面提到的Dubbo-Api-Docs相关接口也在该包中</li> |
| <li>dubbo-api-docs-examples: 使用示例</li> |
| </ul> |
| <h3 id="dubbo-admin">Dubbo-Admin</h3> |
| <blockquote> |
| <p><a href="https://github.com/KeRan213539/dubbo-admin">Dubbo-Admin仓库地址</a></p> |
| </blockquote> |
| <p>文档的展示及测试放在了 dubbo admin 项目中</p> |
| <h2 id="注解说明">注解说明</h2> |
| <ul> |
| <li>@EnableDubboApiDocs: 配制注解, 启用 dubbo api docs 功能</li> |
| <li>@ApiModule: 类注解, dubbo接口模块信息,用于标注一个接口类模块的用途 |
| <ul> |
| <li>value: 模块名称</li> |
| <li>apiInterface: 提供者实现的接口</li> |
| <li>version: 模块版本</li> |
| </ul> |
| </li> |
| <li>@ApiDoc: 方法注解, dubbo 接口信息,用于标注一个接口的用途 |
| <ul> |
| <li>value: 接口名称</li> |
| <li>description: 接口描述(可使用html标签)</li> |
| <li>version: 接口版本</li> |
| <li>responseClassDescription: 响应的数据的描述</li> |
| </ul> |
| </li> |
| <li>@RequestParam: 类属性/方法参数注解,标注请求参数 |
| <ul> |
| <li>value: 参数名</li> |
| <li>required: 是否必传参数</li> |
| <li>description: 参数描述</li> |
| <li>example: 参数示例</li> |
| <li>defaultValue: 参数默认值</li> |
| <li>allowableValues: 允许的值,设置该属性后界面上将对参数生成下拉列表 |
| <ul> |
| <li>注:使用该属性后将生成下拉选择框</li> |
| <li>boolean 类型的参数不用设置该属性,将默认生成 true/false 的下拉列表</li> |
| <li>枚举类型的参数会自动生成下拉列表,如果不想开放全部的枚举值,可以单独设置此属性.</li> |
| </ul> |
| </li> |
| </ul> |
| </li> |
| <li>@ResponseProperty: 类属性注解, 标注响应参数 |
| <ul> |
| <li>value: 参数名</li> |
| <li>example: 示例</li> |
| </ul> |
| </li> |
| </ul> |
| <h2 id="使用注意">使用注意</h2> |
| <ul> |
| <li>响应bean(接口的返回类型)支持自定义泛型, 但只支持一个泛型占位符</li> |
| <li>关于Map的使用:Map的key只能用基本数据类型.如果Map的key不是基础数据类型,生成的 就不是标准json格式,会出异常</li> |
| <li>接口的同步/异步取自 org.apache.dubbo.config.annotation.Service#async / org.apache.dubbo.config.annotation.DubboService#async</li> |
| </ul> |
| <h2 id="示例说明">示例说明</h2> |
| <p><a href="https://github.com/apache/dubbo-spi-extensions/tree/2.7.x/dubbo-api-docs">dubbo-spi-extensions / Dubbo-Api-Docs</a> 中的 dubbo-api-docs-examples 目录中为示例工程:</p> |
| <ul> |
| <li>examples-api: jar包项目,包含服务提供者的接口类及参数Bean</li> |
| <li>examples-provider: 使用 dubbo-spring-boot-starter 的提供者项目, 注册中心使用 nacos</li> |
| <li>examples-provider-sca: 使用 spring-cloud-starter-dubbo 的提供者项目, 注册中心使用 nacos</li> |
| </ul> |
| <h3 id="示例使用步骤">示例使用步骤</h3> |
| <ol> |
| <li>示例使用nacos作为注册中心,<a href="https://nacos.io">下载并启动nacos</a></li> |
| <li>任意启动 examples-provider 和 examples-provider-sca 中的任意一个,当然也可以两个都启动. examples-provider 使用 20881端口 examples-provider-sca 使用20882端口.两个项目都是spring boot项目,启动类在 org.apache.dubbo.apidocs.examples 包下.</li> |
| <li>启动 <a href="https://github.com/KeRan213539/dubbo-admin">Dubbo-Admin</a>, 浏览器访问: http://localhost:8080</li> |
| <li>进入 dubbo-admin 中的 &ldquo;接口文档&quot;模块</li> |
| <li>在&quot;dubbo提供者IP&quot;和&quot;dubbo提供者端口&quot;中分别输入提供者所在机器IP和端口, 点击右侧 &quot; 加载接口列表&rdquo; 按钮</li> |
| <li>左侧接口列表中加载出接口列表,点击任意接口,右边展示出该接口信息及参数表单.</li> |
| <li>填入表单内容后,点击最下方测试按钮</li> |
| <li>响应部分展示了响应示例及实际响应结果</li> |
| </ol></description></item><item><title>Blog: Dubbo测试验证</title><link>https://dubbo.apache.org/zh-cn/blog/2019/12/02/dubbo%E6%B5%8B%E8%AF%95%E9%AA%8C%E8%AF%81/</link><pubDate>Mon, 02 Dec 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/12/02/dubbo%E6%B5%8B%E8%AF%95%E9%AA%8C%E8%AF%81/</guid><description> |
| <p>除了线上常规的使用场景以外,我们在日常使用中还需要一些特定的使用方式,比如对正在开发的功能进行验证测试,比如单独调用某台机器的服务,这篇文章就来介绍一下这些场景下的使用方式。</p> |
| <h3 id="只订阅">只订阅</h3> |
| <p>为方便开发测试,经常会在线下共用一个所有服务可用的注册中心,这时,如果一个正在开发中的服务提供者注册,可能会影响消费者不能正常运行。</p> |
| <p>可以让服务提供者开发方,只订阅服务(开发的服务可能依赖其它服务),而不注册正在开发的服务,通过直连测试正在开发的服务。 <br> |
| <img src="https://dubbo.apache.org/imgs/blog/subscribe-only.jpg" alt="subscribe-only"> |
| 禁用注册配置</p> |
| <pre><code>&lt;dubbo:registry address=&quot;10.20.153.10:9090&quot; register=&quot;false&quot; /&gt; |
| </code></pre> |
| <p>或者</p> |
| <pre><code>&lt;dubbo:registry address=&quot;10.20.153.10:9090?register=false&quot; /&gt; |
| </code></pre> |
| <h3 id="指定ip调用">指定IP调用</h3> |
| <p>在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表<br> |
| <img src="https://dubbo.apache.org/imgs/blog/dubbo-directly.jpg" alt="subscribe-only"></p> |
| <p>可以通过以下几种配置来指定IP调用</p> |
| <ul> |
| <li>XML 配置: 如果是线上需求需要点对点,可在 <a href="dubbo:reference">dubbo:reference</a> 中配置 url 指向提供者,将绕过注册中心,多个地址用分号隔开,配置如下: |
| <code>&lt;dubbo:reference id=&quot;xxxService&quot; interface=&quot;com.alibaba.xxx.XxxService&quot; url=&quot;dubbo://localhost:20890&quot; /&gt;</code></li> |
| <li>通过-D参数指定: 在 JVM 启动参数中加入-D参数映射服务地址,如:<code>java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890</code></li> |
| <li>通过文件映射: 如果服务比较多,也可以用文件映射,用 -Ddubbo.resolve.file 指定映射文件路径,此配置优先级高于 <a href="dubbo:reference">dubbo:reference</a> 中的配置,如: |
| <code>java -Ddubbo.resolve.file=xxx.properties</code><br> |
| 然后在映射文件 xxx.properties 中加入配置,其中 key 为服务名,value 为服务提供者 URL:<code>com.alibaba.xxx.XxxService=dubbo://localhost:20890</code></li> |
| </ul> |
| <h3 id="回声测试">回声测试</h3> |
| <h4 id="使用方式">使用方式</h4> |
| <p>回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。</p> |
| <p>所有服务自动实现 EchoService 接口,只需将任意服务引用强制转型为 EchoService,即可使用。</p> |
| <p>Spring 配置:</p> |
| <pre><code>&lt;dubbo:reference id=&quot;memberService&quot; interface=&quot;com.xxx.MemberService&quot; /&gt; |
| </code></pre> |
| <p>代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>// 远程服务引用 |
| </span></span><span style="display:flex;"><span>MemberService memberService = ctx.getBean(&#34;memberService&#34;); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>EchoService echoService = (EchoService) memberService; // 强制转型为EchoService |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>// 回声测试可用性 |
| </span></span><span style="display:flex;"><span>String status = echoService.$echo(&#34;OK&#34;); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>assert(status.equals(&#34;OK&#34;)); |
| </span></span></code></pre></div><h4 id="实现原理">实现原理</h4> |
| <p>我们在实现,注册服务的时候,并没有配置EchoService这个接口,为什么可以直接使用呢?原来是Dubbo在生成proxy的时候,已经实现了<code>EchoService这个接口</code></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T <span style="color:#268bd2">getProxy</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;[]</span> interfaces <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> String config <span style="color:#719e07">=</span> invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">().</span>getParameter<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;interfaces&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> config<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> types <span style="color:#719e07">=</span> Constants<span style="color:#719e07">.</span>COMMA_SPLIT_PATTERN<span style="color:#719e07">.</span>split<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>types <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> types<span style="color:#719e07">.</span>length <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> interfaces <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Class<span style="color:#719e07">&lt;?&gt;[</span>types<span style="color:#719e07">.</span>length <span style="color:#719e07">+</span> 2<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> interfaces<span style="color:#719e07">[</span>0<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> invoker<span style="color:#719e07">.</span>getInterface<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> interfaces<span style="color:#719e07">[</span>1<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> EchoService<span style="color:#719e07">.</span>class<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0<span style="color:#719e07">;</span> i <span style="color:#719e07">&lt;</span> types<span style="color:#719e07">.</span>length<span style="color:#719e07">;</span> i<span style="color:#719e07">++)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> interfaces<span style="color:#719e07">[</span>i <span style="color:#719e07">+</span> 1<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> ReflectUtils<span style="color:#719e07">.</span>forName<span style="color:#719e07">(</span>types<span style="color:#719e07">[</span>i<span style="color:#719e07">]);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>interfaces <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> interfaces <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Class<span style="color:#719e07">&lt;?&gt;[]{</span>invoker<span style="color:#719e07">.</span>getInterface<span style="color:#719e07">(),</span> EchoService<span style="color:#719e07">.</span>class<span style="color:#719e07">};</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getProxy<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> interfaces<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>通过这种方式,任何bean都可以被转换成EchoService的实例,但是并没有实现<code>$echo</code>这个方法,这里,Dubbo使用filter机制做了处理:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">EchoFilter</span> <span style="color:#268bd2">implements</span> Filter <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Result <span style="color:#268bd2">invoke</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;?&gt;</span> invoker<span style="color:#719e07">,</span> Invocation inv<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>inv<span style="color:#719e07">.</span>getMethodName<span style="color:#719e07">().</span>equals<span style="color:#719e07">(</span>Constants<span style="color:#719e07">.</span>$ECHO<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> inv<span style="color:#719e07">.</span>getArguments<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> inv<span style="color:#719e07">.</span>getArguments<span style="color:#719e07">().</span>length <span style="color:#719e07">==</span> 1<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> RpcResult<span style="color:#719e07">(</span>inv<span style="color:#719e07">.</span>getArguments<span style="color:#719e07">()[</span>0<span style="color:#719e07">]);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">.</span>invoke<span style="color:#719e07">(</span>inv<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在经过EchoFilter.invoke方法时,如果调用的是<code>$echo</code>,会中断当前的调用过程,直接返回<code>$echo</code>的入参,否则会继续执行Filter链。<br> |
| 通过动态代理和EchoFilter机制,使得回声测试的整个过程对用户透明,不需要做任何额外的配置,直接调用即可。</p></description></item><item><title>Blog: Dubbo 在 Service Mesh 下的思考和方案</title><link>https://dubbo.apache.org/zh-cn/blog/2019/11/30/dubbo-%E5%9C%A8-service-mesh-%E4%B8%8B%E7%9A%84%E6%80%9D%E8%80%83%E5%92%8C%E6%96%B9%E6%A1%88/</link><pubDate>Sat, 30 Nov 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/11/30/dubbo-%E5%9C%A8-service-mesh-%E4%B8%8B%E7%9A%84%E6%80%9D%E8%80%83%E5%92%8C%E6%96%B9%E6%A1%88/</guid><description> |
| <h2 id="开头">开头</h2> |
| <p>Service Mesh这个“热”词是2016年9月被“造”出来,而今年2018年更是被称为service Mesh的关键之年,各家大公司都希望能在这个思潮下领先一步。今天我也分享阿里中间件在这方面的观点,思考和实践。考虑到有些人没了解过Dubbo(集团内以HSF为主)和Servicemesh,先简单介绍下这两个词。Dubbo应该是国内最受欢迎的远程服务框架,在Github上有超过2w的star数,也是阿里分布式架构互联互通的核心所在。跟Dubbo一样,servicemesh也是面向服务互联互通这一问题域,是云原生技术栈的核心之一;大家可以简单理解service mesh就是云原生组织定义的微服务架构解决理念。Dubbo是实现框架,融入service mesh理念就是我们今天分享的。</p> |
| <h2 id="现状和挑战">现状和挑战</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbomesh/1.png" alt="1.png | center | 826x206"></p> |
| <p>当前Dubbo支撑的阿里分布式应用内支撑万级别的应用数,运行在20多万的服务器实例上,每天调用量是万亿级别,这应该是国内最大的分布式应用集群。</p> |
| <p>挑战主要来自三方面</p> |
| <ul> |
| <li>首先, 数以万计的应用意味着有以十万级的服务,理顺错综复杂的服务拓扑关系,甚至及时诊断某个异常调用链路,需要考虑海量数据的拉取分析,是非常有挑战的,阿里通过EagleEye鹰眼链路系统提供可观察性和治理能力来解决;</li> |
| <li>第二个挑战是机房级别容灾,阿里的机房是分布在天南海北,大家可以想象横跨数千公里的网络延迟会造成服务互通很大的影响,所以在保证一定恢复时间和一定数据容错的情况下做异地多活是有巨大挑战,阿里通过支持异地多活的单元化架构解决。</li> |
| <li>第三个挑战是阿里业务众多,尤其像阿里生态中的高德,UC,优酷等所使用的开发语言跟淘系Java是不一样的,比如PHP,C,Nodejs,Dart等,要维护多个版本并保证各版本具有同样的功能是成本比较高的;这个挑战在云原生的新一代理念下更具挑战,毕竟。今天主题跟第三个挑战是息息相关,能解决一定的问题。</li> |
| </ul> |
| <p>这里讲个大鱼吃小鱼的故事来简单理解下云原生:软件会吃掉这个世界,也就是信息化不可避免,而开源会吃掉软件,最终云原生会吃掉开源。这正代表了云原生理念的颠覆性,从商业软件到开源到云原生,环环相套,以体系化和层次化的方式推荐各个方面的开源方案和标准,这会极大降低企业级架构服务的技术门槛,是企业信息化之路的一大利好,当然也是进化方向。这个故事跟今天的主题&ndash;开发者定义软件未来,是非常契合,也就是说这个趋势至少在企业级软件服务领域正在发生。云原生:Cloud Native is Patterns with A complete and trusted tool kit for modern architectures。</p> |
| <p><span data-type="color" style="color:white">Service Mesh的典型方案</span></p> |
| <h2 id="service-mesh的典型方案">Service Mesh的典型方案</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbomesh/2.png" alt="2.png | center | 826x206" title=""></p> |
| <p>讲完故事,回到servicemesh。</p> |
| <p>传统形态下SDK代表着一个特定语言的库,由应用和微服务框架共处一进程内,在发布升级中共享生命周期。比较典型的代表是Twitter的finagle,Google的stubby/grpc,阿里巴巴的HSF/Dubbo.</p> |
| <p>Serviemesh下推荐是右边Sidecar方案,Sidecar方案没有引入新的功能,只是改变了原有功能的位置,以独立的应用来存在,大家可以暂时以nginx来理解其网络代理能力也可以。</p> |
| <p>在这张图中希望大家关注两个信息, 1)所有的sidecar形成逻辑网络被称为数据面,是业务服务的链路中是强依赖节点,承载了业务数据互联互通的基础;传统的ops管控服务被称为控制面,这部分跟传统是大同小异。 2)在sidecar形态下,网络会增加两跳,即应用与sidecar之间,他们之间的数据互通也是基于协议规范。后面会详细讲。</p> |
| <h2 id="sidecar模式的优劣">Sidecar模式的优劣</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbomesh/3.png" alt="3.png | center | 826x206" title=""></p> |
| <p>接下来从开发和运维两个阶段来分开比较。</p> |
| <ul> |
| <li>多语言支持方面,既然sidecar是独立应用,用最合适的一种语言开发完成即可,就避免了需要针对不同语言的应用场景做不同的版本开发。当前阿里选择基于C语言的Envoy做二次开发来追求最小的footprint和性能,当然也曾经历一些弯路,比如曾经用Java开发过一个sidecar,但最终由于引入JRE体量大和GC带来的抖动等问题证明不可行。有必要强调的是:这里说的是sidecar自身开发现在避免了多语言多版本的问题,而真要支持任意服务自由采用任意语言实现这一理想,是需要站在从业务到数据面再到业务的整个链路上的数据交互做思考。</li> |
| <li>性能方面,sidecar情形下由于会增加两跳,这两跳是业务应用与sidecar的两个进程之间的调用,这是本机,即便是经过优化,也是会增加进程切换以及数据转换的开销。经过我们的优化测试,在正常的业务访问下,相比SDK形态下最多增加1毫秒的开销,这在大多数业务情形下是基本无感知无影响。</li> |
| <li>再看运维阶段的比较,一般SDK形态的服务框架都是只关心开发的诉求,对于如何运维都是不关心,而软件生命周期中运维是最长的,如何从中间件角度解决更多的运维问题是非常有意义的。阿里的中间件经常需要升级,以库的形式升级时就需要业务方应用重新打包,这个推动业务方变更的方式是比较被动,而且周期很长。</li> |
| <li>当以镜像为基本原子单位进行发布部署时,阿里的中间件SDK体量大概是200兆,需要与业务一起打包,这样在业务应用升级时让分发的包就显得笨重,时效性相比sidecar形态就差一截。</li> |
| </ul> |
| <p>稍微总结下,sidecar具有两个明显优势,一个是多语言开发维护成本低 ,另一个是独立升级,当然代价是需要增加一点点的网络延迟。至此大家是不是觉得Sidecar基本完美? 别着急,需要大家再思考一个问题:SDK模式下中间件组件会随应用一起发布,拥有完全一致的生命周期;而在sidecar模式下,如何管理sidecar的生命周期?这里可以拿无线耳机来举个例子,无线耳机是独立了,但必须独立电源的驱动,所以充电是要的。是的,在大规模的集群中这个点会带来不小的复杂性。</p> |
| <h2 id="关键点">关键点</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbomesh/4.png" alt="4.png | center | 826x206" title=""></p> |
| <p>下面跟大家分享下我们对servicemesh理解的三个关键技术点。分别是sidecar运维,数据面与控制面的集成,协议。</p> |
| <ul> |
| <li>先说sidecar的运维,这是个难点,也是为什么sidecar方案以前没有被广泛应用的重要原因。前面说sidecar与应用现在成为两个不同的进程,要考虑多个事宜,一是要考虑如何把sidecar与应用部署在一起,二是考虑业务进程或sidecar进程一方需要升级重启时如何协同来保证请求的正常处理或转发,即优雅上下线的问题。这些事宜考虑清楚并解决后,算是具备servicemesh的前提条件。当然,kubernetes解决了这块的事情,提供了initiator类似插件的机制来对原子性的pod进行注入sidecar,并通过健康检查机制来保证两个进程的协同。简单地也可以这么理解:先把kubernetes容器调度平台的实施是servicemesh的前提条件。</li> |
| <li>数据面中的sidecar的服务治理能力则是其核心竞争力,包括负载均衡策略,路由,安全,权重等等,这些能力是以规则形式通过控制面来统一下发给数据面。在传统微服务框架下数据面和控制面的集成是紧耦合,也就是数据面和控制面是一体的,举例来说用了Dubbo框架,只能选择Dubbo-Ops。而Envoy作为servicemesh思潮的带领者,提出了一整套的API规范,Istio可以实现其xDS接口,阿里巴巴也可以根据自己的架构设计实现类似的服务平台。</li> |
| <li>协议 协议 协议, 重要的事说三遍。。。sidecar和Dubbo的内核是网络协议的处理器,而sidecar又是面向多语言场景的,所以自然协议处理能力是要强调的。先说下阿里Dubbo当下向Mesh方向发展时遇到难点。首先我们的服务接口都是通过Java Interface描述,其次涉及的传输模型DTO也是Java POJO定义,最后协议也是私有的。这会导致跨语言比较难,而sidecar形态需要面向多语言,这些问题更是首当其冲。考虑到这里有点稍微偏细节点,希望大家带着如下问题来先思考下:业务应用到sidecar之间的数据交换要考虑什么? Sidecar自身在处理网络字节流时又要考虑什么?是的,首先业务应用最好都不依赖特定协议库,也不依赖特接口定义库;Sidecar自身处理数据时跟nginx很接近,但最好具备协议转换适配的能力,比如把基于HTTP的请求转换为Dubbo请求,就能轻松集成Dubbo遗留系统。</li> |
| </ul> |
| <h2 id="回看协议">回看协议</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbomesh/5.png" alt="5.png | center | 826x206" title=""></p> |
| <p>既然协议在跨语言场景下如此重要,有必要稍微回归下协议的历史轨迹。看历史一般是轻松有趣的过程,最重要的好处是能使我们头脑清晰而不迷茫。</p> |
| <p>我们先从2008年说起,很近也就10年,阿里服务框架诞生这一年。当年各大公司还在炒作SOA思想的时候,阿里在不清楚SOA思想的情况下根据自身业务诉求实践拥抱了SOA的架构。阿里服务框架一直是从三个层面来定义,第一RPC通信 第二是提供丰富强大的治理能力 第三就是基于容器隔离的运维能力,使得中间件可以独立升级。这个理念直到今日都是非常先进,非常的赞。就像前面说的,Dubbo主要是面向Java领域的微服务架构解决方案,在以Java为主导的技术架构下是绝对首选,但因为其协议设计是私有特性,要想成为跨语言的协议标准是有一定难度。</p> |
| <p>事实上,之前已经出现了很多通用的跨语言的服务集成规范。最早是91年的CORBA,是分布式对象访问协议,2000年的SOAP是当年webservice思想下的协议,无论是CORBA还是SOAP都是支持所有平台和语言的一套规范,但是设计地比较复杂笨重,且性能存在一定问题。</p> |
| <p>REST是一种架构风格,相比SOAP的设计,有非常优秀的理念和最佳实践指导,并且万维网作为世界上最大型最成功的的分布式应用是REST最好的证明。但跟SOAP一样,REST跑在1上有性能瓶颈,这个也可能是当年阿里服务框架没有选择REST规范的原因。额外提下,REST思想虽然很早就有,但事实上REST的规范在Java领域JAX-RS API 直到最近两年在2.2版本下才算稳定成形,且越来越接近微服务框架。</p> |
| <p>1996年的1在连接通道不支持多工复用,根本无法发挥TCP/UDP的网络能力;而到了2015年HTTP2则解决这些,能够最大限度的利用TCP层的网络宽带,且支持了streaming,push等交互模式,这些跟很多的私有或专有应用协议干得是一个事,但是标准化的大家都容易接受的事。这里必须提一下,伴随HTTP2而来的是grpc,原先Google早早推出了Protocolbuffer,但一直没把自家stubby开源,我猜测最大的原因是不想grpc跑在一个私有协议上,而是在等HTTP2.</p> |
| <p>总结下来,协议技术一直在向着轻量级和标准规范化的方向发展。像SOAP,CORBA这些重量级的不跨平台的协议必然消失在历史车轮里,私有或专有的协议也会向标准协议靠拢。在面向跨语言的场景下,有两种的协议规范是大概率胜出,一种是REST,一种是grpc,两者都是以HTTP为交换通道。</p> |
| <h2 id="面向多语言协议的三层面">面向多语言协议的三层面</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbomesh/6.png" alt="6.png | center | 826x206" title=""></p> |
| <p>展开来讲,在面向多语言的协议需要考虑三个层面。</p> |
| <ul> |
| <li>先从最右边的会话层,干得事是在tcp字节流的基础上形成交互模式,比如 一对一的标准请求响应模式, 以及onway, 一对多的streaming模式。Dubbo在这一层是有扩展能力的,目前除了支持自定义的Dubbo-Remoting,也支持基于HTTP通道能力,我们觉得未来的趋势是HTTP2,所以也会支持这块.这里在分享一句话跟大家一起思考,HTTP不是RPC,HTTP被翻译成超文本传输协议,但不是传输层。另外提一下,这一层是对于MQ,Streaming Compute,Cache等等都是通用的。</li> |
| <li>再说展示层,干得事是在真正的服务调用过程中,业务对象以何种形式被格式化,比如HTTP头中的content-type就用于这个展示协议的描述,最常用的JSON,TXT,XML等。这一层对于sidecar来说,可以做透明处理,也就是说sidecar只需要解析出头部信息,前提是要求业务应用把需要在治理时用到的一些字段信息以字符串形式放到头部中。Dubbo当前是默认HEssion,跨语言能力比较弱,所以未来JSON是我们首选。</li> |
| <li>最后,首先一个服务是干什么的,它的名字,方法,参数都是怎样的,等等基本元信息是需要统一描述的,即便像是REST这样基于URI,也是需要一种协议来定义,以前Dubbo是基于java interface来定义,现在我们在多语言的mesh环境下是考虑向OpenAPI specification方向考虑,支持swagger。 |
| 我们相信在这几个层面,尤其是会话层和应用层,用不多几年一定会是标准化的,尤其是在云原生的趋势下。</li> |
| </ul> |
| <h3 id="方案之kubernetes集成span-data-typecolor-stylecolorwhiteduspan">方案之Kubernetes集成<span data-type="color" style="color:white">Du</span></h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbomesh/7.png" alt="7.png | center | 826x206" title=""> |
| <span data-type="color" style="color:white">bbo Mesh方案之Kubernetes集成</span> |
| 其实,servicemesh在最近两年流行最大的原因是云原生理念的逐渐深入人心,从广义角度看,能够融入云原生的微服务框架都能称得上servicemesh。谈云原生,肯定绕不开kubernetes,所以我们在Dubbo Mesh的方案的第一个分享是 在kubernetes下的集成,目标是复用Kubernetes的基础服务,从而使得Dubbo能解决kubernetes环境下的微服务集成问题,同时能最大限度的利用dubbo已有的功能。核心思路是两点,</p> |
| <ul> |
| <li>Dubbo应用在构建阶段自动生成其deployment和service的声明文件。这个主要是解决Dubbo与kubernetes的服务映射。</li> |
| <li>Dubbo地址注册针对kubernetes的扩展实现,通过Kubernetes的APIServer来拉取并监听某个服务的podIP。这样,在kubernetes集群内,Dubbo服务就能在其podID的虚拟网络内实现服务发现。</li> |
| </ul> |
| <h2 id="方案之跨语言协议支持">方案之跨语言协议支持</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbomesh/8.png" alt="8.png | center | 826x206" title=""></p> |
| <p>前面讲了很多关于协议方面的东西,也为我们在Dubbo Mesh的方案的第二点分享是做了铺垫, 第二点的目标是Dubbo 协议的多语言支持。核心思路是</p> |
| <ul> |
| <li>积极兼容开源社区Envoy,这个使得Envoy上兼容支持Dubbo的私有协议。</li> |
| <li>Dubbo支持HTTP/2作为传输通道,这个是为了Dubbo的协议通道能力向更加开放更加标准规范的方向做努力。</li> |
| </ul> |
| <h2 id="servicemesh之云原生的指导">ServiceMesh之云原生的指导</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbomesh/9.png" alt="9.png | center | 826x206" title=""></p> |
| <p>孤立地看待servicemesh其实和传统服务框架,价值还不算大,甚至成本相对更高。这时候,当我们把servicemesh设定到云原生的上下文中,就会发现不一样的意义。</p> |
| <p>servicemesh是云原生理念的路径地图的第五步,如果没有前面的容器化,CICD等四部,真正拥抱servicemesh也只是空中楼阁。阿里在这方面的实践经验是,servicemesh的实施是需要结合软件开发的整个生命周期进行统筹,从软件在本地开发测试,到通过持续集成服务的自动化构建,再到以镜像方式分发到仓库并依托调度云平台的持续部署,最后持续监控。</p> |
| <p>dubbo已经开源好多年,是非常符合云原生这个原则,正向servicemesh方向和云原生理念上努力,为企业信息化做出一点贡献。</p> |
| <h2 id="总结">总结</h2> |
| <p>总结一下Dubbo Mesh是Dubbo在cloud native下的一种演进,这个演进是为了更加开放更加靠近标准协议规范的方向做的探索。通过分享希望大家能带走三点思考。</p> |
| <ol> |
| <li>servicemesh的多语言方案其实是走规范化标准化的协议之路,这样才能覆盖多语言的诉求。</li> |
| <li>建议大家根据实际业务场景来慎重权衡sidecar模式下运维复杂性和收益回报。</li> |
| <li>一定把servicemesh设定在云原生的上下文中才具意义,离开了Kubernetes谈servicemesh的实践是不建议的大跃进。 |
| 最后希望大家一起共建共享的Dubbo开源社区,谢谢。</li> |
| </ol></description></item><item><title>Blog: Dubbo服务端异步接口的实现背景和实践</title><link>https://dubbo.apache.org/zh-cn/blog/2019/11/02/dubbo%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</link><pubDate>Sat, 02 Nov 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/11/02/dubbo%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</guid><description> |
| <h2 id="铺垫">铺垫</h2> |
| <p>建议先对Dubbo的处理过程中涉及的线程阶段先做个了解,具体可参考<a href="https://dubbo.apache.org/zh-cn/blog/2019/11/01/dubbo%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/">Dubbo客户端异步接口的实现背景和使用场景</a>。</p> |
| <h2 id="实现背景">实现背景</h2> |
| <p>有必要比较详细点的介绍下服务端的线程策略来加深用户在选择服务端异步的判断依据,同时有必要引出协程这一在服务端异步中常常会用到的“秘密武器”。</p> |
| <h3 id="服务端的线程策略">服务端的线程策略</h3> |
| <p>Dubbo支持多种NIO框架来做Remoting的协议实现,无论是Netty,Mina或者Grizzly,实现都大同小异,都是基于事件驱动的方式来做网络通道建立,数据流读取的。其中以Grizzly对于<a href="https://javaee.github.io/grizzly/iostrategies.html">线程策略</a>的介绍为例,通常支持以下四种。Dubbo作为一个RPC框架,默认选择的是第一种策略,原因在于业务服务是CPU密集型还是IO阻塞型,是无法断定的,第一种策略是最保险的策略。当然,对于这几种策略有了了解后,再结合业务场景做针对性的选择是最完美的。</p> |
| <ol> |
| <li><strong>Worker-thread策略</strong></li> |
| </ol> |
| <p>最常用最普适的策略,其中IO线程将NIO事件处理委托给工作线程。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubboasyn_server/1.png" alt="workerthread-strategy.png"></p> |
| <p>此策略具有很高的伸缩性。我们可以根据需要更改IO和worker线程池的大小,并且不存在在特定NIO事件处理期间可能发生的,同一Selector各个Channel之间相互干扰的风险。</p> |
| <p>缺点是有线程上下文切换的代价。</p> |
| <ol start="2"> |
| <li><strong>Same-thread策略</strong></li> |
| </ol> |
| <p>可能是最有效的策略。与第一种不同,同一线程处理当前线程中的NIO事件,避免了昂贵的线程上下文切换。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubboasyn_server/2.png" alt="samethread-strategy.png"></p> |
| <p>这个策略可以调整IO线程池大小,也是具备可伸缩性;缺点也很明显,它要求业务处理中一定不要有阻塞处理,因为它可能会阻止在同一个IO线程上发生的其他NIO事件的处理。</p> |
| <ol start="3"> |
| <li><strong>Dynamic策略</strong></li> |
| </ol> |
| <p>如前所述,前两种策略具有明显的优点和缺点。但是,如果策略可以尝试在运行时根据当前条件(负载,收集的统计信息等)巧妙地交换它们,何如?</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubboasyn_server/3.png" alt="dynamic-strategy.png"></p> |
| <p>这种策略可能会带来很多好处,能更好地控制资源,前提是不要使条件评估逻辑过载,防止评估判断的复杂性会使这种策略效率低下。 |
| 多说一句,希望大家对这个策略多留意一下,它可能是Dubbo服务端异步方式的最佳搭配。我也多扯个淡,这几天关注了些adaptive XX或者predictive XX,这里看到dynamic真是亲切,Dubbo作为产品级生产级的微服务解决方案,是必须既要adaptive,又要predictive,还要dynamic,哈哈。</p> |
| <ol start="4"> |
| <li><strong>Leader-follower策略</strong></li> |
| </ol> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubboasyn_server/4.png" alt="leaderfollower-strategy.png"></p> |
| <p>此策略类似于第一种,但它不是将NIO事件处理传递给worker线程,而是通过将控制传递给Selector给工作线程,并将实际NIO事件处理当前IO线程中。这种策略其实是把worker和IO线程阶段做了混淆,个人不建议。</p> |
| <h3 id="协程与线程">协程与线程</h3> |
| <p>在CPU资源的管理上,OS和JVM的最小调度单位都是线程,业务应用通过扩展实现的协程包是可以具备独立的运行单位,事实上也是基于线程来做的,核心应该是遇到IO阻塞,或者锁等待时,保存上下文,然后切换到另一个协程。至于说的协程开销低,能更高效的使用CPU,这些考虑到协程库的用户态实现和上下文设计是支持的,但也建议大家结合实际业务场景做性能测试。</p> |
| <p><strong>在默认的Dubbo线程策略中,是有worker线程池来执行业务逻辑,但也常常会发生ThreadPool Full的问题,为了尽快释放worker线程,在业务服务的实现中会另起线程。代价是再次增加线程上下文切换,同时需要考虑链路级别的数据传送(比如tracing信息)和流控的出口控制等等。当然,如果Dubbo能够切换到Same-thread策略,再配合协程库的支持,服务端异步是一种值得推荐的使用方式。</strong></p> |
| <h2 id="示例">示例</h2> |
| <p>通过示例来体验下Dubbo服务端异步接口。Demo代码请访问github之<a href="https://github.com/dubbo/dubbo-samples/tree/master/2-advanced/dubbo-samples-notify">https://github.com/dubbo/dubbo-samples/tree/master/2-advanced/dubbo-samples-notify</a>。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">AsyncServiceImpl</span> <span style="color:#268bd2">implements</span> AsyncService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Main sayHello() method start.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> AsyncContext asyncContext <span style="color:#719e07">=</span> RpcContext<span style="color:#719e07">.</span>startAsync<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> Thread<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> asyncContext<span style="color:#719e07">.</span>signalContextSwitch<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Attachment from consumer: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getContext<span style="color:#719e07">().</span>getAttachment<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;consumer-key1&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34; -- Async start.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Thread<span style="color:#719e07">.</span>sleep<span style="color:#719e07">(</span>500<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>InterruptedException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> e<span style="color:#719e07">.</span>printStackTrace<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> asyncContext<span style="color:#719e07">.</span>write<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, response from provider.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34; -- Async end.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}).</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Main sayHello() method end.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;hello, &#34;</span> <span style="color:#719e07">+</span> name<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="实践建议">实践建议</h2> |
| <ul> |
| <li>不要迷信服务端异步。</li> |
| <li>服务端异步在Event-Driven或者Reactive面前基本是伪命题.<span data-type="color" style="color:rgb(36, 41, 46)"><span data-type="background" style="background-color:rgb(255, 255, 255)">补充下原因:服务端异步初衷是说Dubbo的服务端业务线程数(默认是200个)不够,但其实在event-driven模式下,200个肯定不需要那么多,只需要cpu核数那样就可以。只要业务实现是非阻塞的纯异步方式的业务逻辑处理,用再多的线程数都是浪费资源。</span></span></li> |
| <li>要用服务端异步,建议服务端的线程策略采用same thread模式+协程包。</li> |
| </ul> |
| <h2 id="小结">小结</h2> |
| <p>Dubbo在支持业务应用时,会碰到千奇百怪的需求场景,服务端异步为用户提供了一种解决ThreadPool Full的方案。当发生ThreadPool Full的情况下,如果当前系统瓶颈是CPU,不建议用这种方案;如果系统Load不高,调高worker的线程数目,或者采用服务端异步,都是可以考虑的。</p></description></item><item><title>Blog: Dubbo客户端异步接口的实现背景和实践</title><link>https://dubbo.apache.org/zh-cn/blog/2019/11/01/dubbo%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</link><pubDate>Fri, 01 Nov 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/11/01/dubbo%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</guid><description> |
| <h2 id="铺垫">铺垫</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubboasyn_client/1.png" alt="image | left"></p> |
| <p>先简单介绍下一次完整的Dubbo调用所经历的线程阶段。几个信息这里罗列下</p> |
| <ol> |
| <li> |
| <p>Biz~代表业务线程,即便是业务逻辑处理所处的线程,Biz~线程池可能是业务自己创建维护,大多数的可能是系统框架自身管理的(比如web型的业务系统跑在Tomcat容器下,Biz~线程就是Tomcat维护);IO~代表网络数据处理线程,是IO框架(比如Netty,Grizzly)创建维护,Dubbo Remoting所默认Netty实现是NioEventloopLoopGroup;另外按照Channel与IO线程的绑定关系,也可以直接把IO~看成一个可接受事件消息的Channel。像Biz和IO这样的异步处理阶段在JDK8中有个很精确地抽象描述,叫CompletionStage。</p> |
| </li> |
| <li> |
| <p>大家知道,线程与线程之间做数据通信的方式是共享变量,Biz和IO两个stage之间的数据通信是Queue,具体到Dubbo实现,在客户端一侧的实现(即上图中用1所标注的步骤)中Biz是通过向EventLoop的LinkedBlockingQueue放置一个Task,而EventLoop有对应的Thread会不停的迭代Queue来执行Task中所包含的信息,具体代码可以看SingleThreadEventExecutor(顺便提下,Netty中默认是用无上限的LinkedBlockingQueue,在Biz的速率高于网络速率情况下,似乎好像有Memory Leak的风险)。</p> |
| </li> |
| <li> |
| <p>如上图所示,标准的一次RPC调用经过了图中所示的1,2,3,4的四次消息(事件)传递,分别是客户端业务线程到IO线程的请求发出,服务端IO线程到业务逻辑线程的__请求接受,__服务端处理完成后由业务逻辑线程到IO线程的响应写出,客户端收到结果后从IO线程到业务逻辑的响应处理。除了1与4之间一般需要维护响应和请求的映射对应关系,四次的事件处理都是完全独立的,所以一次RPC调用天然是异步的,而同步是基于异步而来。</p> |
| </li> |
| </ol> |
| <h2 id="客户端异步">客户端异步</h2> |
| <h3 id="实现背景">实现背景</h3> |
| <p>在Java语言(其他语言不清楚)下一次本地接口的调用可以透明地通过代理机制转为远程RPC的调用,大多数业务方也比较喜欢这种与本地接口类似的编程方式做远程服务集成,所以虽然RPC内部天然是异步的,但使用Dubbo的用户使用最广泛的还是同步,而异步反而成为小众的使用场景。同步的优点是编程模型更加符合业务方的“传统”习惯,代价是在图中的1代表的请求发出事件后需要阻塞当前的Biz~线程,一直等到4代表的响应处理后才能唤醒。在这个短则微秒级别,长则秒级的1,2,3,4处理过程中都要阻塞Biz~线程,就会消耗线程资源,增加系统资源的开销。</p> |
| <p>所以,客户端异步的出发点是节省线程资源开销,代价是需要了解下异步的使用方式:)。在同步方式下API接口的返回类型是代表着某个业务类,而当异步情况下,响应返回与请求发出是完全独立的两个事件,需要API接口的返回类型变为上述中说的CompletionStage才是最贴合的,这是Dubbo在异步上支持的必然异步。回到最近的Dubbo发布版,是不改变接口的情况下,需要在服务创建时注册一个回调接口来处理响应返回事件。</p> |
| <p>下面以示例来说。</p> |
| <h3 id="示例">示例</h3> |
| <p>事件通知的示例代码请参考:<a href="https://github.com/dubbo/dubbo-samples/tree/master/2-advanced/dubbo-samples-notify">https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-notify</a></p> |
| <p>事件通知允许 Consumer 端在调用之前、调用正常返回之后或调用出现异常时,触发 <code>oninvoke</code>、<code>onreturn</code>、<code>onthrow</code> 三个事件。</p> |
| <p>可以通过在配置 Consumer 时,指定事件需要通知的方法,如:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;demoCallback&#34;</span> class=<span style="color:#2aa198">&#34;com.alibaba.dubbo.samples.notify.impl.NotifyImpl&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> check=<span style="color:#2aa198">&#34;false&#34;</span> interface=<span style="color:#2aa198">&#34;com.alibaba.dubbo.samples.notify.api.DemoService&#34;</span> version=<span style="color:#2aa198">&#34;1.0.0&#34;</span> group=<span style="color:#2aa198">&#34;cn&#34;</span><span style="color:#268bd2">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:method</span> name=<span style="color:#2aa198">&#34;sayHello&#34;</span> onreturn=<span style="color:#2aa198">&#34;demoCallback.onreturn&#34;</span> onthrow=<span style="color:#2aa198">&#34;demoCallback.onthrow&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/dubbo:reference&gt;</span> |
| </span></span></code></pre></div><p>其中,NotifyImpl 的代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">NotifyImpl</span> <span style="color:#268bd2">implements</span> Notify<span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>Integer<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> ret <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;</span>Integer<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onreturn</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> id<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ret<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>id<span style="color:#719e07">,</span> name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;onreturn: &#34;</span> <span style="color:#719e07">+</span> name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onthrow</span><span style="color:#719e07">(</span>Throwable ex<span style="color:#719e07">,</span> String name<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> id<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;onthrow: &#34;</span> <span style="color:#719e07">+</span> name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里要强调一点,自定义 Notify 接口中的三个方法的参数规则如下:</p> |
| <ul> |
| <li><code>oninvoke</code> 方法参数与调用方法的参数相同;</li> |
| <li><code>onreturn</code>方法第一个参数为调用方法的返回值,其余为调用方法的参数;</li> |
| <li><code>onthrow</code>方法第一个参数为调用异常,其余为调用方法的参数。</li> |
| </ul> |
| <p>上述配置中,<code>sayHello</code>方法为同步调用,因此事件通知方法的执行也是同步执行。可以配置 <code>async=true</code>让方法调用为异步,这时事件通知的方法也是异步执行的。特别强调一下,<code>oninvoke</code>方法不管是否异步调用,都是同步执行的。</p> |
| <h3 id="实践建议">实践建议</h3> |
| <ul> |
| <li> |
| <div data-type="alignment" data-value="justify" style="text-align:justify"> |
| <div data-type="p">RPC调用后的逻辑非强依赖结果:异步回调是在客户端<strong>非强依赖服务端的结果</strong>情况下,是适用客户端的异步调用。</div> |
| </div> |
| </li> |
| <li> |
| <div data-type="alignment" data-value="justify" style="text-align:justify"> |
| <div data-type="p">rx场景:自从了解到reactive的编程模型后,认为只要编程思维能够拥抱reactive,并且业务模型的状态机设计能做适当的调整,任何场景下都比较适用异步来解决,从而得到更好的终端响应体验。 对于Dubbo来说,当下的异步接口模型是需要像reactive的模型接口做改进,才能使得用户更自然地适用异步接口。</div> |
| </div> |
| </li> |
| </ul> |
| <h3 id="小结">小结</h3> |
| <ul> |
| <li>客户端异步的出发点就是请求发出和响应处理本身为两个不同的独立事件,响应如何被处理和在哪个线程中处理等都是不需要和请求发出事件的业务逻辑线程做耦合绑定。</li> |
| <li>响应事件回调的处理逻辑在哪个线程中做处理是需要根据情况来选择。建议,如果回调逻辑比较简单,建议直接在IO线程中;如果包含了远程访问或者DB访问等IO型的__同步__操作,建议在独立的线程池做处理。</li> |
| </ul></description></item><item><title>Blog: Dubbo 在跨语言和协议穿透性方向上的探索:支持 HTTP/2 gRPC 和 Protobuf</title><link>https://dubbo.apache.org/zh-cn/blog/2019/10/28/dubbo-%E5%9C%A8%E8%B7%A8%E8%AF%AD%E8%A8%80%E5%92%8C%E5%8D%8F%E8%AE%AE%E7%A9%BF%E9%80%8F%E6%80%A7%E6%96%B9%E5%90%91%E4%B8%8A%E7%9A%84%E6%8E%A2%E7%B4%A2%E6%94%AF%E6%8C%81-http/2-grpc-%E5%92%8C-protobuf/</link><pubDate>Mon, 28 Oct 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/10/28/dubbo-%E5%9C%A8%E8%B7%A8%E8%AF%AD%E8%A8%80%E5%92%8C%E5%8D%8F%E8%AE%AE%E7%A9%BF%E9%80%8F%E6%80%A7%E6%96%B9%E5%90%91%E4%B8%8A%E7%9A%84%E6%8E%A2%E7%B4%A2%E6%94%AF%E6%8C%81-http/2-grpc-%E5%92%8C-protobuf/</guid><description> |
| <p>本文总体上可分为基础产品简介、Dubbo 对 gRPC (HTTP/2) 和 Protobuf 的支持及示例演示三部分,在简介部分介绍了 Dubbo、HTTP/2、gRPC、Protobuf 的基本概念和特点;第二部分介绍了 Dubbo 为何要支持 gRPC (HTTP/2) 和 Protobuf,以及这种支持为 gRPC 和 Dubbo 开发带来的好处与不同;第三部分通过两个实例分别演示了 Dubbo gRPC 和 Dubbo Protobuf 的使用方式。</p> |
| <h2 id="基本介绍">基本介绍</h2> |
| <h3 id="dubbo-协议">Dubbo 协议</h3> |
| <p>从协议层面展开,以下是当前 2.7 版本支持的 Dubbo 协议</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/grpc/dubbo-ptotocol.png" alt="image-20191029103919557"></p> |
| <p>众所周知,Dubbo 协议是直接定义在 TCP 传输层协议之上,由于 TCP 高可靠全双工的特点,为 Dubbo 协议的定义提供了最大的灵活性,但同时也正是因为这样的灵活性,RPC 协议普遍都是定制化的私有协议,Dubbo 同样也面临这个问题。在这里我们着重讲一下 Dubbo 在协议通用性方面值得改进的地方,关于协议详细解析请参见<a href="https://dubbo.apache.org/zh-cn/blog/2018/10/05/dubbo-%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3/">官网博客</a></p> |
| <ul> |
| <li>Dubbo 协议体 Body 中有一个可扩展的 attachments 部分,这给 RPC 方法之外额外传递附加属性提供了可能,是一个很好的设计。但是类似的 Header 部分,却缺少类似的可扩展 attachments,这点可参考 HTTP 定义的 Ascii Header 设计,将 Body Attachments 和 Header Attachments 做职责划分。</li> |
| <li>Body 协议体中的一些 RPC 请求定位符如 Service Name、Method Name、Version 等,可以提到 Header 中,和具体的序列化协议解耦,以更好的被网络基础设施识别或用于流量管控。</li> |
| <li>扩展性不够好,欠缺协议升级方面的设计,如 Header 头中没有预留的状态标识位,或者像 HTTP 有专为协议升级或协商设计的特殊 packet。</li> |
| <li>在 Java 版本的代码实现上,不够精简和通用。如在链路传输中,存在一些语言绑定的内容;消息体中存在冗余内容,如 Service Name 在 Body 和 Attachments 中都存在。</li> |
| </ul> |
| <h3 id="http1">HTTP/1</h3> |
| <p>相比于直接构建与 TPC 传输层的私有 RPC 协议,构建于 HTTP 之上的远程调用解决方案会有更好的通用性,如WebServices 或 REST 架构,使用 HTTP + JSON 可以说是一个事实标准的解决方案。</p> |
| <p>之所有选择构建在 HTTP 之上,我认为有两个最大的优势:</p> |
| <ol> |
| <li>HTTP 的语义和可扩展性能很好的满足 RPC 调用需求。</li> |
| <li>通用性,HTTP 协议几乎被网络上的所有设备所支持,具有很好的协议穿透性。</li> |
| </ol> |
| <p><img src="https://dubbo.apache.org/imgs/blog/grpc/http1.png" alt="image-20191029113404906"></p> |
| <p>具体来说,HTTP/1 的优势和限制是:</p> |
| <ul> |
| <li> |
| <p>典型的 Request – Response 模型,一个链路上一次只能有一个等待的 Request 请求</p> |
| </li> |
| <li> |
| <p>HTTP/1 支持 Keep-Alive 链接,避免了链接重复创建开销</p> |
| </li> |
| <li> |
| <p>Human Readable Headers,使用更通用、更易于人类阅读的头部传输格式</p> |
| </li> |
| <li> |
| <p>无直接 Server Push 支持,需要使用 Polling Long-Polling 等变通模式</p> |
| </li> |
| </ul> |
| <h3 id="http2">HTTP/2</h3> |
| <p>HTTP/2 保留了 HTTP/1 的所有语义,在保持兼容的同时,在通信模型和传输效率上做了很大的改进。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/grpc/http2.png" alt="image-20191029113416731"></p> |
| <ul> |
| <li> |
| <p>支持单条链路上的 Multiplexing,相比于 Request - Response 独占链路,基于 Frame 实现更高效利用链路</p> |
| </li> |
| <li> |
| <p>Request - Stream 语义,原生支持 Server Push 和 Stream 数据传输</p> |
| </li> |
| <li> |
| <p>Flow Control,单条 Stream 粒度的和整个链路粒度的流量控制</p> |
| </li> |
| <li> |
| <p>头部压缩 HPACK</p> |
| </li> |
| <li> |
| <p>Binary Frame</p> |
| </li> |
| <li> |
| <p>原生 TLS 支持</p> |
| </li> |
| </ul> |
| <h3 id="grpc">gRPC</h3> |
| <p>上面提到了在 HTTP 及 TCP 协议之上构建 RPC 协议各自的优缺点,相比于 Dubbo 构建于 TPC 传输层之上,Google 选择将 gRPC 直接定义在 HTTP/2 协议之上,关于 gRPC 的 <a href="https://grpc.io/docs/what-is-grpc/introduction/">基本介绍</a>和 <a href="https://grpc.io/blog/principles/?spm=ata.13261165.0.0.2be55017XbUhs8">设计愿景</a> 请参考以上两篇文章,我这里仅摘取 设计愿景 中几个能反映 gRPC 设计目的特性来做简单说明。</p> |
| <ul> |
| <li> |
| <p>Coverage &amp; Simplicity,协议设计和框架实现要足够通用和简单,能运行在任何设备之上,甚至一些资源首先的如 IoT、Mobile 等设备。</p> |
| </li> |
| <li> |
| <p>Interoperability &amp; Reach,要构建在更通用的协议之上,协议本身要能被网络上几乎所有的基础设施所支持。</p> |
| </li> |
| <li> |
| <p>General Purpose &amp; Performant,要在场景和性能间做好平衡,首先协议本身要是适用于各种场景的,同时也要尽量有高的性能。</p> |
| </li> |
| <li> |
| <p>Payload Agnostic,协议上传输的负载要保持语言和平台中立。</p> |
| </li> |
| <li> |
| <p>Streaming,要支持 Request - Response、Request - Stream、Bi-Steam 等通信模型。</p> |
| </li> |
| <li> |
| <p>Flow Control,协议自身具备流量感知和限制的能力。</p> |
| </li> |
| <li> |
| <p>Metadata Exchange,在 RPC 服务定义之外,提供额外附加数据传输的能力。</p> |
| </li> |
| </ul> |
| <p>总的来说,在这样的设计理念指导下,gRPC 最终被设计为一个跨语言、跨平台的、通用的、高性能的、基于 HTTP/2 的 RPC 协议和框架。</p> |
| <h3 id="protobuf">Protobuf</h3> |
| <p><a href="https://developers.google.com/protocol-buffers/docs/overview">Protocol buffers (Protobuf)</a> 是 Google 推出的一个跨平台、语言中立的结构化数据描述和序列化的产品,它定义了一套结构化数据定义的协议,同时也提供了相应的 <a href="https://github.com/protocolbuffers/protobuf/releases/tag/v3.10.0">Compiler</a> 工具,用来将语言中立的描述转化为相应语言的具体描述。</p> |
| <p>它的一些特性包括:</p> |
| <ul> |
| <li> |
| <p>跨语言 跨平台,语言中立的数据描述格式,默认提供了生成多种语言的 Compiler 工具。</p> |
| </li> |
| <li> |
| <p>安全性,由于反序列化的范围和输出内容格式都是 Compiler 在编译时预生成的,因此绕过了类似 Java Deserialization Vulnarability 的问题。</p> |
| </li> |
| <li> |
| <p>二进制 高性能</p> |
| </li> |
| <li> |
| <p>强类型</p> |
| </li> |
| <li> |
| <p>字段变更向后兼容</p> |
| </li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>message Person { |
| </span></span><span style="display:flex;"><span> required string name = 1; |
| </span></span><span style="display:flex;"><span> required int32 id = 2; |
| </span></span><span style="display:flex;"><span> optional string email = 3; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> enum PhoneType { |
| </span></span><span style="display:flex;"><span> MOBILE = 0; |
| </span></span><span style="display:flex;"><span> HOME = 1; |
| </span></span><span style="display:flex;"><span> WORK = 2; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> message PhoneNumber { |
| </span></span><span style="display:flex;"><span> required string number = 1; |
| </span></span><span style="display:flex;"><span> optional PhoneType type = 2 [default = HOME]; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> repeated PhoneNumber phone = 4; |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>除了结构化数据描述之外,Protobuf 还支持定义 RPC 服务,它允许我们定义一个 <code>.proto</code> 的服务描述文件,进而利用 Protobuf Compiler 工具生成特定语言和 RPC 框架的接口和 stub。后续将要具体讲到的 gRPC + Protobuf、Dubbo-gRPC + Protobuf 以及 Dubbo + Protobuf 都是通过定制 Compiler 类实现的。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>service SearchService { |
| </span></span><span style="display:flex;"><span> rpc Search (SearchRequest) returns (SearchResponse); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h2 id="dubbo-所做的支持">Dubbo 所做的支持</h2> |
| <p>跨语言的服务开发涉及到多个方面,从服务定义、RPC 协议到序列化协议都要做到语言中立,同时还针对每种语言有对应的 SDK 实现。虽然得益于社区的贡献,现在 Dubbo 在多语言 SDK 实现上逐步有了起色,已经提供了包括 Java, Go, PHP, C#, Python, NodeJs, C 等版本的客户端或全量实现版本,但在以上提到的跨语言友好型方面,以上三点还是有很多可改进之处。</p> |
| <ul> |
| <li> |
| <p>协议,上面我们已经分析过 Dubbo 协议既有的缺点,如果能在 HTTP/2 之上构建应用层协议,则无疑能避免这些弊端,同时最大可能的提高协议的穿透性,避免网关等协议转换组件的存在,更有利于链路上的流量管控。考虑到 gRPC 是构建在 HTTP/2 之上,并且已经是云原生领域推荐的通信协议,Dubbo 在第一阶段选择了直接支持 gRPC 协议作为当前的 HTTP/2 解决方案。我们也知道 gRPC 框架自身的弊端在于易用性不足以及服务治理能力欠缺(这也是目前绝大多数厂商不会直接裸用 gRPC 框架的原因),通过将其集成进 Dubbo 框架,用户可以方便的使用 Dubbo 编程模型 + Dubbo 服务治理 + gRPC 协议通信的组合。</p> |
| </li> |
| <li> |
| <p>服务定义,当前 Dubbo 的服务定义和具体的编程语言绑定,没有提供一种语言中立的服务描述格式,比如 Java 就是定义 Interface 接口,到了其他语言又得重新以另外的格式定义一遍。因此 Dubbo 通过支持 Protobuf 实现了语言中立的服务定义。</p> |
| </li> |
| <li> |
| <p>序列化,Dubbo 当前支持的序列化包括 Json、Hessian2、Kryo、FST、Java 等,而这其中支持跨语言的只有 Json、Hessian2,通用的 Json 有固有的性能问题,而 Hessian2 无论在效率还是多语言 SDK 方面都有所欠缺。为此,Dubbo 通过支持 Protobuf 序列化来提供更高效、易用的跨语言序列化方案。</p> |
| </li> |
| </ul> |
| <h2 id="示例">示例</h2> |
| <h3 id="示例-1使用-dubbo-开发-grpc-服务">示例 1,使用 Dubbo 开发 gRPC 服务</h3> |
| <p><a href="https://grpc.io/">gRPC</a> 是 Google 开源的构建在 HTTP/2 之上的一个 PRC 通信协议。Dubbo 依赖其灵活的协议扩展机制,增加了对 gRPC (HTTP/2) 协议的支持。</p> |
| <p>目前的支持限定在 Dubbo Java 语言版本,后续 Go 语言或其他语言版本将会以类似方式提供支持。下面,通过一个<a href="https://github.com/apache/dubbo-samples/tree/master/99-integration/dubbo-samples-grpc">简单的示例</a>来演示如何在 Dubbo 中使用 gRPC 协议通信。</p> |
| <h4 id="1-定义服务-idl">1. 定义服务 IDL</h4> |
| <p>首先,通过标准的 Protobuf 协议定义服务如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>syntax = <span style="color:#2aa198">&#34;proto3&#34;</span>; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>option java_multiple_files = <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span>option java_package = <span style="color:#2aa198">&#34;io.grpc.examples.helloworld&#34;</span>; |
| </span></span><span style="display:flex;"><span>option java_outer_classname = <span style="color:#2aa198">&#34;HelloWorldProto&#34;</span>; |
| </span></span><span style="display:flex;"><span>option objc_class_prefix = <span style="color:#2aa198">&#34;HLW&#34;</span>; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">package</span> helloworld; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// The greeting service definition. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>service Greeter { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Sends a greeting |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> rpc <span style="color:#268bd2">SayHello</span> (HelloRequest) <span style="color:#268bd2">returns</span> (HelloReply) {} |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// The request message containing the user&#39;s name. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>message HelloRequest { |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">string</span> name = <span style="color:#2aa198">1</span>; |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// The response message containing the greetings |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>message HelloReply { |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">string</span> message = <span style="color:#2aa198">1</span>; |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>在此,我们定义了一个只有一个方法 sayHello 的 Greeter 服务,同时定义了方法的入参和出参,</p> |
| <h4 id="2-pcompiler-生成-stub">2. PCompiler 生成 Stub</h4> |
| <ol> |
| <li>定义 Maven Protobuf Compiler 插件工具。这里我们扩展了 Protobuf 的 Compiler 工具,以用来生成 Dubbo 特有的 RPC stub,此当前以 Maven 插件的形式发布。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.xolstice.maven.plugins<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>protobuf-maven-plugin<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>0.5.1<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;protocArtifact&gt;</span>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/protocArtifact&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;pluginId&gt;</span>dubbo-grpc-java<span style="color:#268bd2">&lt;/pluginId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;pluginArtifact&gt;</span>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}<span style="color:#268bd2">&lt;/pluginArtifact&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;outputDirectory&gt;</span>build/generated/source/proto/main/java<span style="color:#268bd2">&lt;/outputDirectory&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;clearOutputDirectory&gt;</span>false<span style="color:#268bd2">&lt;/clearOutputDirectory&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;pluginParameter&gt;</span>grpc<span style="color:#268bd2">&lt;/pluginParameter&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;executions&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goal&gt;</span>compile<span style="color:#268bd2">&lt;/goal&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goal&gt;</span>compile-custom<span style="color:#268bd2">&lt;/goal&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/executions&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/plugin&gt;</span> |
| </span></span></code></pre></div><p>其中,</p> |
| <p>pluginArtifact 指定了 Dubbo 定制版本的 Java Protobuf Compiler 插件,通过这个插件来在编译过程中生成 Dubbo 定制版本的 gRPC stub。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;pluginArtifact&gt;</span>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}<span style="color:#268bd2">&lt;/pluginArtifact&gt;</span> |
| </span></span></code></pre></div><p>由于 <code>protoc-gen-dubbo-java</code> 支持 gRPC 和 Dubbo 两种协议,可生成的 stub 类型,默认值是 gRPC,关于 dubbo 协议的使用可参见 <a href="https://dubbo.apache.org/zh-cn/overview/mannual/java-sdk/quick-start/idl/">使用 Protobuf 开发 Dubbo 服务</a>。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;pluginParameter&gt;</span>grpc<span style="color:#268bd2">&lt;/pluginParameter&gt;</span> |
| </span></span></code></pre></div><ol start="2"> |
| <li> |
| <p>生成 Java Bean 和 Dubbo-gRPC stub</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#586e75"># 运行以下 maven 命令</span> |
| </span></span><span style="display:flex;"><span>$ mvn clean compile |
| </span></span></code></pre></div><p>生成的 Stub 和消息类 如下: |
| <img src="https://dubbo.apache.org/imgs/blog/grpc/compiler-classes.png" alt="image-20191026130516896"></p> |
| <p>重点关注 GreeterGrpc ,包含了所有 gRPC 标准的 stub 类/方法,同时增加了 Dubbo 特定的接口,之后 Provider 端的服务暴露和 Consumer 端的服务调用都将依赖这个接口。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Code generated for Dubbo |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">IGreeter</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">default</span> <span style="color:#268bd2">public</span> io<span style="color:#719e07">.</span>grpc<span style="color:#719e07">.</span>examples<span style="color:#719e07">.</span>helloworld<span style="color:#719e07">.</span>HelloReply <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>io<span style="color:#719e07">.</span>grpc<span style="color:#719e07">.</span>examples<span style="color:#719e07">.</span>helloworld<span style="color:#719e07">.</span>HelloRequest request<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> UnsupportedOperationException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No need to override this method, extend XxxImplBase and override all methods it allows.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">default</span> <span style="color:#268bd2">public</span> com<span style="color:#719e07">.</span>google<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>util<span style="color:#719e07">.</span>concurrent<span style="color:#719e07">.</span>ListenableFuture<span style="color:#719e07">&lt;</span>io<span style="color:#719e07">.</span>grpc<span style="color:#719e07">.</span>examples<span style="color:#719e07">.</span>helloworld<span style="color:#719e07">.</span>HelloReply<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> io<span style="color:#719e07">.</span>grpc<span style="color:#719e07">.</span>examples<span style="color:#719e07">.</span>helloworld<span style="color:#719e07">.</span>HelloRequest request<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> UnsupportedOperationException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No need to override this method, extend XxxImplBase and override all methods it allows.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>io<span style="color:#719e07">.</span>grpc<span style="color:#719e07">.</span>examples<span style="color:#719e07">.</span>helloworld<span style="color:#719e07">.</span>HelloRequest request<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> io<span style="color:#719e07">.</span>grpc<span style="color:#719e07">.</span>stub<span style="color:#719e07">.</span>StreamObserver<span style="color:#719e07">&lt;</span>io<span style="color:#719e07">.</span>grpc<span style="color:#719e07">.</span>examples<span style="color:#719e07">.</span>helloworld<span style="color:#719e07">.</span>HelloReply<span style="color:#719e07">&gt;</span> responseObserver<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div></li> |
| </ol> |
| <h4 id="3-业务逻辑开发">3. 业务逻辑开发</h4> |
| <p>继承 <code>GreeterGrpc.GreeterImplBase</code> (来自第 2 步),编写业务逻辑,这点和原生 gRPC 是一致的。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">package</span> org.apache.dubbo.samples.basic.impl<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> io.grpc.examples.helloworld.GreeterGrpc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> io.grpc.examples.helloworld.HelloReply<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> io.grpc.examples.helloworld.HelloRequest<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> io.grpc.stub.StreamObserver<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">GrpcGreeterImpl</span> <span style="color:#268bd2">extends</span> GreeterGrpc<span style="color:#719e07">.</span>GreeterImplBase <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>HelloRequest request<span style="color:#719e07">,</span> StreamObserver<span style="color:#719e07">&lt;</span>HelloReply<span style="color:#719e07">&gt;</span> responseObserver<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Received request from client.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Executing thread is &#34;</span> <span style="color:#719e07">+</span> Thread<span style="color:#719e07">.</span>currentThread<span style="color:#719e07">().</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> HelloReply reply <span style="color:#719e07">=</span> HelloReply<span style="color:#719e07">.</span>newBuilder<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setMessage<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> request<span style="color:#719e07">.</span>getName<span style="color:#719e07">()).</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> responseObserver<span style="color:#719e07">.</span>onNext<span style="color:#719e07">(</span>reply<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> responseObserver<span style="color:#719e07">.</span>onCompleted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="4-provider-端暴露-dubbo-服务">4. Provider 端暴露 Dubbo 服务</h4> |
| <p>以 Spring XML 为例</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:application</span> name=<span style="color:#2aa198">&#34;demo-provider&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 指定服务暴露协议为 gRPC --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:protocol</span> id=<span style="color:#2aa198">&#34;grpc&#34;</span> name=<span style="color:#2aa198">&#34;grpc&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address:127.0.0.1}:2181&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;greeter&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.impl.GrpcGreeterImpl&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 指定 protoc-gen-dubbo-java 生成的接口 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;io.grpc.examples.helloworld.GreeterGrpc$IGreeter&#34;</span> ref=<span style="color:#2aa198">&#34;greeter&#34;</span> protocol=<span style="color:#2aa198">&#34;grpc&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ClassPathXmlApplicationContext context <span style="color:#719e07">=</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ClassPathXmlApplicationContext<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;spring/dubbo-demo-provider.xml&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> context<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo service started&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> CountDownLatch<span style="color:#719e07">(</span>1<span style="color:#719e07">).</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="5-引用-dubbo-服务">5. 引用 Dubbo 服务</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:application</span> name=<span style="color:#2aa198">&#34;demo-consumer&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://${zookeeper.address:127.0.0.1}:2181&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 指定 protoc-gen-dubbo-java 生成的接口 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;greeter&#34;</span> interface=<span style="color:#2aa198">&#34;io.grpc.examples.helloworld.GreeterGrpc$IGreeter&#34;</span> protocol=<span style="color:#2aa198">&#34;grpc&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> IOException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ClassPathXmlApplicationContext context <span style="color:#719e07">=</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ClassPathXmlApplicationContext<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;spring/dubbo-demo-consumer.xml&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> context<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> GreeterGrpc<span style="color:#719e07">.</span>IGreeter greeter <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>GreeterGrpc<span style="color:#719e07">.</span>IGreeter<span style="color:#719e07">)</span> context<span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;greeter&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> HelloReply reply <span style="color:#719e07">=</span> greeter<span style="color:#719e07">.</span>sayHello<span style="color:#719e07">(</span>HelloRequest<span style="color:#719e07">.</span>newBuilder<span style="color:#719e07">().</span>setName<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;world!&#34;</span><span style="color:#719e07">).</span>build<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Result: &#34;</span> <span style="color:#719e07">+</span> reply<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>in<span style="color:#719e07">.</span>read<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="示例1附高级用法">示例1附:高级用法</h4> |
| <p><strong>一、异步调用</strong></p> |
| <p>再来看一遍 <code>protoc-gen-dubbo-java</code> 生成的接口:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Code generated for Dubbo |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">IGreeter</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> <span style="color:#268bd2">public</span> HelloReply <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>HelloRequest request<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ...... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> <span style="color:#268bd2">public</span> ListenableFuture<span style="color:#719e07">&lt;</span>HelloReply<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>HelloRequest request<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ...... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>HelloRequest request<span style="color:#719e07">,</span> StreamObserver<span style="color:#719e07">&lt;</span>HelloReply<span style="color:#719e07">&gt;</span> responseObserver<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里为 sayHello 方法生成了三种类型的重载方法,分别用于同步调用、异步调用和流式调用,如果消费端要进行异步调用,直接调用 sayHelloAsync() 即可:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> IOException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> GreeterGrpc<span style="color:#719e07">.</span>IGreeter greeter <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>GreeterGrpc<span style="color:#719e07">.</span>IGreeter<span style="color:#719e07">)</span> context<span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;greeter&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> ListenableFuture<span style="color:#719e07">&lt;</span>HelloReply<span style="color:#719e07">&gt;</span> future <span style="color:#719e07">=</span> |
| </span></span><span style="display:flex;"><span> greeter<span style="color:#719e07">.</span>sayHAsyncello<span style="color:#719e07">(</span>HelloRequest<span style="color:#719e07">.</span>newBuilder<span style="color:#719e07">().</span>setName<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;world!&#34;</span><span style="color:#719e07">).</span>build<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>二、高级配置</strong></p> |
| <p>由于当前实现方式是直接集成了 gRPC-java SDK,因此很多配置还没有和 Dubbo 侧对齐,或者还没有以 Dubbo 的配置形式开放,因此,为了提供最大的灵活性,我们直接把 gRPC-java 的配置接口暴露了出来。</p> |
| <p>绝大多数场景下,你可能并不会用到以下扩展,因为它们更多的是对 gRPC 协议的拦截或者 HTTP/2 层面的配置。同时使用这些扩展点可能需要对 HTTP/2 或 gRPC 有基本的了解。</p> |
| <p><strong>扩展点</strong></p> |
| <p>目前支持的扩展点如下:</p> |
| <ul> |
| <li> |
| <p>org.apache.dubbo.rpc.protocol.grpc.interceptors.ClientInterceptor</p> |
| </li> |
| <li> |
| <p>org.apache.dubbo.rpc.protocol.grpc.interceptors.GrpcConfigurator</p> |
| </li> |
| <li> |
| <p>org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerInterceptor</p> |
| </li> |
| <li> |
| <p>org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerTransportFilter</p> |
| </li> |
| </ul> |
| <p>GrpcConfigurator 是最通用的扩展点,我们以此为例来说明一下,其基本定义如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">GrpcConfigurator</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 用来定制 gRPC NettyServerBuilder |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">default</span> NettyServerBuilder <span style="color:#268bd2">configureServerBuilder</span><span style="color:#719e07">(</span>NettyServerBuilder builder<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> builder<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 用来定制 gRPC NettyChannelBuilder |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">default</span> NettyChannelBuilder <span style="color:#268bd2">configureChannelBuilder</span><span style="color:#719e07">(</span>NettyChannelBuilder builder<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> builder<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 用来定制 gRPC CallOptions, 定义某个服务在每次请求间传递数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">default</span> CallOptions <span style="color:#268bd2">configureCallOptions</span><span style="color:#719e07">(</span>CallOptions options<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> options<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>以下是一个示例扩展实现:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MyGrpcConfigurator</span> <span style="color:#268bd2">implements</span> GrpcConfigurator <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> ExecutorService executor <span style="color:#719e07">=</span> Executors |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>newFixedThreadPool<span style="color:#719e07">(</span>200<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> NamedThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Customized-grpc&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> NettyServerBuilder <span style="color:#268bd2">configureServerBuilder</span><span style="color:#719e07">(</span>NettyServerBuilder builder<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> builder<span style="color:#719e07">.</span>executor<span style="color:#719e07">(</span>executor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> NettyChannelBuilder <span style="color:#268bd2">configureChannelBuilder</span><span style="color:#719e07">(</span>NettyChannelBuilder builder<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> builder<span style="color:#719e07">.</span>flowControlWindow<span style="color:#719e07">(</span>10<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> CallOptions <span style="color:#268bd2">configureCallOptions</span><span style="color:#719e07">(</span>CallOptions options<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> options<span style="color:#719e07">.</span>withOption<span style="color:#719e07">(</span>CallOptions<span style="color:#719e07">.</span>Key<span style="color:#719e07">.</span>create<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;key&#34;</span><span style="color:#719e07">),</span> <span style="color:#2aa198">&#34;value&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>配置为 Dubbo SPI,`resources/META-INF/services 增加配置文件</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span>default<span style="color:#719e07">=</span><span style="color:#2aa198">org.apache.dubbo.samples.basic.comtomize.MyGrpcConfigurator</span> |
| </span></span></code></pre></div><ol> |
| <li> |
| <p>指定 Provider 端线程池</p> |
| <p>默认用的是 Dubbo 的线程池,有 fixed (默认)、cached、direct 等类型。以下演示了切换为业务自定义线程池。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> ExecutorService executor <span style="color:#719e07">=</span> Executors |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>newFixedThreadPool<span style="color:#719e07">(</span>200<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> NamedThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Customized-grpc&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> NettyServerBuilder <span style="color:#268bd2">configureServerBuilder</span><span style="color:#719e07">(</span>NettyServerBuilder builder<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> builder<span style="color:#719e07">.</span>executor<span style="color:#719e07">(</span>executor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div></li> |
| <li> |
| <p>指定 Consumer 端限流值</p> |
| <p>设置 Consumer 限流值为 10</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> NettyChannelBuilder <span style="color:#268bd2">configureChannelBuilder</span><span style="color:#719e07">(</span>NettyChannelBuilder builder<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> builder<span style="color:#719e07">.</span>flowControlWindow<span style="color:#719e07">(</span>10<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div></li> |
| <li> |
| <p>传递附加参数</p> |
| <p>DemoService 服务调用传递 key</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> CallOptions <span style="color:#268bd2">configureCallOptions</span><span style="color:#719e07">(</span>CallOptions options<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getServiceInterface<span style="color:#719e07">().</span>equals<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;xxx.DemoService&#34;</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> options<span style="color:#719e07">.</span>withOption<span style="color:#719e07">(</span>CallOptions<span style="color:#719e07">.</span>Key<span style="color:#719e07">.</span>create<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;key&#34;</span><span style="color:#719e07">),</span> <span style="color:#2aa198">&#34;value&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> options<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div></li> |
| </ol> |
| <p><strong>三、TLS 配置</strong></p> |
| <p>配置方式和 Dubbo 提供的通用的 <a href="https://dubbo.apache.org/zh-cn/overview/mannual/java-sdk/advanced-features-and-usage/security/tls/">TLS 支持</a>一致,具体请参见文档</p> |
| <h3 id="示例-2-使用-protobuf-开发-dubbo-服务">示例 2, 使用 Protobuf 开发 Dubbo 服务</h3> |
| <p>下面,我们以一个<a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/serialization/dubbo-samples-protobuf">具体的示例</a>来看一下基于 Protobuf 的 Dubbo 服务开发流程。</p> |
| <h4 id="1-定义服务">1. 定义服务</h4> |
| <p>通过标准 Protobuf 定义服务</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>syntax = <span style="color:#2aa198">&#34;proto3&#34;</span>; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>option java_multiple_files = <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span>option java_package = <span style="color:#2aa198">&#34;org.apache.dubbo.demo&#34;</span>; |
| </span></span><span style="display:flex;"><span>option java_outer_classname = <span style="color:#2aa198">&#34;DemoServiceProto&#34;</span>; |
| </span></span><span style="display:flex;"><span>option objc_class_prefix = <span style="color:#2aa198">&#34;DEMOSRV&#34;</span>; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">package</span> demoservice; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// The demo service definition. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>service DemoService { |
| </span></span><span style="display:flex;"><span> rpc <span style="color:#268bd2">SayHello</span> (HelloRequest) <span style="color:#268bd2">returns</span> (HelloReply) {} |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// The request message containing the user&#39;s name. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>message HelloRequest { |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">string</span> name = <span style="color:#2aa198">1</span>; |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// The response message containing the greetings |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>message HelloReply { |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">string</span> message = <span style="color:#2aa198">1</span>; |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>这里定义了一个 DemoService 服务,服务只包含一个 sayHello 方法,同时定义了方法的入参和出参。</p> |
| <h4 id="2-compiler-编译服务">2. Compiler 编译服务</h4> |
| <ol> |
| <li>引入 Protobuf Compiler Maven 插件,同时指定 <code>protoc-gen-dubbo-java</code> RPC 扩展</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;plugin&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.xolstice.maven.plugins<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>protobuf-maven-plugin<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>0.5.1<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;protocArtifact&gt;</span>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/protocArtifact&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;pluginId&gt;</span>dubbo-grpc-java<span style="color:#268bd2">&lt;/pluginId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;pluginArtifact&gt;</span>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}<span style="color:#268bd2">&lt;/pluginArtifact&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;outputDirectory&gt;</span>build/generated/source/proto/main/java<span style="color:#268bd2">&lt;/outputDirectory&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;clearOutputDirectory&gt;</span>false<span style="color:#268bd2">&lt;/clearOutputDirectory&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;pluginParameter&gt;</span>dubbo<span style="color:#268bd2">&lt;/pluginParameter&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/configuration&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;executions&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goal&gt;</span>compile<span style="color:#268bd2">&lt;/goal&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;goal&gt;</span>compile-custom<span style="color:#268bd2">&lt;/goal&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/goals&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/execution&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/executions&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/plugin&gt;</span> |
| </span></span></code></pre></div><p>注意,这里与 <a href="https://github.com/apache/dubbo-samples/tree/master/99-integration/dubbo-samples-grpc">Dubbo 对 gRPC</a> 支持部分的区别在于: |
| <code> &lt;pluginParameter&gt;dubbo&lt;/pluginParameter&gt;</code></p> |
| <ol start="2"> |
| <li> |
| <p>生成 Dubbo stub</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#586e75"># 运行以下 maven 命令</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">$mvn</span> clean compile |
| </span></span></code></pre></div><p>生成的 Java 类如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/grpc/compiler-protobuf.png" alt="image-20191028201240976"></p> |
| <p>DemoServiceDubbo 为 Dubbo 定制的 stub</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">final</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceDubbo</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> AtomicBoolean registered <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> AtomicBoolean<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> Class<span style="color:#719e07">&lt;?&gt;</span> init<span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;</span> clazz <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> clazz <span style="color:#719e07">=</span> Class<span style="color:#719e07">.</span>forName<span style="color:#719e07">(</span>DemoServiceDubbo<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registered<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>serialize<span style="color:#719e07">.</span>protobuf<span style="color:#719e07">.</span>support<span style="color:#719e07">.</span>ProtobufUtils<span style="color:#719e07">.</span>marshaller<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>demo<span style="color:#719e07">.</span>HelloRequest<span style="color:#719e07">.</span>getDefaultInstance<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>serialize<span style="color:#719e07">.</span>protobuf<span style="color:#719e07">.</span>support<span style="color:#719e07">.</span>ProtobufUtils<span style="color:#719e07">.</span>marshaller<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>demo<span style="color:#719e07">.</span>HelloReply<span style="color:#719e07">.</span>getDefaultInstance<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>ClassNotFoundException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> clazz<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">DemoServiceDubbo</span><span style="color:#719e07">()</span> <span style="color:#719e07">{}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> String SERVICE_NAME <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;demoservice.DemoService&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Code generated for Dubbo |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">IDemoService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">static</span> Class<span style="color:#719e07">&lt;?&gt;</span> clazz <span style="color:#719e07">=</span> init<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>demo<span style="color:#719e07">.</span>HelloReply <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>demo<span style="color:#719e07">.</span>HelloRequest request<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> java<span style="color:#719e07">.</span>util<span style="color:#719e07">.</span>concurrent<span style="color:#719e07">.</span>CompletableFuture<span style="color:#719e07">&lt;</span>org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>demo<span style="color:#719e07">.</span>HelloReply<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>demo<span style="color:#719e07">.</span>HelloRequest request<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>最值得注意的是 <code>IDemoService</code> 接口,它会作为 Dubbo 服务定义基础接口。</p> |
| </li> |
| </ol> |
| <h4 id="3-开发业务逻辑">3. 开发业务逻辑</h4> |
| <p>从这一步开始,所有开发流程就和直接定义 Java 接口一样了。实现接口定义业务逻辑。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceImpl</span> <span style="color:#268bd2">implements</span> DemoServiceDubbo<span style="color:#719e07">.</span>IDemoService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> Logger logger <span style="color:#719e07">=</span> LoggerFactory<span style="color:#719e07">.</span>getLogger<span style="color:#719e07">(</span>DemoServiceImpl<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> HelloReply <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>HelloRequest request<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> request<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, request from consumer: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getContext<span style="color:#719e07">().</span>getRemoteAddress<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> HelloReply<span style="color:#719e07">.</span>newBuilder<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setMessage<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> request<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, response from provider: &#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getContext<span style="color:#719e07">().</span>getLocalAddress<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> CompletableFuture<span style="color:#719e07">&lt;</span>HelloReply<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>HelloRequest request<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture<span style="color:#719e07">.</span>completedFuture<span style="color:#719e07">(</span>sayHello<span style="color:#719e07">(</span>request<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="4-配置-provider">4. 配置 Provider</h4> |
| <p>暴露 Dubbo 服务</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:application</span> name=<span style="color:#2aa198">&#34;demo-provider&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:protocol</span> name=<span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.demo.provider.DemoServiceImpl&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.demo.DemoServiceDubbo$IDemoService&#34;</span> ref=<span style="color:#2aa198">&#34;demoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ClassPathXmlApplicationContext context <span style="color:#719e07">=</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ClassPathXmlApplicationContext<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;spring/dubbo-provider.xml&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> context<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>in<span style="color:#719e07">.</span>read<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="5-配置-consumer">5. 配置 Consumer</h4> |
| <p>引用 Dubbo 服务</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:application</span> name=<span style="color:#2aa198">&#34;demo-consumer&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> check=<span style="color:#2aa198">&#34;false&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.demo.DemoServiceDubbo$IDemoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ClassPathXmlApplicationContext context <span style="color:#719e07">=</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ClassPathXmlApplicationContext<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;spring/dubbo-consumer.xml&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> context<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> IDemoService demoService <span style="color:#719e07">=</span> context<span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;demoService&#34;</span><span style="color:#719e07">,</span> IDemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> HelloRequest request <span style="color:#719e07">=</span> HelloRequest<span style="color:#719e07">.</span>newBuilder<span style="color:#719e07">().</span>setName<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello&#34;</span><span style="color:#719e07">).</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> HelloReply reply <span style="color:#719e07">=</span> demoService<span style="color:#719e07">.</span>sayHello<span style="color:#719e07">(</span>request<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;result: &#34;</span> <span style="color:#719e07">+</span> reply<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>in<span style="color:#719e07">.</span>read<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div></description></item><item><title>Blog: 本地存根和本地伪装</title><link>https://dubbo.apache.org/zh-cn/blog/2019/10/22/%E6%9C%AC%E5%9C%B0%E5%AD%98%E6%A0%B9%E5%92%8C%E6%9C%AC%E5%9C%B0%E4%BC%AA%E8%A3%85/</link><pubDate>Tue, 22 Oct 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/10/22/%E6%9C%AC%E5%9C%B0%E5%AD%98%E6%A0%B9%E5%92%8C%E6%9C%AC%E5%9C%B0%E4%BC%AA%E8%A3%85/</guid><description> |
| <h2 id="基本概念">基本概念</h2> |
| <p>典型的 RPC 调用客户端是依赖并且只依赖接口编程来进行远程调用的。在真正发起远程调用之前,用户往往需要做一些预处理的工作,比如提前校验参数。在拿到返回调用结果之后,用户可能需要缓存结果,或者是在调用失败的时候构造容错数据,而不是简单的抛出异常。</p> |
| <p>这个时候,用户可以编写出类似以下的代码来处理上面提出的这些场景:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> preProcess<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> service<span style="color:#719e07">.</span>invoke<span style="color:#719e07">(...);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> mockValue<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> postProcess<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>类似的,用户也可以通过面向切面编程 <em>AOP</em> 的高级技巧来解决上面的诉求,比如通过 <em>Spring AOP</em> 的方式可以通过类似下面的这段配置来完成。使用 <em>AOP</em> 的技巧相比上面的代码来说,避免了容错处理等与业务无关的代码对业务代码的侵入,使得业务处理主逻辑更简洁。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;demo-service-stub&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.demo.DemoServiceStub&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;demo-service-mock&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.demo.DemoServiceMock&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;aop:config&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;aop:aspect</span> id=<span style="color:#2aa198">&#34;stub&#34;</span> ref=<span style="color:#2aa198">&#34;demo-service-stub&#34;</span><span style="color:#268bd2">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;aop:pointcut</span> id=<span style="color:#2aa198">&#34;stubPointcut&#34;</span> expression=<span style="color:#2aa198">&#34;execution(* org.apache.dubbo.samples.DemoService+.*(..))&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;aop:before</span> method=<span style="color:#2aa198">&#34;preProcess&#34;</span> pointcut-ref=<span style="color:#2aa198">&#34;stubPointcut&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;aop:after-returning</span> method=<span style="color:#2aa198">&#34;postProcess&#34;</span> pointcut-ref=<span style="color:#2aa198">&#34;stubPointcut&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/aop:aspect&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;aop:aspect</span> id=<span style="color:#2aa198">&#34;mock&#34;</span> ref=<span style="color:#2aa198">&#34;demo-service-mock&#34;</span><span style="color:#268bd2">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;aop:pointcut</span> id=<span style="color:#2aa198">&#34;mockPointcut&#34;</span> expression=<span style="color:#2aa198">&#34;execution(* org.apache.dubbo.samples.DemoService+.*(..))&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;aop:after-throwing</span> method=<span style="color:#2aa198">&#34;mock&#34;</span> pointcut-ref=<span style="color:#2aa198">&#34;mockPointcut&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/aop:aspect&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/aop:config&gt;</span> |
| </span></span></code></pre></div><p>为了进一步的方便用户做 Dubbo 开发,框架提出了本地存根 <em>Stub</em> 和本地伪装 <em>Mock</em> 的概念。通过约定大于配置的理念,进一步的简化了配置,使用起来更加方便,并且不依赖额外的 <em>AOP</em> 框架就达到了 <em>AOP</em> 的效果。</p> |
| <p>本地存根的工作方式与 <em>AOP</em> 的 <strong>around</strong> advice 类似,而本地伪装的工作方式等同于 <em>AOP</em> 中的 <strong>after-throwing</strong> advice,也就是说,只有当远程调用发生 <em>exception</em> 的时候才会执行本地伪装。本地存根和本地伪装的工作流程如下图所示:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-mock-stub-flow.png" alt="dubbo-mock-stub-flow"></p> |
| <ol> |
| <li>服务消费者发起调用</li> |
| <li>如果服务消费者端存在本地存根 <em>Stub</em> 的话,会先执行本地存根</li> |
| <li>本地存根 Stub 持有远程服务的 <em>Proxy</em> 对象,<em>Stub</em> 在执行的时候,会先执行自己的逻辑 (<em>before</em>),然后通过 <em>Proxy</em> 发起远程调用,最后在返回过程之前也会执行自己的逻辑 (<em>after-returning</em>)</li> |
| <li>如果远程服务的 <em>Proxy</em> 对象在执行过程中抛出了 <em>exception</em>,会执行服务消费端的本地伪装 <em>Mock</em> 的逻辑 (<em>after-throwing</em>),返回容错数据,从而达到服务降级的目的</li> |
| </ol> |
| <h2 id="开发一个本地存根-stub">开发一个本地存根 Stub</h2> |
| <p>本地存根 <em>Stub</em> 由用户来提供,并在服务消费方部署。完整的示例可以在这里 <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> 获得。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceStub</span> <span style="color:#268bd2">implements</span> DemoService <span style="color:#719e07">{</span> <span style="color:#586e75">// #1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> Logger logger <span style="color:#719e07">=</span> LoggerFactory<span style="color:#719e07">.</span>getLogger<span style="color:#719e07">(</span>DemoServiceStub<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> DemoService demoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">DemoServiceStub</span><span style="color:#719e07">(</span>DemoService demoService<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> <span style="color:#586e75">// #2 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>demoService <span style="color:#719e07">=</span> demoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> <span style="color:#586e75">// #3 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;before execute remote service, parameter: &#34;</span> <span style="color:#719e07">+</span> name<span style="color:#719e07">);</span> <span style="color:#586e75">// #4 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String result <span style="color:#719e07">=</span> demoService<span style="color:#719e07">.</span>sayHello<span style="color:#719e07">(</span>name<span style="color:#719e07">);</span> <span style="color:#586e75">// #5 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;after execute remote service, result: &#34;</span> <span style="color:#719e07">+</span> result<span style="color:#719e07">);</span> <span style="color:#586e75">// #6 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;fail to execute service&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> <span style="color:#586e75">// #7 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>要和框架在一起工作,本地存根的实现需要遵循一些与框架事先做出的约定:</p> |
| <ol> |
| <li>首先本地存根 <em>Stub</em> 是服务接口的一个实现</li> |
| <li>本地存根的实现需要提供一个拷贝构造方法,方便框架将远程调用的 <em>Proxy</em> 对象注入进来</li> |
| <li>同样的,本地存根需要提供服务接口中所有方法的实现。在本例中,需要实现 <em>sayHello</em> 方法</li> |
| <li>在真正发起远程调用之前,用户可以在本地执行一些操作。在本例中,在日志中记录传入的参数</li> |
| <li>通过框架传入的 <em>Proxy</em> 对象真正发起远程调用</li> |
| <li>在远程调用结束后,也可以加入本地代码的执行。在本例中,在日志中记录远程调用的返回结果</li> |
| <li>如果发生错误的时候,也可以做一些错误恢复的动作。在本例中,在日志中记录异常。当然,如果提供了本地伪装的话,<em>catch</em> 中的逻辑是可以省略掉的</li> |
| </ol> |
| <p>其中步骤 4、步骤 6、和步骤 7 共同构建了等同于面向切面编程中的概念,分别对应于 <strong>before</strong>、<strong>after-returning</strong>、以及 <strong>after-throwing</strong>。</p> |
| <p><em>DemoServiceStub</em> 运行在客户端,要使用本地存根的话,还需要在 <em>stub-consumer.xml</em> 中配置属性 <em>stub</em>。可以简单的通过指定 <em>stub=&ldquo;true&rdquo;</em> 来告诉 Dubbo 框架使用本地存根,这个时候,本地存根的包名需要和服务接口的包名一致,类名必须在服务接口的类名后加上 <strong>Stub</strong> 的后缀。例如,当服务接口名是 <em>org.apache.dubbo.samples.stub.api.DemoService</em> 时,本地存根的全类名应该是 <em>org.apache.dubbo.samples.stub.api.DemoServiceStub</em>。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> check=<span style="color:#2aa198">&#34;false&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.stub.api.DemoService&#34;</span> stub=<span style="color:#2aa198">&#34;true&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>如果不希望使用默认的命名规则,也可以直接通过 <em>stub</em> 属性来指定本地存根的全类名。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> check=<span style="color:#2aa198">&#34;false&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.stub.api.DemoService&#34;</span> stub=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.stub.impl.DemoStub&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>启动服务端 <em>StubProvider</em> 后,再运行客户端 <em>StubConsumer</em>,可以通过观察客户端的日志来验证本地存根的运行结果。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#719e07">[</span>09/04/19 11:52:21:021 CST<span style="color:#719e07">]</span> main INFO api.DemoServiceStub: before execute remote service, parameter: dubbo |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">[</span>09/04/19 11:52:21:021 CST<span style="color:#719e07">]</span> main INFO api.DemoServiceStub: after execute remote service, result: greeting dubbo |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">[</span>09/04/19 11:52:21:021 CST<span style="color:#719e07">]</span> main INFO stub.StubConsumer: result: greeting dubbo |
| </span></span></code></pre></div><h2 id="开发一个本地伪装-mock">开发一个本地伪装 Mock</h2> |
| <p>上面说了本地伪装通常用于在远程调用出错的情况下服务降级。完整的示例可以在这里 <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> 获得。</p> |
| <p>这里通过在服务提供方的代码中睡眠来模拟调用端超时,从而执行本地伪装来做容错处理。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceImpl</span> <span style="color:#268bd2">implements</span> DemoService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Thread<span style="color:#719e07">.</span>sleep<span style="color:#719e07">(</span>5000<span style="color:#719e07">);</span> <span style="color:#586e75">// #1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>InterruptedException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> e<span style="color:#719e07">.</span>printStackTrace<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;hello &#34;</span> <span style="color:#719e07">+</span> name<span style="color:#719e07">;</span> <span style="color:#586e75">// #2 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ol> |
| <li>Dubbo 默认的超时时间是 <em>1000 ms</em>,这里通过睡眠 <em>5000ms</em> 来达到触发超时异常的发生</li> |
| <li>由于超时的发生,这个结果并不会被返回给客户端,取而代之的是 <em>org.apache.dubbo.remoting.TimeoutException</em></li> |
| </ol> |
| <p>在客户端提供本地伪装的实现。当远程调用发生错误的时候,返回给调用方的不是服务端的 &ldquo;hello name&rdquo;,取而代之的是 &ldquo;mock name&rdquo;。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceMock</span> <span style="color:#268bd2">implements</span> DemoService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> Logger logger <span style="color:#719e07">=</span> LoggerFactory<span style="color:#719e07">.</span>getLogger<span style="color:#719e07">(</span>DemoServiceMock<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;about to execute mock: &#34;</span> <span style="color:#719e07">+</span> DemoServiceMock<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;mock &#34;</span> <span style="color:#719e07">+</span> name<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>同样的,要使用本地伪装的话,还需要在 <em>mock-consumer.xml</em> 中配置属性 <em>mock</em>。可以简单的通过指定 <em>mock=&ldquo;true&rdquo;</em> 来告诉 Dubbo 框架使用本地伪装,这个时候,本地伪装的包名需要和服务接口的包名一致,类名必须在服务接口的类名后加上 <strong>Mock</strong> 的后缀。例如,当服务接口名是 <em>org.apache.dubbo.samples.stub.api.DemoService</em> 时,本地存根的全类名应该是 <em>org.apache.dubbo.samples.stub.api.DemoServiceMock</em>。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> check=<span style="color:#2aa198">&#34;false&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.mock.api.DemoService&#34;</span> |
| </span></span><span style="display:flex;"><span> mock=<span style="color:#2aa198">&#34;true&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>如果不希望使用默认的命名规则,也可以直接通过 <em>mock</em> 属性来指定本地伪装的全类名。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> check=<span style="color:#2aa198">&#34;false&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.mock.api.DemoService&#34;</span> mock=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.mock.impl.DemoMock&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>通过提供一个本地伪装的类,可以最大限度的控制出错之后的容错逻辑。有的时候,业务上并不需要这样灵活的机制,只有返回一个默认值的诉求,这个时候提供一个完整的本地伪装的实现就显得有点重了。或者线上出错的时候,应用并没有打包本地伪装,需要通过推送规则的方式临时对服务降级。Dubbo 框架为上面的这两种诉求都提供了快捷方式,帮助用户快速配置服务降级。</p> |
| <p>启动服务端 <em>MockProvider</em> 之后,然后再执行 <em>MockConsumer</em> 就可以看到下面的结果:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>Caused by: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer. start time: 2019-04-09 14:20:48.061, end time: 2019-04-09 14:20:49.077, client elapsed: <span style="color:#2aa198">0</span> ms, server elapsed: <span style="color:#2aa198">1015</span> ms, timeout: <span style="color:#2aa198">1000</span> ms, request: Request <span style="color:#719e07">[</span><span style="color:#268bd2">id</span><span style="color:#719e07">=</span>2, <span style="color:#268bd2">version</span><span style="color:#719e07">=</span>2.0.2, <span style="color:#268bd2">twoway</span><span style="color:#719e07">=</span>true, <span style="color:#268bd2">event</span><span style="color:#719e07">=</span>false, <span style="color:#268bd2">broken</span><span style="color:#719e07">=</span>false, <span style="color:#268bd2">data</span><span style="color:#719e07">=</span>RpcInvocation <span style="color:#719e07">[</span><span style="color:#268bd2">methodName</span><span style="color:#719e07">=</span>sayHello, <span style="color:#268bd2">parameterTypes</span><span style="color:#719e07">=[</span>class java.lang.String<span style="color:#719e07">]</span>, <span style="color:#268bd2">arguments</span><span style="color:#719e07">=[</span>world<span style="color:#719e07">]</span>, <span style="color:#268bd2">attachments</span><span style="color:#719e07">={</span><span style="color:#268bd2">path</span><span style="color:#719e07">=</span>org.apache.dubbo.samples.mock.api.DemoService, <span style="color:#268bd2">interface</span><span style="color:#719e07">=</span>org.apache.dubbo.samples.mock.api.DemoService, <span style="color:#268bd2">version</span><span style="color:#719e07">=</span>0.0.0<span style="color:#719e07">}]]</span>, channel: /30.5.125.99:56433 -&gt; /30.5.125.99:20880 |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.returnFromResponse<span style="color:#719e07">(</span>DefaultFuture.java:295<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get<span style="color:#719e07">(</span>DefaultFuture.java:191<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get<span style="color:#719e07">(</span>DefaultFuture.java:164<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke<span style="color:#719e07">(</span>DubboInvoker.java:108<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.AbstractInvoker.invoke<span style="color:#719e07">(</span>AbstractInvoker.java:157<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.monitor.support.MonitorFilter.invoke<span style="color:#719e07">(</span>MonitorFilter.java:88<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span style="color:#268bd2">$1</span>.invoke<span style="color:#719e07">(</span>ProtocolFilterWrapper.java:73<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke<span style="color:#719e07">(</span>FutureFilter.java:49<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span style="color:#268bd2">$1</span>.invoke<span style="color:#719e07">(</span>ProtocolFilterWrapper.java:73<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke<span style="color:#719e07">(</span>ConsumerContextFilter.java:54<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span style="color:#268bd2">$1</span>.invoke<span style="color:#719e07">(</span>ProtocolFilterWrapper.java:73<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke<span style="color:#719e07">(</span>ListenerInvokerWrapper.java:78<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke<span style="color:#719e07">(</span>InvokerWrapper.java:56<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke<span style="color:#719e07">(</span>FailoverClusterInvoker.java:80<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> ... <span style="color:#2aa198">5</span> more |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">[</span>09/04/19 02:20:49:049 CST<span style="color:#719e07">]</span> main WARN api.DemoServiceMock: about to execute mock: DemoServiceMock |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">[</span>09/04/19 02:20:49:049 CST<span style="color:#719e07">]</span> main INFO mock.MockConsumer: result: mock world |
| </span></span></code></pre></div><p>下面通过规则推送为例展示这种快捷方式的用法,更多的用法请参考 Dubbo 官方用户手册 <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。通过向配置中心推送指定服务的配置,就可以做到动态服务降级的目的。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>--- <span style="color:#586e75"># 1</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">configVersion</span>: v2.7 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">scope</span>: service |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">key</span>: org.apache.dubbo.samples.mock.api.DemoService <span style="color:#586e75">#2</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">enabled</span>: <span style="color:#cb4b16">true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">configs</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">addresses</span>: [<span style="color:#2aa198">0.0.0.0</span>] |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">side</span>: consumer <span style="color:#586e75">#3</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">parameters</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">mock</span>: return configured-mock-value <span style="color:#586e75">#4</span> |
| </span></span><span style="display:flex;"><span> ... |
| </span></span></code></pre></div><ol> |
| <li>以 <em>Zookeeper</em> 为例,规则的路径是 /dubbo/config/org.apache.dubbo.samples.mock.api.DemoService/configurators</li> |
| <li>该规则作用在 <em>org.apache.dubbo.samples.mock.api.DemoService</em> 服务上</li> |
| <li>该规则作用在客户端</li> |
| <li>当错误发送时,对服务的调用返回默认值 <em>configured-mock-value</em></li> |
| </ol> |
| <p>启动服务端 <em>MockProvider</em> 之后,再执行例子<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>中的 <em>Configurator</em> 完成对服务降级规则的配置,然后再执行 <em>MockConsumer</em> 就可以看到下面的结果:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#719e07">[</span>09/04/19 02:19:01:001 CST<span style="color:#719e07">]</span> main INFO integration.AbstractConfiguratorListener: <span style="color:#719e07">[</span>DUBBO<span style="color:#719e07">]</span> Notification of overriding rule, change <span style="color:#b58900">type</span> is: MODIFIED, raw config content is: |
| </span></span><span style="display:flex;"><span> --- |
| </span></span><span style="display:flex;"><span>configVersion: v2.7 |
| </span></span><span style="display:flex;"><span>scope: service |
| </span></span><span style="display:flex;"><span>key: org.apache.dubbo.samples.mock.api.DemoService |
| </span></span><span style="display:flex;"><span>enabled: <span style="color:#b58900">true</span> |
| </span></span><span style="display:flex;"><span>configs: |
| </span></span><span style="display:flex;"><span>- addresses: <span style="color:#719e07">[</span>0.0.0.0<span style="color:#719e07">]</span> |
| </span></span><span style="display:flex;"><span> side: consumer |
| </span></span><span style="display:flex;"><span> parameters: |
| </span></span><span style="display:flex;"><span> mock: <span style="color:#719e07">return</span> configured-mock-value |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span>, dubbo version: 2.7.1, current host: 30.5.125.99 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>Caused by: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout. start time: 2019-04-09 14:19:03.737, end time: 2019-04-09 14:19:04.741, client elapsed: <span style="color:#2aa198">1</span> ms, server elapsed: <span style="color:#2aa198">1002</span> ms, timeout: <span style="color:#2aa198">1000</span> ms, request: Request <span style="color:#719e07">[</span><span style="color:#268bd2">id</span><span style="color:#719e07">=</span>2, <span style="color:#268bd2">version</span><span style="color:#719e07">=</span>2.0.2, <span style="color:#268bd2">twoway</span><span style="color:#719e07">=</span>true, <span style="color:#268bd2">event</span><span style="color:#719e07">=</span>false, <span style="color:#268bd2">broken</span><span style="color:#719e07">=</span>false, <span style="color:#268bd2">data</span><span style="color:#719e07">=</span>RpcInvocation <span style="color:#719e07">[</span><span style="color:#268bd2">methodName</span><span style="color:#719e07">=</span>sayHello, <span style="color:#268bd2">parameterTypes</span><span style="color:#719e07">=[</span>class java.lang.String<span style="color:#719e07">]</span>, <span style="color:#268bd2">arguments</span><span style="color:#719e07">=[</span>world<span style="color:#719e07">]</span>, <span style="color:#268bd2">attachments</span><span style="color:#719e07">={</span><span style="color:#268bd2">path</span><span style="color:#719e07">=</span>org.apache.dubbo.samples.mock.api.DemoService, <span style="color:#268bd2">interface</span><span style="color:#719e07">=</span>org.apache.dubbo.samples.mock.api.DemoService, <span style="color:#268bd2">version</span><span style="color:#719e07">=</span>0.0.0<span style="color:#719e07">}]]</span>, channel: /30.5.125.99:56412 -&gt; /30.5.125.99:20880 |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get<span style="color:#719e07">(</span>DefaultFuture.java:188<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get<span style="color:#719e07">(</span>DefaultFuture.java:164<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke<span style="color:#719e07">(</span>DubboInvoker.java:108<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.AbstractInvoker.invoke<span style="color:#719e07">(</span>AbstractInvoker.java:157<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.monitor.support.MonitorFilter.invoke<span style="color:#719e07">(</span>MonitorFilter.java:88<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span style="color:#268bd2">$1</span>.invoke<span style="color:#719e07">(</span>ProtocolFilterWrapper.java:73<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke<span style="color:#719e07">(</span>FutureFilter.java:49<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span style="color:#268bd2">$1</span>.invoke<span style="color:#719e07">(</span>ProtocolFilterWrapper.java:73<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke<span style="color:#719e07">(</span>ConsumerContextFilter.java:54<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper<span style="color:#268bd2">$1</span>.invoke<span style="color:#719e07">(</span>ProtocolFilterWrapper.java:73<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke<span style="color:#719e07">(</span>ListenerInvokerWrapper.java:78<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke<span style="color:#719e07">(</span>InvokerWrapper.java:56<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke<span style="color:#719e07">(</span>FailoverClusterInvoker.java:80<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> ... <span style="color:#2aa198">5</span> more |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">[</span>09/04/19 02:19:04:004 CST<span style="color:#719e07">]</span> main INFO mock.MockConsumer: result: configured-mock-value |
| </span></span></code></pre></div><h2 id="总结">总结</h2> |
| <p>本文介绍了 Dubbo 中本地存根和本地伪装的概念和用法。从本质来讲,本地存根和本地伪装等同于面向切面编程中的概念,通过诸如 Spring 框架提供的 <em>AOP</em> 编程可以达到同样的目的。通过本文如何开发一个本地存根和本地伪装的示例,读者可以直观的感受到通过框架提供的机制更加方便快捷。同时,对于很多简单的场景和动态配置推送的场景,框架提供了仅通过配置而无需编码的方式来满足,进一步提升了框架使用者的效率。</p> |
| <div class="footnotes" role="doc-endnotes"> |
| <hr> |
| <ol> |
| <li id="fn:1"> |
| <p><a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-stub">https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-stub</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> |
| </li> |
| <li id="fn:2"> |
| <p><a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-mock">https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-mock</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> |
| </li> |
| <li id="fn:3"> |
| <p>/zh-cn/docsv2.7/user/examples/local-mock/&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> |
| </li> |
| </ol> |
| </div></description></item><item><title>Blog: Dubbo 中的 URL 统一模型</title><link>https://dubbo.apache.org/zh-cn/blog/2019/10/17/dubbo-%E4%B8%AD%E7%9A%84-url-%E7%BB%9F%E4%B8%80%E6%A8%A1%E5%9E%8B/</link><pubDate>Thu, 17 Oct 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/10/17/dubbo-%E4%B8%AD%E7%9A%84-url-%E7%BB%9F%E4%B8%80%E6%A8%A1%E5%9E%8B/</guid><description> |
| <h3 id="定义">定义</h3> |
| <p>在不谈及 dubbo 时,我们大多数人对 URL 这个概念并不会感到陌生。统一资源定位器 (<a href="https://www.ietf.org/rfc/rfc1738.txt">RFC1738</a>――Uniform Resource Locators (URL))应该是最广为人知的一个 RFC 规范,它的定义也非常简单</p> |
| <blockquote> |
| <p>因特网上的可用资源可以用简单字符串来表示,该文档就是描述了这种字符串的语法和语 |
| 义。而这些字符串则被称为:“统一资源定位器”(URL)</p> |
| </blockquote> |
| <p><strong>一个标准的 URL 格式</strong>至多可以包含如下的几个部分</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>protocol://username:password@host:port/path?key=value&amp;key=value |
| </span></span></code></pre></div><p><strong>一些典型 URL</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>http://www.facebook.com/friends?param1=value1&amp;amp;param2=value2 |
| </span></span><span style="display:flex;"><span>https://username:password@10.20.130.230:8080/list?version=1.0.0 |
| </span></span><span style="display:flex;"><span>ftp://username:password@192.168.1.7:21/1/read.txt |
| </span></span></code></pre></div><p>当然,也有一些<strong>不太符合常规的 URL</strong>,也被归类到了 URL 之中</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>192.168.1.3:20880 |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = 192.168.1.3, port = 20880, url path = null |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:///home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = null, url path = home/user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file://home/user1/router.js?type=script&lt;br&gt; |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = home, url path = user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:///D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = null, url path = D:/1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:/D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span>同上 file:///D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>/home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = null, url path = home/user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = home, url path = user1/router.js |
| </span></span></code></pre></div><h3 id="dubbo-中的-url">Dubbo 中的 URL</h3> |
| <p>在 dubbo 中,也使用了类似的 URL,主要用于在各个扩展点之间传递数据,组成此 URL 对象的具体参数如下:</p> |
| <ul> |
| <li>protocol:一般是 dubbo 中的各种协议 如:dubbo thrift http zk</li> |
| <li>username/password:用户名/密码</li> |
| <li>host/port:主机/端口</li> |
| <li>path:接口名称</li> |
| <li>parameters:参数键值对</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">URL</span><span style="color:#719e07">(</span>String protocol<span style="color:#719e07">,</span> String username<span style="color:#719e07">,</span> String password<span style="color:#719e07">,</span> String host<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> port<span style="color:#719e07">,</span> String path<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> parameters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>username <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> username<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> password <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> password<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Invalid url, password without username!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>protocol <span style="color:#719e07">=</span> protocol<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>username <span style="color:#719e07">=</span> username<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>password <span style="color:#719e07">=</span> password<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>host <span style="color:#719e07">=</span> host<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>port <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>port <span style="color:#719e07">&lt;</span> 0 <span style="color:#719e07">?</span> 0 <span style="color:#719e07">:</span> port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>path <span style="color:#719e07">=</span> path<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// trim the beginning &#34;/&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">while</span><span style="color:#719e07">(</span>path <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> path<span style="color:#719e07">.</span>startsWith<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;/&#34;</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> path<span style="color:#719e07">.</span>substring<span style="color:#719e07">(</span>1<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>parameters <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> parameters <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> parameters <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;(</span>parameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>parameters <span style="color:#719e07">=</span> Collections<span style="color:#719e07">.</span>unmodifiableMap<span style="color:#719e07">(</span>parameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看出,dubbo 认为 protocol,username,passwored,host,port,path 是主要的 URL 参数,其他键值对存放在 parameters 之中。</p> |
| <p><strong>一些典型的 Dubbo URL</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000 |
| </span></span><span style="display:flex;"><span>描述一个 dubbo 协议的服务 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.RegistryService&amp;pid=1214&amp;qos.port=33333&amp;timestamp=1545721981946 |
| </span></span><span style="display:flex;"><span>描述一个 zookeeper 注册中心 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>consumer://30.5.120.217/org.apache.dubbo.demo.DemoService?application=demo-consumer&amp;category=consumers&amp;check=false&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=1209&amp;qos.port=33333&amp;side=consumer&amp;timestamp=1545721827784 |
| </span></span><span style="display:flex;"><span>描述一个消费者 |
| </span></span></code></pre></div><p>可以说,任意的一个领域中的一个实现都可以认为是一类 URL,dubbo 使用 URL 来统一描述了元数据,配置信息,贯穿在整个框架之中。</p> |
| <h3 id="url-相关的生命周期">URL 相关的生命周期</h3> |
| <h4 id="rpc调用">RPC调用</h4> |
| <p>从地址发现的视角,URL 代表了一条可用的 provider 实例地址,除了地址信息之外还有相关的配置信息,这些配置信息是层次化的,有 provider 侧指定的配置值、consumer 侧指定的配置值、接口级别的配置值、方法级别的配置值等。这些 URL 配置值将直接影响消费端的 RPC 调用行为。</p> |
| <p>以 timeout 为例,下图显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似:</p> |
| <ul> |
| <li>方法级优先,接口级次之,全局配置再次之。</li> |
| <li>如果级别一样,则消费方优先,提供方次之。</li> |
| </ul> |
| <p>其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。</p> |
| <p><img src="https://dubbo.apache.org/imgs/user/dubbo-config-override.jpg" alt="dubbo-config-override"></p> |
| <p>(建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置)。</p> |
| <p>理论上 ReferenceConfig 中除了<code>interface</code>这一项,其他所有配置项都可以缺省不配置,框架会自动使用ConsumerConfig,ServiceConfig, ProviderConfig等提供的缺省配置。</p> |
| <h4 id="解析服务">解析服务</h4> |
| <p>基于 dubbo.jar 内的 <code>META-INF/spring.handlers</code> 配置,Spring 在遇到 dubbo 名称空间时,会回调 <code>DubboNamespaceHandler</code>。</p> |
| <p>所有 dubbo 的标签,都统一用 <code>DubboBeanDefinitionParser</code> 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。</p> |
| <p>在 <code>ServiceConfig.export()</code> 或 <code>ReferenceConfig.get()</code> 初始化时,将 Bean 对象转换 URL 格式,所有 Bean 属性转成 URL 的参数。</p> |
| <p>然后将 URL 传给协议扩展点,基于扩展点自适应机制,根据 URL 的协议头,进行不同协议的服务暴露或引用。</p> |
| <h4 id="暴露服务">暴露服务</h4> |
| <p><strong>1. 只暴露服务端口:</strong></p> |
| <p>在没有注册中心,直接暴露提供者的情况下,<code>ServiceConfig</code> 解析出的 URL 的格式为:<code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>。</p> |
| <p>基于扩展点自适应机制,通过 URL 的 <code>dubbo://</code> 协议头识别,直接调用 <code>DubboProtocol</code>的 <code>export()</code> 方法,打开服务端口。</p> |
| <p><strong>2. 向注册中心暴露服务:</strong></p> |
| <p>在有注册中心,需要注册提供者地址的情况下,<code>ServiceConfig</code> 解析出的 URL 的格式为: <code>registry://registry-host/org.apache.dubbo.registry.RegistryService?export=URL.encode(&quot;dubbo://service-host/com.foo.FooService?version=1.0.0&quot;)</code>,</p> |
| <p>基于扩展点自适应机制,通过 URL 的 <code>registry://</code> 协议头识别,就会调用 <code>RegistryProtocol</code> 的 <code>export()</code> 方法,将 <code>export</code> 参数中的提供者 URL,先注册到注册中心。</p> |
| <p>再重新传给 <code>Protocol</code> 扩展点进行暴露: <code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>,然后基于扩展点自适应机制,通过提供者 URL 的 <code>dubbo://</code> 协议头识别,就会调用 <code>DubboProtocol</code> 的 <code>export()</code> 方法,打开服务端口。</p> |
| <h4 id="引用服务">引用服务</h4> |
| <p><strong>1. 直连引用服务:</strong></p> |
| <p>在没有注册中心,直连提供者的情况下,<code>ReferenceConfig</code> 解析出的 URL 的格式为:<code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>。</p> |
| <p>基于扩展点自适应机制,通过 URL 的 <code>dubbo://</code> 协议头识别,直接调用 <code>DubboProtocol</code> 的 <code>refer()</code> 方法,返回提供者引用。</p> |
| <p><strong>2. 从注册中心发现引用服务:</strong></p> |
| <p>在有注册中心,通过注册中心发现提供者地址的情况下,<code>ReferenceConfig</code> 解析出的 URL 的格式为:<code>registry://registry-host/org.apache.dubbo.registry.RegistryService?refer=URL.encode(&quot;consumer://consumer-host/com.foo.FooService?version=1.0.0&quot;)</code>。</p> |
| <p>基于扩展点自适应机制,通过 URL 的 <code>registry://</code> 协议头识别,就会调用 <code>RegistryProtocol</code> 的 <code>refer()</code> 方法,基于 <code>refer</code> 参数中的条件,查询提供者 URL,如: <code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>。</p> |
| <p>基于扩展点自适应机制,通过提供者 URL 的 <code>dubbo://</code> 协议头识别,就会调用 <code>DubboProtocol</code> 的 <code>refer()</code> 方法,得到提供者引用。</p> |
| <p>然后 <code>RegistryProtocol</code> 将多个提供者引用,通过 <code>Cluster</code> 扩展点,伪装成单个提供者引用返回。</p> |
| <h3 id="url-统一模型的意义">URL 统一模型的意义</h3> |
| <p>对于 dubbo 中的 URL,有人理解为配置总线,有人理解为统一配置模型,说法虽然不同,但都是在表达一个意思,这样的 URL 在 dubbo 中被当做是<a href="https://dubbo.apache.org/zh-cn/docsv2.7/dev/contract/">公共契约</a>,所有扩展点参数都包含 URL 参数,URL 作为上下文信息贯穿整个扩展点设计体系。</p> |
| <p>在没有 URL 之前,只能以字符串传递参数,不停的解析和拼装,导致相同类型的接口,参数时而 Map, 时而 Parameters 类包装:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>export<span style="color:#719e07">(</span>String url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span>createExporter<span style="color:#719e07">(</span>String host<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> port<span style="color:#719e07">,</span> Parameters params<span style="color:#719e07">)</span> |
| </span></span></code></pre></div><p>使用 URL 一致性模型:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>export<span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span>createExporter<span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> |
| </span></span></code></pre></div><p>在最新的 dubbo 代码中,我们可以看到大量使用 URL 来进行上下文之间信息的传递,这样的好处是显而易见的:</p> |
| <ol> |
| <li>使得代码编写者和阅读者能够将一系列的参数联系起来,进而形成规范,使得代码易写,易读。</li> |
| <li>可扩展性强,URL 相当于参数的集合(相当于一个 Map),他所表达的含义比单个参数更丰富,当我们在扩展代码时,可以将新的参数追加到 URL 之中,而不需要改变入参,返参的结构。</li> |
| <li>统一模型,它位于 org.apache.dubbo.common 包中,各个扩展模块都可以使用它作为参数的表达形式,简化了概念,降低了代码的理解成本。</li> |
| </ol> |
| <p>如果你能够理解 final 契约和 restful 契约,那我相信你会很好地理解 URL 契约。契约的好处我还是啰嗦一句:大家都这么做,就形成了默契,沟通是一件很麻烦的事,统一 URL 模型可以省去很多沟通成本,这边是 URL 统一模型存在的意义。</p></description></item><item><title>Blog: 研究 Dubbo 网卡地址注册时的一点思考</title><link>https://dubbo.apache.org/zh-cn/blog/2019/10/01/%E7%A0%94%E7%A9%B6-dubbo-%E7%BD%91%E5%8D%A1%E5%9C%B0%E5%9D%80%E6%B3%A8%E5%86%8C%E6%97%B6%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83/</link><pubDate>Tue, 01 Oct 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/10/01/%E7%A0%94%E7%A9%B6-dubbo-%E7%BD%91%E5%8D%A1%E5%9C%B0%E5%9D%80%E6%B3%A8%E5%86%8C%E6%97%B6%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83/</guid><description> |
| <h2 id="1-如何选择合适的网卡地址">1 如何选择合适的网卡地址</h2> |
| <p>可能相当一部分人还不知道我这篇文章到底要讲什么,我说个场景,大家应该就明晰了。在分布式服务调用过程中,以 Dubbo 为例,服务提供者往往需要将自身的 IP 地址上报给注册中心,供消费者去发现。在大多数情况下 Dubbo 都可以正常工作,但如果你留意过 Dubbo 的 github issue,其实有不少人反馈:Dubbo Provider 注册了错误的 IP。如果你能立刻联想到:多网卡、内外网地址共存、VPN、虚拟网卡等关键词,那我建议你一定要继续将本文看下去,因为我也想到了这些,它们都是本文所要探讨的东西!那么“如何选择合适的网卡地址”呢,Dubbo 现有的逻辑到底算不算完备?我们不急着回答它,而是带着这些问题一起进行研究,相信到文末,其中答案,各位看官自有评说。</p> |
| <h2 id="2-dubbo-是怎么做的">2 Dubbo 是怎么做的</h2> |
| <p>Dubbo 获取网卡地址的逻辑在各个版本中也是千回百转,走过弯路,也做过优化,我们用最新的 2.7.2-SNAPSHOT 版本来介绍,在看以下源码时,大家可以怀着质疑的心态去阅读,在 dubbo github 的 master 分支可以获取源码。获取 localhost 的逻辑位于 <code>org.apache.dubbo.common.utils.NetUtils#getLocalAddress0()</code> 之中</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> InetAddress <span style="color:#268bd2">getLocalAddress0</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> InetAddress localAddress <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 首先尝试获取 /etc/hosts 中 hostname 对应的 IP |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> localAddress <span style="color:#719e07">=</span> InetAddress<span style="color:#719e07">.</span>getLocalHost<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Optional<span style="color:#719e07">&lt;</span>InetAddress<span style="color:#719e07">&gt;</span> addressOp <span style="color:#719e07">=</span> toValidAddress<span style="color:#719e07">(</span>localAddress<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>addressOp<span style="color:#719e07">.</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> addressOp<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 没有找到适合注册的 IP,则开始轮询网卡 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Enumeration<span style="color:#719e07">&lt;</span>NetworkInterface<span style="color:#719e07">&gt;</span> interfaces <span style="color:#719e07">=</span> NetworkInterface<span style="color:#719e07">.</span>getNetworkInterfaces<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#cb4b16">null</span> <span style="color:#719e07">==</span> interfaces<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> localAddress<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>interfaces<span style="color:#719e07">.</span>hasMoreElements<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> NetworkInterface network <span style="color:#719e07">=</span> interfaces<span style="color:#719e07">.</span>nextElement<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Enumeration<span style="color:#719e07">&lt;</span>InetAddress<span style="color:#719e07">&gt;</span> addresses <span style="color:#719e07">=</span> network<span style="color:#719e07">.</span>getInetAddresses<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>addresses<span style="color:#719e07">.</span>hasMoreElements<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 返回第一个匹配的适合注册的 IP |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Optional<span style="color:#719e07">&lt;</span>InetAddress<span style="color:#719e07">&gt;</span> addressOp <span style="color:#719e07">=</span> toValidAddress<span style="color:#719e07">(</span>addresses<span style="color:#719e07">.</span>nextElement<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>addressOp<span style="color:#719e07">.</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> addressOp<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> localAddress<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>Dubbo 这段选取本地地址的逻辑大致分成了两步</p> |
| <ol> |
| <li>先去 /etc/hosts 文件中找 hostname 对应的 IP 地址,找到则返回;找不到则转 2</li> |
| <li>轮询网卡,寻找合适的 IP 地址,找到则返回;找不到返回 null,在 getLocalAddress0 外侧还有一段逻辑,如果返回 null,则注册 127.0.0.1 这个本地回环地址</li> |
| </ol> |
| <p>首先强调下,这段逻辑并没有太大的问题,先别急着挑刺,让我们来分析下其中的一些细节,并进行验证。</p> |
| <h3 id="21-尝试获取-hostname-映射-ip">2.1 尝试获取 hostname 映射 IP</h3> |
| <p>Dubbo 首先选取的是 hostname 对应的 IP,在源码中对应的 <code>InetAddress.getLocalHost();</code> 在 <code>*nix</code> 系统实际部署 Dubbo 应用时,可以首先使用 <code>hostname</code> 命令获取主机名</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>xujingfengdeMacBook-Pro:~ xujingfeng$ hostname |
| </span></span><span style="display:flex;"><span>xujingfengdeMacBook-Pro.local |
| </span></span></code></pre></div><p>紧接着在 <code>/etc/hosts</code> 配置 IP 映射,为了验证 Dubbo 的机制,我们随意为 hostname 配置一个 IP 地址</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>127.0.0.1 localhost |
| </span></span><span style="display:flex;"><span>1.2.3.4 xujingfengdeMacBook-Pro.local |
| </span></span></code></pre></div><p>接着调用 <code>NetUtils.getLocalAddress0()</code> 进行验证,控制台打印如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>xujingfengdeMacBook-Pro.local/1.2.3.4 |
| </span></span></code></pre></div><h3 id="22-判定有效的-ip-地址">2.2 判定有效的 IP 地址</h3> |
| <p>在 toValidAddress 逻辑中,Dubbo 存在以下逻辑判定一个 IP 地址是否有效</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> Optional<span style="color:#719e07">&lt;</span>InetAddress<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">toValidAddress</span><span style="color:#719e07">(</span>InetAddress address<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>address <span style="color:#719e07">instanceof</span> Inet6Address<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Inet6Address v6Address <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>Inet6Address<span style="color:#719e07">)</span> address<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isValidV6Address<span style="color:#719e07">(</span>v6Address<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> Optional<span style="color:#719e07">.</span>ofNullable<span style="color:#719e07">(</span>normalizeV6Address<span style="color:#719e07">(</span>v6Address<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isValidV4Address<span style="color:#719e07">(</span>address<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> Optional<span style="color:#719e07">.</span>of<span style="color:#719e07">(</span>address<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> Optional<span style="color:#719e07">.</span>empty<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>依次校验其符合 Ipv6 或者 Ipv4 的 IP 规范,对于 Ipv6 的地址,见如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">static</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isValidV6Address</span><span style="color:#719e07">(</span>Inet6Address address<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> preferIpv6 <span style="color:#719e07">=</span> Boolean<span style="color:#719e07">.</span>getBoolean<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;java.net.preferIPv6Addresses&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>preferIpv6<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> address<span style="color:#719e07">.</span>isReachable<span style="color:#719e07">(</span>100<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>IOException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>首先获取 <code>java.net.preferIPv6Addresses</code> 参数,其默认值为 false,鉴于大多数应用并没有使用 Ipv6 地址作为理想的注册 IP,这问题不大,紧接着通过 isReachable 判断网卡的连通性。例如一些网卡可能是 VPN/虚拟网卡的地址,如果没有配置路由表,往往无法连通,可以将之过滤。</p> |
| <p>对于 Ipv4 的地址,见如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">static</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isValidV4Address</span><span style="color:#719e07">(</span>InetAddress address<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>address <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> address<span style="color:#719e07">.</span>isLoopbackAddress<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> address<span style="color:#719e07">.</span>getHostAddress<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> result <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>name <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> IP_PATTERN<span style="color:#719e07">.</span>matcher<span style="color:#719e07">(</span>name<span style="color:#719e07">).</span>matches<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>Constants<span style="color:#719e07">.</span>ANYHOST_VALUE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>name<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>Constants<span style="color:#719e07">.</span>LOCALHOST_VALUE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>对比 Ipv6 的判断,这里我们已经发现前后不对称的情况了</p> |
| <ul> |
| <li>Ipv4 相比 Ipv6 的逻辑多了 Ipv4 格式的正则校验、本地回环地址校验、ANYHOST 校验</li> |
| <li>Ipv4 相比 Ipv6 的逻辑少了网卡连通性的校验</li> |
| </ul> |
| <p>大家都知道,Ipv4 将 127.0.0.1 定为本地回环地址, Ipv6 也存在回环地址:0:0:0:0:0:0:0:1 或者表示为 ::1。改进建议也很明显,我们放到文末统一总结。</p> |
| <h3 id="23-轮询网卡">2.3 轮询网卡</h3> |
| <p>如果上述地址获取为 null 则进入轮询网卡的逻辑(例如 hosts 未指定 hostname 的映射或者 hostname 配置成了 127.0.0.1 之类的地址便会导致获取到空的网卡地址),轮询网卡对应的源码是 <code>NetworkInterface.getNetworkInterfaces()</code> ,这里面涉及的知识点就比较多了,支撑起了我写这篇文章的素材,Dubbo 的逻辑并不复杂,进行简单的校验,返回第一个可用的 IP 即可。</p> |
| <p>性子急的读者可能忍不住了,多网卡!合适的网卡可能不止一个,Dubbo 怎么应对呢?按道理说,我们也替 Dubbo 说句公道话,客官要不你自己指定下?我们首先得对多网卡的场景达成一致看法,才能继续把这篇文章完成下去:我们只能<strong>尽可能</strong>过滤那些“<strong>不对</strong>”的网卡。Dubbo 看样子对所有网卡是一视同仁了,那么是不是可以尝试优化一下其中的逻辑呢?</p> |
| <p>许多开源的服务治理框架在 stackoverflow 或者其 issue 中,注册错 IP 相关的问题都十分高频,大多数都是轮询网卡出了问题。既然事情发展到这儿,势必需要了解一些网络、网卡的知识,我们才能过滤掉那些明显不适合 RPC 服务注册的 IP 地址了。</p> |
| <h2 id="3-ifconfig-介绍">3 Ifconfig 介绍</h2> |
| <p>我并没有想要让大家对后续的内容望而却步,特地选择了这个大家最熟悉的 Linux 命令!对于那些吐槽:“天呐,都 2019 年了,你怎么还在用 net-tools/ifconfig,iproute2/ip 了解一下”的言论,请大家视而不见。无论你使用的是 mac,还是 linux,都可以使用它去 CRUD 你的网卡配置。</p> |
| <h3 id="31-常用指令">3.1 常用指令</h3> |
| <p><strong>启动关闭指定网卡:</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>ifconfig eth0 up |
| </span></span><span style="display:flex;"><span>ifconfig eth0 down |
| </span></span></code></pre></div><p><code>ifconfig eth0 up</code> 为启动网卡 eth0,<code>ifconfig eth0 down</code> 为关闭网卡 eth0。ssh 登陆 linux 服务器操作的用户要小心执行这个操作了,千万不要蠢哭自己。不然你下一步就需要去 google:“禁用 eth0 网卡后如何远程连接 Linux 服务器” 了。</p> |
| <p><strong>为网卡配置和删除IPv6地址:</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>ifconfig eth0 add 33ffe:3240:800:1005::2/64 #为网卡eth0配置IPv6地址 |
| </span></span><span style="display:flex;"><span>ifconfig eth0 del 33ffe:3240:800:1005::2/64 #为网卡eth0删除IPv6地址 |
| </span></span></code></pre></div><p><strong>用 ifconfig 修改 MAC 地址:</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>ifconfig eth0 hw ether 00:AA:BB:CC:dd:EE |
| </span></span></code></pre></div><p><strong>配置 IP 地址:</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>[root@localhost ~]# ifconfig eth0 192.168.2.10 |
| </span></span><span style="display:flex;"><span>[root@localhost ~]# ifconfig eth0 192.168.2.10 netmask 255.255.255.0 |
| </span></span><span style="display:flex;"><span>[root@localhost ~]# ifconfig eth0 192.168.2.10 netmask 255.255.255.0 broadcast 192.168.2.255 |
| </span></span></code></pre></div><p><strong>启用和关闭arp协议:</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>ifconfig eth0 arp #开启网卡eth0 的arp协议 |
| </span></span><span style="display:flex;"><span>ifconfig eth0 -arp #关闭网卡eth0 的arp协议 |
| </span></span></code></pre></div><p><strong>设置最大传输单元:</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>ifconfig eth0 mtu 1500 #设置能通过的最大数据包大小为 1500 bytes |
| </span></span></code></pre></div><h3 id="32-查看网卡信息">3.2 查看网卡信息</h3> |
| <p>在一台 ubuntu 上执行 <code>ifconfig -a</code></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ubuntu@VM-30-130-ubuntu:~$ ifconfig -a |
| </span></span><span style="display:flex;"><span>eth0 Link encap:Ethernet HWaddr 52:54:00:a9:5f:ae |
| </span></span><span style="display:flex;"><span> inet addr:10.154.30.130 Bcast:10.154.63.255 Mask:255.255.192.0 |
| </span></span><span style="display:flex;"><span> UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 |
| </span></span><span style="display:flex;"><span> RX packets:149673 errors:0 dropped:0 overruns:0 frame:0 |
| </span></span><span style="display:flex;"><span> TX packets:152271 errors:0 dropped:0 overruns:0 carrier:0 |
| </span></span><span style="display:flex;"><span> collisions:0 txqueuelen:1000 |
| </span></span><span style="display:flex;"><span> RX bytes:15205083 <span style="color:#719e07">(</span>15.2 MB<span style="color:#719e07">)</span> TX bytes:21386362 <span style="color:#719e07">(</span>21.3 MB<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>lo Link encap:Local Loopback |
| </span></span><span style="display:flex;"><span> inet addr:127.0.0.1 Mask:255.0.0.0 |
| </span></span><span style="display:flex;"><span> UP LOOPBACK RUNNING MTU:65536 Metric:1 |
| </span></span><span style="display:flex;"><span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0 |
| </span></span><span style="display:flex;"><span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 |
| </span></span><span style="display:flex;"><span> collisions:0 txqueuelen:1 |
| </span></span><span style="display:flex;"><span> RX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> TX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>docker0 Link encap:Ethernet HWaddr 02:42:58:45:c1:15 |
| </span></span><span style="display:flex;"><span> inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0 |
| </span></span><span style="display:flex;"><span> UP BROADCAST MULTICAST MTU:1500 Metric:1 |
| </span></span><span style="display:flex;"><span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0 |
| </span></span><span style="display:flex;"><span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 |
| </span></span><span style="display:flex;"><span> collisions:0 txqueuelen:0 |
| </span></span><span style="display:flex;"><span> RX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> TX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 |
| </span></span><span style="display:flex;"><span> UP POINTOPOINT NOARP MULTICAST MTU:1500 Metric:1 |
| </span></span><span style="display:flex;"><span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0 |
| </span></span><span style="display:flex;"><span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 |
| </span></span><span style="display:flex;"><span> collisions:0 txqueuelen:100 |
| </span></span><span style="display:flex;"><span> RX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> TX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> |
| </span></span></code></pre></div><p>为了防止黑客对我的 Linux 发起攻击,我还是偷偷对 IP 做了一点“改造”,请不要为难一个趁着打折+组团购买廉价云服务器的小伙子。对于部分网卡的详细解读:</p> |
| <p>eth0 表示第一块网卡, 其中 HWaddr 表示网卡的物理地址,可以看到目前这个网卡的物理地址(MAC 地址)是 02:42:38:52:70:54</p> |
| <p>inet addr 用来表示网卡的 IP 地址,此网卡的 IP 地址是 10.154.30.130,广播地址, Bcast: 172.18.255.255,掩码地址 Mask:255.255.0.0</p> |
| <p>lo 是表示主机的回环地址,这个一般是用来测试一个网络程序,但又不想让局域网或外网的用户能够查看,只能在此台主机上运行和查看所用的网络接口。比如把 HTTPD 服务器的指定到回坏地址,在浏览器输入 127.0.0.1 就能看到你所架构的 WEB 网站了。但只有你能看得到,局域网的其它主机或用户则无从知晓。</p> |
| <p>第一行:连接类型:Ethernet(以太网)HWaddr(硬件mac地址)</p> |
| <p>第二行:网卡的IP地址、子网、掩码</p> |
| <p>第三行:UP(代表网卡开启状态)RUNNING(代表网卡的网线被接上)MULTICAST(支持组播)MTU:1500(最大传输单元):1500字节(ifconfig 不加 -a 则无法看到 DOWN 的网卡)</p> |
| <p>第四、五行:接收、发送数据包情况统计</p> |
| <p>第七行:接收、发送数据字节数统计信息。</p> |
| <p>紧接着的两个网卡 docker0,tun0 是怎么出来的呢?我在我的 ubuntu 上装了 docker 和 openvpn。这两个东西应该是日常干扰我们做服务注册时的罪魁祸首了,当然,也有可能存在 eth1 这样的第二块网卡。ifconfig -a 看到的东西就对应了 JDK 的 api :<code>NetworkInterface.getNetworkInterfaces()</code> 。我们简单做个总结,大致有三个干扰因素</p> |
| <ul> |
| <li>以 docker 网桥为首的虚拟网卡地址,毕竟这东西这么火,怎么也得单独列出来吧?</li> |
| <li>以 TUN/TAP 为代表的虚拟网卡地址,多为 VPN 场景</li> |
| <li>以 eth1 为代表的多网卡场景,有钱就可以装多网卡了!</li> |
| </ul> |
| <p>我们后续的篇幅将针对这些场景做分别的介绍,力求让大家没吃过猪肉,起码看下猪怎么跑的。</p> |
| <h2 id="4-干扰因素一docker-网桥">4 干扰因素一:Docker 网桥</h2> |
| <p>熟悉 docker 的朋友应该知道 docker 会默认创建一个 docker0 的网桥,供容器实例连接。如果嫌默认的网桥不够直观,我们可以使用 bridge 模式自定义创建一个新的网桥:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ubuntu@VM-30-130-ubuntu:~$ docker network create kirito-bridge |
| </span></span><span style="display:flex;"><span>a38696dbbe58aa916894c674052c4aa6ab32266dcf6d8111fb794b8a344aa0d9 |
| </span></span><span style="display:flex;"><span>ubuntu@VM-30-130-ubuntu:~$ ifconfig -a |
| </span></span><span style="display:flex;"><span>br-a38696dbbe58 Link encap:Ethernet HWaddr 02:42:6e:aa:fd:0c |
| </span></span><span style="display:flex;"><span> inet addr:172.19.0.1 Bcast:172.19.255.255 Mask:255.255.0.0 |
| </span></span><span style="display:flex;"><span> UP BROADCAST MULTICAST MTU:1500 Metric:1 |
| </span></span><span style="display:flex;"><span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0 |
| </span></span><span style="display:flex;"><span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 |
| </span></span><span style="display:flex;"><span> collisions:0 txqueuelen:0 |
| </span></span><span style="display:flex;"><span> RX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> TX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> |
| </span></span></code></pre></div><p>使用 docker network 指令创建网桥之后,自动创建了对应的网卡,我只给出了 <code>ifconfig -a</code> 的增量返回部分,可以看出多了一个 br-a38696dbbe58 的网卡。</p> |
| <p>我有意区分了“网桥”和“网卡”,可以使用 bridge-utils/brctl 来查看网桥信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ubuntu@VM-30-130-ubuntu:~$ sudo brctl show |
| </span></span><span style="display:flex;"><span>bridge name bridge id STP enabled interfaces |
| </span></span><span style="display:flex;"><span>br-a38696dbbe58 8000.02426eaafd0c no |
| </span></span><span style="display:flex;"><span>docker0 8000.02425845c215 no |
| </span></span></code></pre></div><p>网桥是一个虚拟设备,这个设备只有 brctl show 能看到,网桥创建之后,会自动创建一个同名的网卡,并将这个网卡加入网桥。</p> |
| <h2 id="5-干扰因素二tuntap-虚拟网络设备">5 干扰因素二:TUN/TAP 虚拟网络设备</h2> |
| <p>平时我们所说的虚拟网卡、虚拟机,大致都跟 TUN/TAP 有关。我的读者大多数是 Java 从业者,相信我下面的内容并没有太超纲,不要被陌生的名词唬住。对于被唬住的读者,也可以直接跳过 5.1~5.3,直接看 5.4 的实战。</p> |
| <h3 id="51-真实网卡工作原理">5.1 真实网卡工作原理</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/network/01.png" alt="1918847-496d0e96c237f25a"></p> |
| <p>上图中的 <strong>eth0</strong> 表示我们主机已有的真实的网卡接口 (<strong>interface</strong>)。</p> |
| <p>网卡接口 <strong>eth0</strong> 所代表的真实网卡通过网线(<strong>wire</strong>)和外部网络相连,该物理网卡收到的数据包会经由接口 <strong>eth0</strong> 传递给内核的网络协议栈(<strong>Network Stack</strong>)。然后协议栈对这些数据包进行进一步的处理。</p> |
| <p>对于一些错误的数据包,协议栈可以选择丢弃;对于不属于本机的数据包,协议栈可以选择转发;而对于确实是传递给本机的数据包,而且该数据包确实被上层的应用所需要,协议栈会通过 <strong>Socket API</strong> 告知上层正在等待的应用程序。</p> |
| <h3 id="52-tun-工作原理">5.2 TUN 工作原理</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/network/02.png" alt="1918847-85ea08bc89d9427e"></p> |
| <p>我们知道,普通的网卡是通过网线来收发数据包的话,而 <strong>TUN</strong> 设备比较特殊,它通过一个文件收发数据包。</p> |
| <p>如上图所示,<strong>tunX</strong> 和上面的 <strong>eth0</strong> 在逻辑上面是等价的, <strong>tunX</strong> 也代表了一个网络接口,虽然这个接口是系统通过软件所模拟出来的.</p> |
| <p>网卡接口 <strong>tunX 所代表的虚拟网卡通过文件 /dev/tunX 与我们的应用程序(App)相连</strong>,应用程序每次使用 <strong>write</strong> 之类的系统调用将数据写入该文件,这些数据会以网络层数据包的形式,通过该虚拟网卡,经由网络接口 <strong>tunX</strong> 传递给网络协议栈,同时该应用程序也可以通过 <strong>read</strong> 之类的系统调用,经由文件 <strong>/dev/tunX</strong> 读取到协议栈向 <strong>tunX</strong> 传递的<strong>所有</strong>数据包。</p> |
| <p>此外,协议栈可以像操纵普通网卡一样来操纵 <strong>tunX</strong> 所代表的虚拟网卡。比如说,给 <strong>tunX</strong> 设定 <strong>IP</strong> 地址,设置路由,总之,在协议栈看来,<strong>tunX</strong> 所代表的网卡和其他普通的网卡区别不大,当然,硬要说区别,那还是有的,那就是 <strong>tunX</strong> 设备不存在 <strong>MAC</strong> 地址,这个很好理解,<strong>tunX</strong> 只模拟到了网络层,要 <strong>MAC</strong>地址没有任何意义。当然,如果是 <strong>tapX</strong> 的话,在协议栈的眼中,<strong>tapX</strong> 和真实网卡没有任何区别。</p> |
| <p>是不是有些懵了?我是谁,为什么我要在这篇文章里面学习 TUN!因为我们常用的 VPN 基本就是基于 TUN/TAP 搭建的,如果我们使用 <strong>TUN</strong> 设备搭建一个基于 <strong>UDP</strong> 的 <strong>VPN</strong> ,那么整个处理过程可能是这幅样子:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/network/03.png" alt="1918847-ac4155ec7e9489b2"></p> |
| <h3 id="53-tap-工作原理">5.3 TAP 工作原理</h3> |
| <p><strong>TAP</strong> 设备与 <strong>TUN</strong> 设备工作方式完全相同,区别在于:</p> |
| <ol> |
| <li><strong>TUN</strong> 设备是一个三层设备,它只模拟到了 <strong>IP</strong> 层,即网络层 我们可以通过 <strong>/dev/tunX</strong> 文件收发 <strong>IP</strong> 层数据包,它无法与物理网卡做 <strong>bridge</strong>,但是可以通过三层交换(如 <strong>ip_forward</strong>)与物理网卡连通。可以使用<code>ifconfig</code>之类的命令给该设备设定 <strong>IP</strong> 地址。</li> |
| <li><strong>TAP</strong> 设备是一个二层设备,它比 <strong>TUN</strong> 更加深入,通过 <strong>/dev/tapX</strong> 文件可以收发 <strong>MAC</strong> 层数据包,即数据链路层,拥有 <strong>MAC</strong> 层功能,可以与物理网卡做 <strong>bridge</strong>,支持 <strong>MAC</strong> 层广播。同样的,我们也可以通过<code>ifconfig</code>之类的命令给该设备设定 <strong>IP</strong> 地址,你如果愿意,我们可以给它设定 <strong>MAC</strong> 地址。</li> |
| </ol> |
| <p>关于文章中出现的二层,三层,我这里说明一下,第一层是物理层,第二层是数据链路层,第三层是网络层,第四层是传输层。</p> |
| <h3 id="54-openvpn-实战">5.4 openvpn 实战</h3> |
| <p>openvpn 是 Linux 上一款开源的 vpn 工具,我们通过它来复现出影响我们做网卡选择的场景。</p> |
| <p>安装 openvpn</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo apt-get install openvpn |
| </span></span></code></pre></div><p>安装一个 TUN 设备:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ubuntu@VM-30-130-ubuntu:~$ sudo openvpn --mktun --dev tun0 |
| </span></span><span style="display:flex;"><span>Mon Apr <span style="color:#2aa198">29</span> 22:23:31 <span style="color:#2aa198">2019</span> TUN/TAP device tun0 opened |
| </span></span><span style="display:flex;"><span>Mon Apr <span style="color:#2aa198">29</span> 22:23:31 <span style="color:#2aa198">2019</span> Persist state <span style="color:#b58900">set</span> to: ON |
| </span></span></code></pre></div><p>安装一个 TAP 设备:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ubuntu@VM-30-130-ubuntu:~$ sudo openvpn --mktun --dev tap0 |
| </span></span><span style="display:flex;"><span>Mon Apr <span style="color:#2aa198">29</span> 22:24:36 <span style="color:#2aa198">2019</span> TUN/TAP device tap0 opened |
| </span></span><span style="display:flex;"><span>Mon Apr <span style="color:#2aa198">29</span> 22:24:36 <span style="color:#2aa198">2019</span> Persist state <span style="color:#b58900">set</span> to: ON |
| </span></span></code></pre></div><p>执行 <code>ifconfig -a</code> 查看网卡,只给出增量的部分:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>tap0 Link encap:Ethernet HWaddr 7a:a2:a8:f1:6b:df |
| </span></span><span style="display:flex;"><span> BROADCAST MULTICAST MTU:1500 Metric:1 |
| </span></span><span style="display:flex;"><span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0 |
| </span></span><span style="display:flex;"><span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 |
| </span></span><span style="display:flex;"><span> collisions:0 txqueuelen:100 |
| </span></span><span style="display:flex;"><span> RX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> TX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 |
| </span></span><span style="display:flex;"><span> inet addr:10.154.30.131 P-t-P:10.154.30.131 Mask:255.255.255.255 |
| </span></span><span style="display:flex;"><span> UP POINTOPOINT NOARP MULTICAST MTU:1500 Metric:1 |
| </span></span><span style="display:flex;"><span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0 |
| </span></span><span style="display:flex;"><span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 |
| </span></span><span style="display:flex;"><span> collisions:0 txqueuelen:100 |
| </span></span><span style="display:flex;"><span> RX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> TX bytes:0 <span style="color:#719e07">(</span>0.0 B<span style="color:#719e07">)</span> |
| </span></span></code></pre></div><p>这样就解释了文章一开始为什么会有 tun0 这样的网卡了。这里读者可能会有疑惑,使用 ifconfig 不是也可以创建 tap 和 tun 网卡吗?当然啦,openvpn 是一个 vpn 工具,只能创建名为 tunX/tapX 的网卡,其遵守着一定的规范,ifconfig 可以随意创建,但没人认那些随意创建的网卡。</p> |
| <h2 id="6-干扰因素三多网卡">6 干扰因素三:多网卡</h2> |
| <p><img src="https://dubbo.apache.org/imgs/blog/network/04.png" alt="image-20190429223515625"></p> |
| <p>这个没有太多好说的,有多张真实的网卡,从普哥那儿搞到如上的 IP 信息。</p> |
| <h2 id="7-mac-下的差异">7 MAC 下的差异</h2> |
| <p>虽然 ifconfig 等指令是 <code>*nux</code> 通用的,但是其展示信息,网卡相关的属性和命名都有较大的差异。例如这是我 MAC 下执行 <code>ifconfig -a</code> 的返回:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>xujingfengdeMacBook-Pro:dubbo-in-action xujingfeng$ ifconfig -a |
| </span></span><span style="display:flex;"><span>lo0: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8049&lt;UP,LOOPBACK,RUNNING,MULTICAST&gt; mtu <span style="color:#2aa198">16384</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>1203&lt;RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP&gt; |
| </span></span><span style="display:flex;"><span> inet 127.0.0.1 netmask 0xff000000 |
| </span></span><span style="display:flex;"><span> inet6 ::1 prefixlen <span style="color:#2aa198">128</span> |
| </span></span><span style="display:flex;"><span> inet6 fe80::1%lo0 prefixlen <span style="color:#2aa198">64</span> scopeid 0x1 |
| </span></span><span style="display:flex;"><span> nd6 <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>201&lt;PERFORMNUD,DAD&gt; |
| </span></span><span style="display:flex;"><span>gif0: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8010&lt;POINTOPOINT,MULTICAST&gt; mtu <span style="color:#2aa198">1280</span> |
| </span></span><span style="display:flex;"><span>stf0: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>0&lt;&gt; mtu <span style="color:#2aa198">1280</span> |
| </span></span><span style="display:flex;"><span>XHC0: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>0&lt;&gt; mtu <span style="color:#2aa198">0</span> |
| </span></span><span style="display:flex;"><span>XHC20: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>0&lt;&gt; mtu <span style="color:#2aa198">0</span> |
| </span></span><span style="display:flex;"><span>en0: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8863&lt;UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST&gt; mtu <span style="color:#2aa198">1500</span> |
| </span></span><span style="display:flex;"><span> ether 88:e9:fe:88:a0:76 |
| </span></span><span style="display:flex;"><span> inet6 fe80::1cab:f689:60d1:bacb%en0 prefixlen <span style="color:#2aa198">64</span> secured scopeid 0x6 |
| </span></span><span style="display:flex;"><span> inet 30.130.11.242 netmask 0xffffff80 broadcast 30.130.11.255 |
| </span></span><span style="display:flex;"><span> nd6 <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>201&lt;PERFORMNUD,DAD&gt; |
| </span></span><span style="display:flex;"><span> media: autoselect |
| </span></span><span style="display:flex;"><span> status: active |
| </span></span><span style="display:flex;"><span>p2p0: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8843&lt;UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST&gt; mtu <span style="color:#2aa198">2304</span> |
| </span></span><span style="display:flex;"><span> ether 0a:e9:fe:88:a0:76 |
| </span></span><span style="display:flex;"><span> media: autoselect |
| </span></span><span style="display:flex;"><span> status: inactive |
| </span></span><span style="display:flex;"><span>awdl0: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8943&lt;UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST&gt; mtu <span style="color:#2aa198">1484</span> |
| </span></span><span style="display:flex;"><span> ether 66:d2:8c:8c:dd:85 |
| </span></span><span style="display:flex;"><span> inet6 fe80::64d2:8cff:fe8c:dd85%awdl0 prefixlen <span style="color:#2aa198">64</span> scopeid 0x8 |
| </span></span><span style="display:flex;"><span> nd6 <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>201&lt;PERFORMNUD,DAD&gt; |
| </span></span><span style="display:flex;"><span> media: autoselect |
| </span></span><span style="display:flex;"><span> status: active |
| </span></span><span style="display:flex;"><span>en1: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8963&lt;UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST&gt; mtu <span style="color:#2aa198">1500</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>60&lt;TSO4,TSO6&gt; |
| </span></span><span style="display:flex;"><span> ether aa:00:d0:13:0e:01 |
| </span></span><span style="display:flex;"><span> media: autoselect &lt;full-duplex&gt; |
| </span></span><span style="display:flex;"><span> status: inactive |
| </span></span><span style="display:flex;"><span>en2: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8963&lt;UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST&gt; mtu <span style="color:#2aa198">1500</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>60&lt;TSO4,TSO6&gt; |
| </span></span><span style="display:flex;"><span> ether aa:00:d0:13:0e:00 |
| </span></span><span style="display:flex;"><span> media: autoselect &lt;full-duplex&gt; |
| </span></span><span style="display:flex;"><span> status: inactive |
| </span></span><span style="display:flex;"><span>bridge0: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8863&lt;UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST&gt; mtu <span style="color:#2aa198">1500</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>63&lt;RXCSUM,TXCSUM,TSO4,TSO6&gt; |
| </span></span><span style="display:flex;"><span> ether aa:00:d0:13:0e:01 |
| </span></span><span style="display:flex;"><span> Configuration: |
| </span></span><span style="display:flex;"><span> id 0:0:0:0:0:0 priority <span style="color:#2aa198">0</span> hellotime <span style="color:#2aa198">0</span> fwddelay <span style="color:#2aa198">0</span> |
| </span></span><span style="display:flex;"><span> maxage <span style="color:#2aa198">0</span> holdcnt <span style="color:#2aa198">0</span> proto stp maxaddr <span style="color:#2aa198">100</span> timeout <span style="color:#2aa198">1200</span> |
| </span></span><span style="display:flex;"><span> root id 0:0:0:0:0:0 priority <span style="color:#2aa198">0</span> ifcost <span style="color:#2aa198">0</span> port <span style="color:#2aa198">0</span> |
| </span></span><span style="display:flex;"><span> ipfilter disabled flags 0x2 |
| </span></span><span style="display:flex;"><span> member: en1 <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>3&lt;LEARNING,DISCOVER&gt; |
| </span></span><span style="display:flex;"><span> ifmaxaddr <span style="color:#2aa198">0</span> port <span style="color:#2aa198">9</span> priority <span style="color:#2aa198">0</span> path cost <span style="color:#2aa198">0</span> |
| </span></span><span style="display:flex;"><span> member: en2 <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>3&lt;LEARNING,DISCOVER&gt; |
| </span></span><span style="display:flex;"><span> ifmaxaddr <span style="color:#2aa198">0</span> port <span style="color:#2aa198">10</span> priority <span style="color:#2aa198">0</span> path cost <span style="color:#2aa198">0</span> |
| </span></span><span style="display:flex;"><span> nd6 <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>201&lt;PERFORMNUD,DAD&gt; |
| </span></span><span style="display:flex;"><span> media: &lt;unknown type&gt; |
| </span></span><span style="display:flex;"><span> status: inactive |
| </span></span><span style="display:flex;"><span>utun0: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8051&lt;UP,POINTOPOINT,RUNNING,MULTICAST&gt; mtu <span style="color:#2aa198">2000</span> |
| </span></span><span style="display:flex;"><span> inet6 fe80::3fe0:3e8b:384:9968%utun0 prefixlen <span style="color:#2aa198">64</span> scopeid 0xc |
| </span></span><span style="display:flex;"><span> nd6 <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>201&lt;PERFORMNUD,DAD&gt; |
| </span></span><span style="display:flex;"><span>utun1: <span style="color:#268bd2">flags</span><span style="color:#719e07">=</span>8051&lt;UP,POINTOPOINT,RUNNING,MULTICAST&gt; mtu <span style="color:#2aa198">1380</span> |
| </span></span><span style="display:flex;"><span> inet6 fe80::7894:3abc:5abd:457d%utun1 prefixlen <span style="color:#2aa198">64</span> scopeid 0xd |
| </span></span><span style="display:flex;"><span> nd6 <span style="color:#268bd2">options</span><span style="color:#719e07">=</span>201&lt;PERFORMNUD,DAD&gt; |
| </span></span></code></pre></div><p>内容很多,我挑几点差异简述下:</p> |
| <ul> |
| <li> |
| <p>内容展示形式不一样,没有 Linux 下的接收、发送数据字节数等统计信息</p> |
| </li> |
| <li> |
| <p>真实网卡的命名不一样:eth0 -&gt; en0</p> |
| </li> |
| <li> |
| <p>虚拟网卡的命名格式不一样:tun/tap -&gt; utun</p> |
| </li> |
| </ul> |
| <p>对于这些常见网卡命名的解读,我摘抄一部分来自 stackoverflow 的回答:</p> |
| <blockquote> |
| <p>In arbitrary order of my familarity / widespread relevance:</p> |
| <p><code>lo0</code> is loopback.</p> |
| <p><code>en0</code> at one point &ldquo;ethernet&rdquo;, now is WiFi (and I have no idea what extra <code>en1</code> or <code>en2</code> are used for).</p> |
| <p><code>fw0</code> is the FireWire network interface.</p> |
| <p><code>stf0</code> is an <a href="https://www.freebsd.org/cgi/man.cgi?gif(4)">IPv6 to IPv4 tunnel interface</a> to support <a href="http://en.wikipedia.org/wiki/6to4">the transition</a> from IPv4 to the IPv6 standard.</p> |
| <p><code>gif0</code> is a more <a href="https://www.freebsd.org/cgi/man.cgi?gif(4)">generic tunneling interface</a> [46]-to-[46].</p> |
| <p><code>awdl0</code> is <a href="https://stackoverflow.com/questions/19587701/what-is-awdl-apple-wireless-direct-link-and-how-does-it-work">Apple Wireless Direct Link</a></p> |
| <p><code>p2p0</code> is related to AWDL features. Either as an old version, or virtual interface with different semantics than <code>awdl</code>.</p> |
| <p>the &ldquo;Network&rdquo; panel in System Preferences to see what network devices &ldquo;exist&rdquo; or &ldquo;can exist&rdquo; with current configuration.</p> |
| <p>many VPNs will add additional devices, often &ldquo;utun#&rdquo; or &ldquo;utap#&rdquo; following <a href="https://en.wikipedia.org/wiki/TUN/TAP">TUN/TAP (L3/L2)</a>virtual networking devices.</p> |
| <p>use <code>netstat -nr</code> to see how traffic is currently routed via network devices according to destination.</p> |
| <p>interface naming conventions started in BSD were retained in OS X / macOS, and now there also additions.</p> |
| </blockquote> |
| <h2 id="8-dubbo-改进建议">8 Dubbo 改进建议</h2> |
| <p>我们进行了以上探索,算是对网卡有一点了解了。回过头来看看 Dubbo 获取网卡的逻辑,是否可以做出改进呢?</p> |
| <p><strong>Dubbo Action 1:</strong></p> |
| <p>保持 Ipv4 和 Ipv6 的一致性校验。为 Ipv4 增加连通性校验;为 Ipv6 增加 LoopBack 和 ANYHOST 等校验。</p> |
| <p><strong>Dubbo Action 2:</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>NetworkInterface network <span style="color:#719e07">=</span> interfaces<span style="color:#719e07">.</span>nextElement<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">if</span> <span style="color:#719e07">(</span>network<span style="color:#719e07">.</span>isLoopback<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> network<span style="color:#719e07">.</span>isVirtual<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> <span style="color:#719e07">!</span>network<span style="color:#719e07">.</span>isUp<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>JDK 提供了以上的 API,我们可以利用起来,过滤一部分一定不正确的网卡。</p> |
| <p><strong>Dubbo Action 3:</strong></p> |
| <p>我们本文花了较多的篇幅介绍了 docker 和 TUN/TAP 两种场景导致的虚拟网卡的问题,算是较为常见的一个影响因素,虽然他们的命名具有固定性,如 docker0、tunX、tapX,但我觉得通过网卡名称的判断方式去过滤注册 IP 有一些 hack,所以不建议 dubbo contributor 提出相应的 pr 去增加这些 hack 判断,尽管可能会对判断有所帮助。</p> |
| <p>对于真实多网卡、内外网 IP 共存的场景,不能仅仅是框架侧在做努力,用户也需要做一些事,就像爱情一样,我可以主动一点,但你也得反馈,才能发展出故事。</p> |
| <p><strong>Dubbo User Action 1:</strong></p> |
| <p>可以配置 <code>/etc/hosts</code> 文件,将 hostname 对应的 IP 显式配置进去。</p> |
| <p><strong>Dubbo User Action 2:</strong></p> |
| <p>可以使用启动参数去显式指定注册的 IP:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">-</span>DDUBBO_IP_TO_REGISTRY<span style="color:#719e07">=</span>1<span style="color:#719e07">.</span>2<span style="color:#719e07">.</span>3<span style="color:#719e07">.</span>4 |
| </span></span></code></pre></div><p>也可以指定 Dubbo 服务绑定在哪块网卡上:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">-</span>DDUBBO_IP_TO_BIND<span style="color:#719e07">=</span>1<span style="color:#719e07">.</span>2<span style="color:#719e07">.</span>3<span style="color:#719e07">.</span>4 |
| </span></span></code></pre></div><h2 id="9-参考文章">9 参考文章</h2> |
| <p><a href="https://www.jianshu.com/p/09f9375b7fa7">TUN/TAP 设备浅析</a></p> |
| <p><a href="https://stackoverflow.com/questions/29958143/what-are-en0-en1-p2p-and-so-on-that-are-displayed-after-executing-ifconfig">what-are-en0-en1-p2p-and-so-on-that-are-displayed-after-executing-ifconfig</a></p></description></item><item><title>Blog: Dubbo Admin服务测试功能</title><link>https://dubbo.apache.org/zh-cn/blog/2019/08/26/service-test/</link><pubDate>Mon, 26 Aug 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/08/26/service-test/</guid><description> |
| <p>基于Dubbo2.7的元数据,Dubbo Admin实现了服务测试功能,可以通过泛化调用,在控制台上调用真实的服务提供者</p> |
| <h2 id="使用方式">使用方式</h2> |
| <ul> |
| <li>部署服务提供者: 可以在<a href="https://github.com/nzomkxia/dubbo-demo">这里</a>下载demo,此工程基于spring boot,方便在IDE或者命令行启动,对于服务测试来说,只需要启动<code>dubbo-basic-provider</code>即可。</li> |
| <li>服务查询: 完成服务端部署后,可以到Dubbo Admin的<code>服务测试</code>页面上查询对应的服务: |
| <img src="https://dubbo.apache.org/imgs/blog/admin/testSearch.jpg" alt="testSearch"><br> |
| 这里的信息和元数据类似,包含方法名,参数类型和返回值信息,点击右边的标签就可以进入服务测试页面</li> |
| <li>服务测试: |
| <img src="https://dubbo.apache.org/imgs/blog/admin/testSuccess.jpg" alt="testSuccess"><br> |
| 服务测试页面包含了两个json编辑器,参数类型的信息都是以json格式保存,这里需要填入对应的参数值(本例中数类型时<code>String</code>),填写完成后点击<code>执行</code>即可对服务端发起调用,调用结果展示在右边的编辑器中,如果调用失败,会显示详细的失败原因,下面来看一下调用失败的例子:<br> |
| <img src="https://dubbo.apache.org/imgs/blog/admin/testFail.jpg" alt="testFail"> |
| 本例中,先关掉Dubbo服务提供者的进程,再执行服务测试,可以看到返回的结果是<code>找不到服务提供者</code>的异常。和普通调用一样,业务和框架的异常都会返回在结果中,方便业务排查。</li> |
| <li>复合类型参数<br> |
| 考虑<code>UserService</code>中的以下方法和类型:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//org.apache.dubbo.demo.api.UserService |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>Result <span style="color:#268bd2">getUser</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">,</span> UserInfoDO userInfoDO<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">UserInfoDO</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">int</span> id<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> LocationDO locationDO<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> DepartmentDO departmentDO<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">toString</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;UserInfoDO{&#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;id=&#34;</span> <span style="color:#719e07">+</span> id <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;, locationDO=&#34;</span> <span style="color:#719e07">+</span> locationDO<span style="color:#719e07">.</span>toString<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;, departmentDO=&#34;</span> <span style="color:#719e07">+</span> departmentDO<span style="color:#719e07">.</span>toString<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#39;}&#39;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DepartmentDO</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String departName<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> LocationDO departLocation<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">toString</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;DepartmentDO{&#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;departName=&#39;&#34;</span> <span style="color:#719e07">+</span> departName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#39;\&#39;&#39;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;, departLocation=&#34;</span> <span style="color:#719e07">+</span> departLocation<span style="color:#719e07">.</span>toString<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#39;}&#39;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">LocationDO</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String address<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">int</span> postNum<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">toString</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;LocationDO{&#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;address=&#39;&#34;</span> <span style="color:#719e07">+</span> address <span style="color:#719e07">+</span> <span style="color:#2aa198">&#39;\&#39;&#39;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;, postNum=&#34;</span> <span style="color:#719e07">+</span> postNum <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#39;}&#39;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>参数是比较复杂的符合类型参数,服务测试的时候,会逐层展开填写每一个field的值,如下图所示:<br> |
| <img src="https://dubbo.apache.org/imgs/blog/admin/complex.jpg" alt="complex"> |
| 同样可以调用成功并且返回结果</p> |
| <h2 id="原理数据来源">原理:数据来源</h2> |
| <p>服务测试中,最重要的就是完整的方法签名信息,和参数的类型信息,有了这些信息才能够一步步填入每个参数的值,拼装出完整的服务消费者。在Dubbo2.7中,新增了元数据中心,Dubbo Admin的方法签名和参数类型信息就是从这里来的:<br> |
| <img src="https://dubbo.apache.org/imgs/blog/admin/metadata.png" alt="medatada"> |
| 如图所示,服务端在运行的时候会将服务的元数据信息注册到元数据中心,格式如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHello&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;org.apache.dubbo.demo.model.User&#34;</span> |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;org.apache.dubbo.demo.model.Result&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;types&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;char&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;long&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;org.apache.dubbo.demo.model.Result&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;msg&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;value&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;char[]&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;hash&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;userName&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;value&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;char[]&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;hash&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;org.apache.dubbo.demo.model.User&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;id&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Long&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;value&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;long&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;username&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Sring&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;value&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;char[]&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;hash&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>与服务测试相关的就是<code>methods</code>和<code>types</code>所包含的方法和类型信息,Dubbo Admin根据这些信息,将参数渲染到服务测试页面的Json Editor中,由用户来输入每个参数,每个成员变量的值。</p> |
| <h2 id="原理-泛化调用">原理: 泛化调用</h2> |
| <p>有了参数类型,下一个问题就是怎么能够调用到服务端,在传统的Dubbo RPC调用中,客户端需要依赖服务端的API jar包(参考前文demo中的<a href="https://github.com/nzomkxia/dubbo-demo/tree/master/dubbo-basic-consumer">dubbo-basic-consumer</a>),这对于Dubbo Admin来说不太可能,因为服务的上下线是动态的,Dubbo Admin无法动态增加jar包依赖,因此需要用到Dubbo中的<strong>泛化调用</strong>,指的是在没有服务端API接口的情况下,客户端直接通过 <code>GenericService</code> 接口来发起服务调用,返回值中的数据对象都用Map来表示。泛化调用在服务端不需要做特殊处理,只需要客户端发起即可。</p> |
| <h2 id="总结和展望">总结和展望</h2> |
| <p>本文简单介绍了服务测试的用法和原理,后续会进一步针对该功能进行增强,比如处理抽象类的参数类型,支持从json文件导入参数值,支持对参数值的保存等等,方便对服务接口进行回归测试。</p></description></item><item><title>Blog: 本地调用</title><link>https://dubbo.apache.org/zh-cn/blog/2019/08/11/%E6%9C%AC%E5%9C%B0%E8%B0%83%E7%94%A8/</link><pubDate>Sun, 11 Aug 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/08/11/%E6%9C%AC%E5%9C%B0%E8%B0%83%E7%94%A8/</guid><description> |
| <h3 id="本地调用介绍">本地调用介绍</h3> |
| <p>当一个应用既是一个服务的提供者,同时也是这个服务的消费者的时候,可以直接对本机提供的服务发起本地调用。从 <code>2.2.0</code> 版本开始,Dubbo 默认在本地以 <em>injvm</em> 的方式暴露服务,这样的话,在同一个进程里对这个服务的调用会优先走本地调用。</p> |
| <p>与本地对象上方法调用不同的是,Dubbo 本地调用会经过 Filter 链,其中包括了 Consumer 端的 Filter 链以及 Provider 端的 Filter 链。通过这样的机制,本地消费者和其他消费者都是统一对待,统一监控,服务统一进行治理。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-local-call-filter.png" alt="filter-chain"></p> |
| <p>同时,相比于远程调用来说,Dubbo 本地调用性能较优,省去了请求、响应的编解码及网络传输的过程。</p> |
| <p>要使用 Dubbo 本地调用不需做特殊配置,按正常 Dubbo 服务暴露服务即可。任一服务在暴露远程服务的同时,也会同时以 <em>injvm</em> 的协议暴露本地服务。<em>injvm</em> 是一个伪协议,不会像其他协议那样对外开启端口,只用于本地调用的目的。</p> |
| <p>以下面的 XML 配置为例:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:protocol</span> name=<span style="color:#2aa198">&#34;dubbo&#34;</span> port=<span style="color:#2aa198">&#34;20800&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;demoServiceTarget&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.impl.DemoServiceImpl&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.api.DemoService&#34;</span> ref=<span style="color:#2aa198">&#34;demoServiceTarget&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.api.DemoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>这里同时配置了同一服务 <em>DemoService</em> 的提供者以及消费者。在这种情况下,该应用中的 <em>DemoService</em> 的消费方会优先使用 <em>injvm</em> 协议进行本地调用。上述的例子可以在 dubbo-samples 工程中找到源码:https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-local</p> |
| <h3 id="细粒度控制本地调用">细粒度控制本地调用</h3> |
| <p>本地调用是可以显示关闭的,通过这种方式,服务提供者可以做到对远端服务消费者和本地消费者一视同仁。具体做法是通过 <em>scope=&ldquo;remote&rdquo;</em> 来关闭 <em>injvm</em> 协议的暴露,这样,即使是本地调用者,也需要从注册中心上获取服务地址列表,然后才能发起调用,而这个时候的调用过程,与远端的服务消费者的过程是一致的。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;target&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.impl.DemoServiceImpl&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 服务提供者指定 scope=&#34;remote&#34; --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.api.DemoService&#34;</span> ref=<span style="color:#2aa198">&#34;target&#34;</span> scope=<span style="color:#2aa198">&#34;remote&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.api.DemoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>同样的,服务消费者也支持通过 <em>scope</em> 来限定发起调用优先走本地,还是只走远程。比如,可以通过以下的方式强制消费端通过<strong>远程调用</strong>的方式来发起 dubbo 调用:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 服务消费者指定 scope=&#34;remote&#34; --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.api.DemoService&#34;</span> scope=<span style="color:#2aa198">&#34;remote&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>如果同时服务提供方限定了 <em>scope=&ldquo;local&rdquo;</em> 的话,</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 服务提供者指定 scope=&#34;remote&#34; --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.api.DemoService&#34;</span> ref=<span style="color:#2aa198">&#34;target&#34;</span> scope=<span style="color:#2aa198">&#34;remote&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 服务消费者指定 scope=&#34;local&#34; --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.api.DemoService&#34;</span> scope=<span style="color:#2aa198">&#34;local&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>那么该程序中的 dubbo 调用将会失败,原因是服务提供方只暴露了远程服务到注册中心上,并没有暴露 <em>injvm</em> 协议的服务,而出于同一个进程中的服务消费者查找不到 <em>injvm</em> 协议的服务,也不会去远程的注册中心上订阅服务地址。同样的,当服务提供者限定 <em>scope=&ldquo;local&rdquo;</em> 而服务消费者限定 <em>scope=&ldquo;remote&rdquo;</em> 也会因为相同的原因导致调用失败。出错信息如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#719e07">[</span>20/03/19 05:03:18:018 CST<span style="color:#719e07">]</span> main INFO config.AbstractConfig: <span style="color:#719e07">[</span>DUBBO<span style="color:#719e07">]</span> Using injvm service org.apache.dubbo.samples.local.api.DemoService, dubbo version: 2.7.1, current host: 169.254.146.168 |
| </span></span><span style="display:flex;"><span>Exception in thread <span style="color:#2aa198">&#34;main&#34;</span> org.springframework.beans.factory.BeanCreationException: Error creating bean with name <span style="color:#2aa198">&#39;demoService&#39;</span>: FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Failed to check the status of the service org.apache.dubbo.samples.local.api.DemoService. No provider available <span style="color:#719e07">for</span> the service org.apache.dubbo.samples.local.api.DemoService from the url injvm://127.0.0.1/org.apache.dubbo.samples.local.api.DemoService?application<span style="color:#719e07">=</span>demo-provider&amp;default.lazy<span style="color:#719e07">=</span>false&amp;default.sticky<span style="color:#719e07">=</span>false&amp;<span style="color:#268bd2">dubbo</span><span style="color:#719e07">=</span>2.0.2&amp;<span style="color:#268bd2">interface</span><span style="color:#719e07">=</span>org.apache.dubbo.samples.local.api.DemoService&amp;<span style="color:#268bd2">lazy</span><span style="color:#719e07">=</span>false&amp;<span style="color:#268bd2">methods</span><span style="color:#719e07">=</span>sayHello&amp;<span style="color:#268bd2">pid</span><span style="color:#719e07">=</span>76198&amp;register.ip<span style="color:#719e07">=</span>169.254.146.168&amp;<span style="color:#268bd2">release</span><span style="color:#719e07">=</span>2.7.1-SNAPSHOT&amp;<span style="color:#268bd2">scope</span><span style="color:#719e07">=</span>local&amp;<span style="color:#268bd2">side</span><span style="color:#719e07">=</span>consumer&amp;<span style="color:#268bd2">sticky</span><span style="color:#719e07">=</span>false&amp;<span style="color:#268bd2">timestamp</span><span style="color:#719e07">=</span><span style="color:#2aa198">1553072598838</span> to the consumer 169.254.146.168 use dubbo version 2.7.1 |
| </span></span></code></pre></div><h3 id="何时无法使用本地调用">何时无法使用本地调用</h3> |
| <p>默认情况下,本地调用是自动开启的,不需要做额外的配置。只有当需要关闭的时候,才需要通过 <em>scope</em> 的配置来显式的关闭。</p> |
| <p>但是,特别需要指出的是,在下面的几种情况下,本地调用是无法使用的:</p> |
| <p>第一,泛化调用的时候无法使用本地调用。</p> |
| <p>第二,消费者明确指定 URL 发起直连调用。当然,如果消费者指定的是 <em>injvm</em> 的 URL,最终的调用也是走本地调用的,比如:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.local.api.DemoService&#34;</span> url=<span style="color:#2aa198">&#34;injvm://127.0.0.1/org.apache.dubbo.samples.local.api.DemoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><h3 id="强制打开本地调用">强制打开本地调用</h3> |
| <p>除了通过 <em>scope</em> 来控制本地调用的行为之外,也可以通过 <em>injvm</em> 这个配置来强制打开或者禁用本地调用。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:consumer</span> injvm=<span style="color:#2aa198">&#34;false&#34;</span> ...<span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:provider</span> injvm=<span style="color:#2aa198">&#34;true&#34;</span> ...<span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>但是通过 <em>injvm</em> 来配置本地调用的方式已经被废弃。通过 <em>scope</em> 的方式来控制是官方推荐的。</p> |
| <h3 id="总结">总结</h3> |
| <p>本文介绍了本地调用的概念以及带来的好处,并进一步的揭示了 dubbo 本地调用实际上是在当前进程中暴露了 <em>injvm</em> 的协议,而该协议并不会对外暴露端口,然后讨论了如何通过 <em>scope</em> 来细粒度的控制本地调用的行为,并强调了通过 <em>invjm</em> 来配置的方式已经被废弃,在未来版本中可能会被删除。</p></description></item><item><title>Blog: 使用Apache Skywalking (Incubator) 做分布式跟踪</title><link>https://dubbo.apache.org/zh-cn/blog/2019/08/11/%E4%BD%BF%E7%94%A8apache-skywalking-incubator-%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</link><pubDate>Sun, 11 Aug 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/08/11/%E4%BD%BF%E7%94%A8apache-skywalking-incubator-%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</guid><description> |
| <h2 id="apache-skywalkingincubator简介">Apache Skywalking(Incubator)简介</h2> |
| <p><a href="https://github.com/apache/skywalking">Apache Skywalking(Incubator)</a> 专门为微服务架构和云原生架构系统而设计并且支持分布式链路追踪的APM系统。<a href="https://github.com/apache/skywalking">Apache Skywalking(Incubator)</a>通过加载探针的方式收集应用调用链路信息,并对采集的调用链路信息进行分析,生成应用间关系和服务间关系以及服务指标。<a href="https://github.com/apache/skywalking">Apache Skywalking (Incubating)</a>目前支持多种语言,其中包括<a href="https://github.com/apache/skywalking">Java</a>,<a href="https://github.com/OpenSkywalking/skywalking-netcore">.Net Core</a>,<a href="https://github.com/OpenSkywalking/skywalking-nodejs">Node.js</a>和<a href="https://github.com/SkyAPM/go2sky">Go</a>语言。</p> |
| <p>目前Skywalking已经支持从6个可视化维度剖析分布式系统的运行情况。总览视图是应用和组件的全局视图,其中包括组件和应用数量,应用的告警波动,慢服务列表以及应用吞吐量;拓扑图从应用依赖关系出发,展现整个应用的拓扑关系;应用视图则是从单个应用的角度,展现应用的上下游关系,TopN的服务和服务器,JVM的相关信息以及对应的主机信息。服务视图关注单个服务入口的运行情况以及此服务的上下游依赖关系,依赖度,帮助用户针对单个服务的优化和监控;调用链展现了调用的单次请求经过的所有埋点以及每个埋点的执行时长;告警视图根据配置阈值针对应用、服务器、服务进行实时告警。</p> |
| <h2 id="dubbo与apache-skywalkingincubator">Dubbo与Apache Skywalking(Incubator)</h2> |
| <h3 id="编写dubbo示例程序">编写Dubbo示例程序</h3> |
| <p>Dubbo实例程序已上传到<a href="https://github.com/SkywalkingTest/dubbo-trace-example">Github仓库</a>中。方便大家下载使用。</p> |
| <h4 id="api工程">API工程</h4> |
| <p>服务接口:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#719e07">package</span> org.apache.skywalking.demo.interfaces; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>public <span style="color:#268bd2">interface</span> HelloService { |
| </span></span><span style="display:flex;"><span> String <span style="color:#268bd2">sayHello</span>(String name); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h4 id="dubbo服务提供工程">Dubbo服务提供工程</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#719e07">package</span> org.apache.skywalking.demo.provider; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>@<span style="color:#268bd2">Service</span>(version = <span style="color:#2aa198">&#34;${demo.service.version}&#34;</span>, |
| </span></span><span style="display:flex;"><span> application = <span style="color:#2aa198">&#34;${dubbo.application.id}&#34;</span>, |
| </span></span><span style="display:flex;"><span> protocol = <span style="color:#2aa198">&#34;${dubbo.protocol.id}&#34;</span>, |
| </span></span><span style="display:flex;"><span> registry = <span style="color:#2aa198">&#34;${dubbo.registry.id}&#34;</span>, timeout = <span style="color:#2aa198">60000</span>) |
| </span></span><span style="display:flex;"><span>public class HelloServiceImpl implements HelloService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> public String <span style="color:#268bd2">sayHello</span>(String name) { |
| </span></span><span style="display:flex;"><span> LockSupport.<span style="color:#268bd2">parkNanos</span>(TimeUnit.SECONDS.<span style="color:#268bd2">toNanos</span>(<span style="color:#2aa198">1</span>)); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;Hello, &#34;</span> <span style="color:#719e07">+</span> name; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h4 id="consumer工程">Consumer工程</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#719e07">package</span> org.apache.skywalking.demo.consumer; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>@RestController |
| </span></span><span style="display:flex;"><span>public class ConsumerController { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> private static <span style="color:#dc322f">int</span> COUNT = <span style="color:#2aa198">0</span>; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> @<span style="color:#268bd2">Reference</span>(version = <span style="color:#2aa198">&#34;${demo.service.version}&#34;</span>, |
| </span></span><span style="display:flex;"><span> application = <span style="color:#2aa198">&#34;${dubbo.application.id}&#34;</span>, |
| </span></span><span style="display:flex;"><span> url = <span style="color:#2aa198">&#34;dubbo://localhost:20880&#34;</span>, timeout = <span style="color:#2aa198">60000</span>) |
| </span></span><span style="display:flex;"><span> private HelloService helloService; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> @<span style="color:#268bd2">GetMapping</span>(<span style="color:#2aa198">&#34;/sayHello/{name}&#34;</span>) |
| </span></span><span style="display:flex;"><span> public String <span style="color:#268bd2">sayHello</span>(@<span style="color:#268bd2">PathVariable</span>(name = <span style="color:#2aa198">&#34;name&#34;</span>) String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> ((COUNT<span style="color:#719e07">++</span>) <span style="color:#719e07">%</span> <span style="color:#2aa198">3</span> <span style="color:#719e07">==</span> <span style="color:#2aa198">0</span>){ |
| </span></span><span style="display:flex;"><span> throw new <span style="color:#268bd2">RuntimeException</span>(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> LockSupport.<span style="color:#268bd2">parkNanos</span>(TimeUnit.SECONDS.<span style="color:#268bd2">toNanos</span>(<span style="color:#2aa198">2</span>)); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> helloService.<span style="color:#268bd2">sayHello</span>(name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h3 id="部署apache-skywalkingincubator">部署Apache Skywalking(Incubator)</h3> |
| <p>Apache Skywalking(Incubator)共提供两种部署模式:单节点模式和集群模式,以下为单节点模式部署步骤,集群模式部署详情参考<a href="https://skywalking.apache.org/docs/main/next/en/setup/backend/backend-setup/">文档</a>。</p> |
| <h4 id="依赖第三方组件">依赖第三方组件</h4> |
| <ol> |
| <li>JDK8+</li> |
| <li>Elasticsearch 5.x</li> |
| </ol> |
| <h4 id="部署步骤">部署步骤</h4> |
| <ol> |
| <li>下载<a href="http://skywalking.apache.org/downloads/"> Apache Skywalking Collector</a></li> |
| <li>部署ElasticSearch |
| <ul> |
| <li>修改elasticsearch.yml文件,并设置<code>cluster.name</code>设置成<code>CollectorDBCluster</code>。此名称需要和collector配置文件一致。</li> |
| <li>修改ES配置<code>network.host</code>值,将<code>network.host</code>的值修改成<code>0.0.0.0</code>。</li> |
| <li>启动Elasticsearch</li> |
| </ul> |
| </li> |
| <li>解压并启动Skywalking Collector。运行<code>bin/startup.sh</code>命令即可启动Skywalking Collector</li> |
| </ol> |
| <h4 id="启动示例程序">启动示例程序</h4> |
| <p>在启动示例程序之前,执行编译打包的命令:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>./mvnw clean package |
| </span></span></code></pre></div><h4 id="启动服务提供端">启动服务提供端</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>java -jar -javaagent:$AGENT_PATH/skywalking-agent.jar -Dskywalking.agent.application_code=dubbo-provider -Dskywalking.collector.servers=localhost:10800 dubbo-provider/target/dubbo-provider.jar |
| </span></span></code></pre></div><h4 id="启动服务消费端">启动服务消费端</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>java -jar -javaagent:$AGENT_PATH/skywalking-agent.jar -Dskywalking.agent.application_code=dubbo-consumer -Dskywalking.collector.servers=localhost:10800 dubbo-consumer/target/dubbo-consumer.jar |
| </span></span></code></pre></div><h4 id="访问消费端提供的服务">访问消费端提供的服务</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>curl http://localhost:8080/sayHello/test |
| </span></span></code></pre></div><h2 id="skywalking监控截图">Skywalking监控截图:</h2> |
| <h3 id="首页">首页</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/skywalking-dashboard.png" alt="/admin-guide/images/skywalking-dashboard.png"></p> |
| <h3 id="拓扑图">拓扑图</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/skywalking-topology.png" alt="/admin-guide/images/skywalking-topology.png"></p> |
| <h3 id="应用视图">应用视图</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/skywalking-application.png" alt="/admin-guide/images/skywalking-application.png"></p> |
| <p>JVM信息 |
| <img src="https://dubbo.apache.org/imgs/blog/skywalking-application_instance.png" alt="/admin-guide/images/skywalking-application_instance.png"></p> |
| <h3 id="服务视图">服务视图</h3> |
| <p>服务消费端: |
| <img src="https://dubbo.apache.org/imgs/blog/skywalking-service-consumer.png" alt="/admin-guide/images/skywalking-service-consumer.png"></p> |
| <p>服务提供端: |
| <img src="https://dubbo.apache.org/imgs/blog/skywalking-service-provider.png" alt="/admin-guide/images/skywalking-service-provider.png"></p> |
| <h3 id="trace视图">Trace视图</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/skywalking-trace.png" alt="/admin-guide/images/skywalking-trace.png"></p> |
| <p>Span信息: |
| <img src="https://dubbo.apache.org/imgs/blog/skywalking-span-Info.png" alt="/admin-guide/images/skywalking-span-Info.png"></p> |
| <h3 id="告警视图">告警视图</h3> |
| <p><img src="https://dubbo.apache.org/imgs/blog/skywalking-alarm.png" alt="/admin-guide/images/skywalking-alarm.png"></p></description></item><item><title>Blog: 在 Dubbo 中使用 REST</title><link>https://dubbo.apache.org/zh-cn/blog/2019/07/26/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-rest/</link><pubDate>Fri, 26 Jul 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/07/26/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-rest/</guid><description> |
| <h2 id="什么是-rest">什么是 REST</h2> |
| <p>REST 是 Roy Thomas Fielding <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> 在 2000 年他的博士论文 <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> “架构风格以及基于网络的软件架构设计” 中提出来的一个概念。REST 是 <strong>RE</strong>presentational <strong>S</strong>tate <strong>T</strong>ransfer 的缩写,翻译过来就是 “表现层状态转化”。REST 就是 Roy 在这篇论文中提出的面向互联网的软件所应当具备的架构风格。</p> |
| <p>按照 REpresentational State Transfer 的字面意思,可以把应用看成是一个虚拟的状态机,软件提供的不是服务而是一系列的<strong>资源</strong>,对这些资源的访问通过<strong>统一的操作</strong>来访问,而返回的结果代表了资源状态的一次跃迁。REST 是一种架构风格,如果一个软件架构符合 REST 风格,就可以称之为 RESTful 架构。这个架构应当具备以下一些设计上的约束:资源具有唯一标示、资源之间有关联关系、使用标准的方式来访问、资源有多种表现形式、无状态交互。</p> |
| <p>举例来说,一个简单的静态 HTML 页面的网站就很好的符合了 RESTful 架构风格。访问 <code>http://example.com/accounts</code> 返回一个包含所有账号的页面,选取其中一个链接 <code>http://example.com/accounts/1</code> 又会返回包含用户 1 的详细信息。爬虫软件在这种场景下工作的很好,当知道了某个网站的首页地址后,可以自举发现这个网站上所有关联的网页。更重要的是,这种访问形式不依赖网站提供的任何客户端,而是仅仅通过 HTTP 标准的访问方式完成的。可以说,HTML 这种超媒体文档的组织形式就是资源的表现层状态迁移的一种形式。</p> |
| <p>对于一个提供服务的动态网站来说,可以按照类似的思路将其 RESTful 化:</p> |
| <ul> |
| <li> |
| <p>GET <code>http://example.com/accounts</code> 返回所有账号信息</p> |
| </li> |
| <li> |
| <p>POST <code>http://example.com/accounts</code> 创建一个新的账号</p> |
| </li> |
| <li> |
| <p>GET <code>http://example.com/accounts/1</code> 返回账号 1 的信息</p> |
| </li> |
| <li> |
| <p>DELETE <code>http://example.com/accounts/1</code> 删除账号 1</p> |
| </li> |
| <li> |
| <p>PUT <code>http://example.com/accounts/1</code> 更新账号 1 信息</p> |
| </li> |
| </ul> |
| <p>其中的思路是利用 HTTP 协议的标准方法 POST、DELETE、PUT、GET 来表达对于一个资源的增删改查 (CRUD) 操作,利用 URL 来表示一个资源的唯一标识。对资源访问的错误码也复用 HTTP 协议的状态码。返回结果通常由 json 或 XML 来表示,如果其中包换了对关联资源的访问方式 (所谓的表现层状态迁移) ,这种类型的 RESTful 应用可以进一步的称之为 <em>hypermedia as the engine of application state</em> (HATEOAS) 应用 <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/rest-sample.png" alt="micro-image"></p> |
| <p><em>source: /imgs/blog/2019/07/26/rest/micro-image.png</em></p> |
| <p>这里需要注意的是,按照 Roy 的定义,RESTful 架构风格与 HTTP 协议并没有什么强关联关系。但是,基于 HTTP 的 RESTful 架构风格是实现起来最自然,也是目前使用最广泛的一种实现,我们称之为 RESTful HTTP。同样的,在下文中将会专注在 HTTP 的场景下介绍如何在 Dubbo 框架中将服务暴露成 Restful 架构。</p> |
| <h2 id="在-dubbo-中使用-rest">在 Dubbo 中使用 REST</h2> |
| <h3 id="背景">背景</h3> |
| <p>随着微服务的流行以及多语言互操作诉求日益增多,在 Dubbo 中暴露 REST 服务变成了一个不容忽视的诉求。为了在 Dubbo 中暴露 REST 服务,通常有两种做法,一种是直接依赖 Sprng REST 或者其他 REST 框架来直接暴露,另一种是通过 Dubbo 框架内置的 REST 能力暴露。两种做法各有优缺点,主要体现在前者与微服务体系中的服务发现组件能够更好的工作,而后者可以无缝的享受到 Dubbo 体系中的服务发现以及服务治理的能力。本文关注的是如何使用后者来暴露 REST 服务。</p> |
| <p>自 <code>2.6.0</code> 开始,Dubbo 合并了当当网捐献的 DubboX <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> 中的主要特性,其中就包括了基于 RESTeasy <code>3.0.19.Final</code> 的 REST 支持,具备 JAXRS 2.0 规范中所有的能力。</p> |
| <h3 id="基本用法">基本用法</h3> |
| <p>在以下的例子中,展示了如何通过最传统的 Spring XML 配置的方式来快速的暴露和调用一个 REST 服务。其中底层的 server 使用的是 netty,服务注册发现基于 Zookeeper。</p> |
| <blockquote> |
| <p>注:本章节讨论的示例可以通过 <a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/basic">https://github.com/beiwei30/dubbo-rest-samples/tree/master/basic</a> 来获得</p> |
| </blockquote> |
| <h4 id="1-maven-依赖">1. Maven 依赖</h4> |
| <p>首先需要在项目中引入 dubbo all-in-one 的依赖以及 RESTEasy 相关的必要依赖。因为在本例中使用 Zookeeper 作为服务发现,还需要引入 Zookeeper client 相关的依赖。为了方便使用,第三方的依赖可以通过框架提供的 BOM 文件 <code>dubbo-dependencies-bom</code> 来引入。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;properties&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo.version&gt;</span>2.6.5<span style="color:#268bd2">&lt;/dubbo.version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/properties&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependencyManagement&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependencies&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>com.alibaba<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>dubbo-dependencies-bom<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${dubbo.version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;type&gt;</span>pom<span style="color:#268bd2">&lt;/type&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;scope&gt;</span>import<span style="color:#268bd2">&lt;/scope&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependencies&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependencyManagement&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependencies&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>com.alibaba<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>dubbo<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${dubbo.version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">&lt;!-- REST support dependencies --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>io.netty<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>netty-all<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.jboss.resteasy<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>resteasy-jaxrs<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.jboss.resteasy<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>resteasy-client<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.jboss.resteasy<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>resteasy-netty4<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>javax.validation<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>validation-api<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.jboss.resteasy<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>resteasy-jackson-provider<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.jboss.resteasy<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>resteasy-jaxb-provider<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>javax.servlet<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>javax.servlet-api<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">&lt;!-- zookeeper client dependency --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.curator<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>curator-framework<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependencies&gt;</span> |
| </span></span></code></pre></div><h4 id="2-定义服务接口">2. 定义服务接口</h4> |
| <p>定义一个服务接口 <code>UserService</code>,该接口提供两个功能,一个是获取指定 User 的详细信息,另一个是新注册一个用户。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Path</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;users&#34;</span><span style="color:#719e07">)</span> <span style="color:#586e75">// #1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@Consumes</span><span style="color:#719e07">({</span>MediaType<span style="color:#719e07">.</span>APPLICATION_JSON<span style="color:#719e07">,</span> MediaType<span style="color:#719e07">.</span>TEXT_XML<span style="color:#719e07">})</span> <span style="color:#586e75">// #2 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@Produces</span><span style="color:#719e07">({</span>MediaType<span style="color:#719e07">.</span>APPLICATION_JSON<span style="color:#719e07">,</span> MediaType<span style="color:#719e07">.</span>TEXT_XML<span style="color:#719e07">})</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">UserService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@GET</span> <span style="color:#586e75">// #3 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">@Path</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;{id: \\d+}&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> User <span style="color:#268bd2">getUser</span><span style="color:#719e07">(</span><span style="color:#268bd2">@PathParam</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;id&#34;</span><span style="color:#719e07">)</span> Long id<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@POST</span> <span style="color:#586e75">// #4 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">@Path</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;register&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> Long <span style="color:#268bd2">registerUser</span><span style="color:#719e07">(</span>User user<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>通过在接口上用 JaxRS 标准的 annotation 来修饰,我们规定了该服务在 REST 下的访问形式:</p> |
| <ol> |
| <li><code>@Path(&quot;users&quot;)</code> 定义了 UserService 通过 &lsquo;/users&rsquo; 来访问</li> |
| <li>在类级别上定义 <code>@Consumers</code> 和 <code>@Produces</code> 来规定参数以及返回值的类型为 XML 和 JSON。在类级别上定义之后,就可以不用在方法级别上进一步定义了</li> |
| <li>getUser 方法上通过 <code>@GET</code> 定义了接受的 HTTP 方法为 GET,通过 <code>@Path</code> 来规定参数是来自于 URL 中的 path。&lsquo;GET /users/1&rsquo; 等同于调用 &lsquo;getUser(1)&rsquo;</li> |
| <li>registerUser 方法上通过 <code>@POST</code> 定义了接受的 HTTP 方法为 POST,通过将 JSON 或 XML 格式的 User 数据 POST 到 &lsquo;/users/register&rsquo; 上来创建一个 User</li> |
| </ol> |
| <p>在 Dubbo 中,将 REST 相关的 annotation 定义在接口或者实现上都是可以的。这个在设计上是个权衡问题。Annotation 定义在实现类上可以保证接口的纯净,否则对于不需要通过 REST 方式调用的 Dubbo 调用方来说将需要强制依赖 JaxRS 的库,但是同时,对于需要通过 REST 方式调用的 Dubbo 调用方来说,就需要自己来处理 REST 调用相关的细节了。Annotation 定义在接口上,框架会自动处理掉 REST 调用相关的细节,并和 Dubbo 的服务发现以及服务治理功能能够很好的结合起来。在本例中采用了在接口上定义 JaxRS annotation 的形式。</p> |
| <h4 id="3-实现服务接口">3. 实现服务接口</h4> |
| <p>为了简洁,这里给出的接口的实现只是简单的返回了接口需要的类型的示例,在真实的系统中,逻辑可能会比较复杂。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">UserServiceImpl</span> <span style="color:#268bd2">implements</span> UserService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> AtomicLong id <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> AtomicLong<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> User <span style="color:#268bd2">getUser</span><span style="color:#719e07">(</span>Long id<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> User<span style="color:#719e07">(</span>id<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;username-&#34;</span> <span style="color:#719e07">+</span> id<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Long <span style="color:#268bd2">registerUser</span><span style="color:#719e07">(</span>User user<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> id<span style="color:#719e07">.</span>incrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="4-装配服务">4. 装配服务</h4> |
| <p>如上所述,本例展示的是如何通过传统的 Spring XML 的方式来装配并暴露 Dubbo 服务。需要指出的是,这里展示了如何同时暴露两种不同的协议,一种是 REST,另一种是原生的 Dubbo 协议。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#719e07">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;beans</span> xmlns=<span style="color:#2aa198">&#34;http://www.springframework.org/schema/beans&#34;</span> |
| </span></span><span style="display:flex;"><span> xmlns:xsi=<span style="color:#2aa198">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span> |
| </span></span><span style="display:flex;"><span> xmlns:dubbo=<span style="color:#2aa198">&#34;http://code.alibabatech.com/schema/dubbo&#34;</span> |
| </span></span><span style="display:flex;"><span> xsi:schemaLocation=<span style="color:#2aa198">&#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd&#34;</span><span style="color:#268bd2">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:application</span> name=<span style="color:#2aa198">&#34;rest-provider&#34;</span><span style="color:#268bd2">/&gt;</span> <span style="color:#586e75">&lt;!-- #1 --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#268bd2">/&gt;</span> <span style="color:#586e75">&lt;!-- #2 --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:protocol</span> name=<span style="color:#2aa198">&#34;rest&#34;</span> port=<span style="color:#2aa198">&#34;8080&#34;</span> server=<span style="color:#2aa198">&#34;netty&#34;</span><span style="color:#268bd2">/&gt;</span> <span style="color:#586e75">&lt;!-- #3 --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:protocol</span> name=<span style="color:#2aa198">&#34;dubbo&#34;</span> server=<span style="color:#2aa198">&#34;netty4&#34;</span><span style="color:#268bd2">/&gt;</span> <span style="color:#586e75">&lt;!-- #4 --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.rest.api.UserService&#34;</span> protocol=<span style="color:#2aa198">&#34;rest,dubbo&#34;</span> ref=<span style="color:#2aa198">&#34;userService&#34;</span><span style="color:#268bd2">/&gt;</span> <span style="color:#586e75">&lt;!-- #5 --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;userService&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.rest.impl.UserServiceImpl&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/beans&gt;</span> <span style="color:#586e75">&lt;!-- #6 --&gt;</span> |
| </span></span></code></pre></div><ol> |
| <li>定义了该应用的名字为 <code>rest-provider</code></li> |
| <li>定义了服务注册通过 Zookeeper,并且 URL 为 &ldquo;zookeeper://127.0.0.1:2181&rdquo;</li> |
| <li>在端口 8080 上以 REST 方式暴露服务,底层的传输使用的是 netty</li> |
| <li>在默认端口 20880 上以原生 Dubbo 方式暴露服务,底层的传输方式是 netty</li> |
| <li>将 ‘userService&rsquo; 的 Spring bean (也就是 UserServiceImpl)暴露为 UserService 服务,支持的协议既包括了 REST 也包括了 Dubbo</li> |
| <li>将 UserServiceImpl 注册成 &lsquo;userService&rsquo; 的 Spring bean</li> |
| </ol> |
| <h4 id="5-服务提供方的启动类">5. 服务提供方的启动类</h4> |
| <p>简单的通过 ClassPathXmlApplicationContext 来加载刚刚配置的 Spring XML 配置 &lsquo;rest-provider.xml&rsquo; 即可启动 Dubbo 服务端</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">RestProvider</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> IOException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ClassPathXmlApplicationContext context <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ClassPathXmlApplicationContext<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;spring/rest-provider.xml&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> context<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>in<span style="color:#719e07">.</span>read<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="6-启动服务端">6. 启动服务端</h4> |
| <p>由于本例依赖 Zookeeper 做服务注册发现,在启动 RestProvider 之前,需要先启动一个 Zookeeper 服务器。之后就可以之间运行 RestProvider 了。通过以下的输出日志,我们可以知道 UserService 以两种方式对外暴露了同一个服务,其中:</p> |
| <ul> |
| <li>REST: rest://192.168.2.132:8080/org.apache.dubbo.samples.rest.api.UserService</li> |
| <li>Dubbo: dubbo://192.168.2.132:20880/org.apache.dubbo.samples.rest.api.UserServic</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">[</span>01/01/19 07:18:56:056 CST<span style="color:#719e07">]</span> main INFO config.AbstractConfig: <span style="color:#719e07">[</span>DUBBO<span style="color:#719e07">]</span> Export dubbo service org.apache.dubbo.samples.rest.api.UserService to url rest://192.168.2.132:8080/org.apache.dubbo.samples.rest.api.UserService?anyhost<span style="color:#719e07">=</span>true&amp;<span style="color:#268bd2">application</span><span style="color:#719e07">=</span>rest-provider&amp;bean.name<span style="color:#719e07">=</span>org.apache.dubbo.samples.rest.api.UserService&amp;bind.ip<span style="color:#719e07">=</span>192.168.2.132&amp;bind.port<span style="color:#719e07">=</span>8080&amp;<span style="color:#268bd2">dubbo</span><span style="color:#719e07">=</span>2.0.2&amp;<span style="color:#268bd2">generic</span><span style="color:#719e07">=</span>false&amp;<span style="color:#268bd2">interface</span><span style="color:#719e07">=</span>org.apache.dubbo.samples.rest.api.UserService&amp;<span style="color:#268bd2">methods</span><span style="color:#719e07">=</span>getUser,registerUser&amp;<span style="color:#268bd2">pid</span><span style="color:#719e07">=</span>27386&amp;<span style="color:#268bd2">server</span><span style="color:#719e07">=</span>netty&amp;<span style="color:#268bd2">side</span><span style="color:#719e07">=</span>provider&amp;<span style="color:#268bd2">timestamp</span><span style="color:#719e07">=</span>1546341536194, dubbo version: 2.6.5, current host: 192.168.2.132 |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">[</span>01/01/19 07:18:57:057 CST<span style="color:#719e07">]</span> main INFO config.AbstractConfig: <span style="color:#719e07">[</span>DUBBO<span style="color:#719e07">]</span> Export dubbo service org.apache.dubbo.samples.rest.api.UserService to url dubbo://192.168.2.132:20880/org.apache.dubbo.samples.rest.api.UserService?anyhost<span style="color:#719e07">=</span>true&amp;<span style="color:#268bd2">application</span><span style="color:#719e07">=</span>rest-provider&amp;bean.name<span style="color:#719e07">=</span>org.apache.dubbo.samples.rest.api.UserService&amp;bind.ip<span style="color:#719e07">=</span>192.168.2.132&amp;bind.port<span style="color:#719e07">=</span>20880&amp;<span style="color:#268bd2">dubbo</span><span style="color:#719e07">=</span>2.0.2&amp;<span style="color:#268bd2">generic</span><span style="color:#719e07">=</span>false&amp;<span style="color:#268bd2">interface</span><span style="color:#719e07">=</span>org.apache.dubbo.samples.rest.api.UserService&amp;<span style="color:#268bd2">methods</span><span style="color:#719e07">=</span>getUser,registerUser&amp;<span style="color:#268bd2">pid</span><span style="color:#719e07">=</span>27386&amp;<span style="color:#268bd2">server</span><span style="color:#719e07">=</span>netty4&amp;<span style="color:#268bd2">side</span><span style="color:#719e07">=</span>provider&amp;<span style="color:#268bd2">timestamp</span><span style="color:#719e07">=</span>1546341537392, dubbo version: 2.6.5, current host: 192.168.2.132 |
| </span></span><span style="display:flex;"><span>... |
| </span></span></code></pre></div><p>也可以通过 zkCli 访问 Zookeeper 服务器来验证。&rsquo;/dubbo/org.apache.dubbo.samples.rest.api.UserService/providers&rsquo; 路径下返回了一个数组 [dubbo://&hellip;, rest:.//&hellip;]。数组的第一个元素是 ’dubbo‘ 打头的,而第二个元素是 &lsquo;rest&rsquo; 打头的。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#719e07">[</span>zk: localhost:2181<span style="color:#719e07">(</span>CONNECTED<span style="color:#719e07">)</span> 10<span style="color:#719e07">]</span> ls /dubbo/org.apache.dubbo.samples.rest.api.UserService/providers |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">[</span>dubbo%3A%2F%2F192.168.2.132%3A20880%2Forg.apache.dubbo.samples.rest.api.UserService%3Fanyhost%3Dtrue%26application%3Drest-provider%26bean.name%3Dorg.apache.dubbo.samples.rest.api.UserService%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.rest.api.UserService%26methods%3DgetUser%2CregisterUser%26pid%3D27386%26server%3Dnetty4%26side%3Dprovider%26timestamp%3D1546341537392, rest%3A%2F%2F192.168.2.132%3A8080%2Forg.apache.dubbo.samples.rest.api.UserService%3Fanyhost%3Dtrue%26application%3Drest-provider%26bean.name%3Dorg.apache.dubbo.samples.rest.api.UserService%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.rest.api.UserService%26methods%3DgetUser%2CregisterUser%26pid%3D27386%26server%3Dnetty%26side%3Dprovider%26timestamp%3D1546341536194<span style="color:#719e07">]</span> |
| </span></span></code></pre></div><p>可以简单的通过 &lsquo;curl&rsquo; 在命令行验证刚才暴露出来的 REST 服务:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ curl http://localhost:8080/users/1 |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">{</span><span style="color:#2aa198">&#34;id&#34;</span>:1,<span style="color:#2aa198">&#34;name&#34;</span>:<span style="color:#2aa198">&#34;username-1&#34;</span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span>$ curl -X POST -H <span style="color:#2aa198">&#34;Content-Type: application/json&#34;</span> -d <span style="color:#2aa198">&#39;{&#34;id&#34;:1,&#34;name&#34;:&#34;Larry Page&#34;}&#39;</span> http://localhost:8080/users/register |
| </span></span><span style="display:flex;"><span><span style="color:#2aa198">1</span> |
| </span></span></code></pre></div><h4 id="7-装配调用端">7. 装配调用端</h4> |
| <p>Dubbo 调用方只需要依赖服务的接口,通过以下方式装配好 Dubbo Consumer,即可发起调用。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#719e07">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;beans</span> xmlns=<span style="color:#2aa198">&#34;http://www.springframework.org/schema/beans&#34;</span> |
| </span></span><span style="display:flex;"><span> xmlns:xsi=<span style="color:#2aa198">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span> |
| </span></span><span style="display:flex;"><span> xmlns:dubbo=<span style="color:#2aa198">&#34;http://code.alibabatech.com/schema/dubbo&#34;</span> |
| </span></span><span style="display:flex;"><span> xsi:schemaLocation=<span style="color:#2aa198">&#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd&#34;</span><span style="color:#268bd2">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:application</span> name=<span style="color:#2aa198">&#34;rest-consumer&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:reference</span> id=<span style="color:#2aa198">&#34;userService&#34;</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.rest.api.UserService&#34;</span> protocol=<span style="color:#2aa198">&#34;rest&#34;</span><span style="color:#268bd2">/&gt;</span> <span style="color:#586e75">&lt;!-- #1 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/beans&gt;</span> |
| </span></span></code></pre></div><ol> |
| <li>&lsquo;userService&rsquo; 配置的 protocol 为 “rest&quot;,将通过 REST 协议调用服务端</li> |
| </ol> |
| <p>需要特别指出的是,这里显示的指定 protocol=&ldquo;rest&rdquo; 在通常情况下不是必须的。这里需要显示指定的原因是我们例子中服务端同时暴露了多种协议,这里指定使用 rest 是为了确保调用方走 REST 协议。</p> |
| <h4 id="8-发起调用">8. 发起调用</h4> |
| <p>简单的通过 ClassPathXmlApplicationContext 来加载刚刚配置的 Spring XML 配置 &lsquo;rest-consumer.xml&rsquo; 即可发起对 RestProvider 所提供的 UserService 的 REST 服务的调用。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">RestConsumer</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ClassPathXmlApplicationContext context <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ClassPathXmlApplicationContext<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;spring/rest-consumer.xml&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> context<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> UserService userService <span style="color:#719e07">=</span> context<span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;userService&#34;</span><span style="color:#719e07">,</span> UserService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&gt;&gt;&gt; &#34;</span> <span style="color:#719e07">+</span> userService<span style="color:#719e07">.</span>getUser<span style="color:#719e07">(</span>1L<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> User user <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> User<span style="color:#719e07">(</span>2L<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;Larry Page&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&gt;&gt;&gt; &#34;</span> <span style="color:#719e07">+</span> userService<span style="color:#719e07">.</span>registerUser<span style="color:#719e07">(</span>user<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里分别展示了对 &lsquo;getUser&rsquo; 和 &lsquo;registerUser&rsquo; 的调用,输出结果如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>&gt;&gt;&gt; User<span style="color:#719e07">{</span><span style="color:#268bd2">id</span><span style="color:#719e07">=</span>1, <span style="color:#268bd2">name</span><span style="color:#719e07">=</span><span style="color:#2aa198">&#39;username-1&#39;</span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span>&gt;&gt;&gt; <span style="color:#2aa198">2</span> |
| </span></span></code></pre></div><h3 id="进阶">进阶</h3> |
| <h4 id="a-在-rest-中使用-annotation">A. 在 REST 中使用 Annotation</h4> |
| <p>在 Dubbo 中使用 annotation 而不是 Spring XML 来暴露和引用服务,对于 REST 协议来说并没有什么不同。有关如何使用 annotation 更详细的用法,请参阅《在 Dubbo 中使用注解》章节。这里主要展示一下与上面基于 Spring XML 配置的例子不同之处。</p> |
| <blockquote> |
| <p>注:本章节讨论的示例可以通过 <a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/annotation">https://github.com/beiwei30/dubbo-rest-samples/tree/master/annotation</a> 来获得</p> |
| </blockquote> |
| <h5 id="1-使用-java-configuration-来配置服务提供方的-protocolregistryapplication">1. 使用 Java Configuration 来配置服务提供方的 protocol、registry、application</h5> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Configuration</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@EnableDubbo</span><span style="color:#719e07">(</span>scanBasePackages <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;org.apache.dubbo.samples.rest.impl&#34;</span><span style="color:#719e07">)</span> <span style="color:#586e75">// #1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ProviderConfiguration</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Bean</span> <span style="color:#586e75">// #2 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> ProtocolConfig <span style="color:#268bd2">protocolConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ProtocolConfig protocolConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> protocolConfig<span style="color:#719e07">.</span>setName<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;rest&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> protocolConfig<span style="color:#719e07">.</span>setPort<span style="color:#719e07">(</span>8080<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> protocolConfig<span style="color:#719e07">.</span>setServer<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;netty&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocolConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Bean</span> <span style="color:#586e75">// #3 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> RegistryConfig <span style="color:#268bd2">registryConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> RegistryConfig registryConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setAddress<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;localhost&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setPort<span style="color:#719e07">(</span>2181<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> registryConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Bean</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> ApplicationConfig <span style="color:#268bd2">applicationConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setName<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;rest-provider&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> applicationConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ol> |
| <li>通过 <code>@EnableDubbo</code> 来指定需要扫描 Dubbo 服务的包名,在本例中,UserServiceImpl 在 &ldquo;org.apache.dubbo.samples.rest.impl&rdquo; 下</li> |
| <li>通过提供一个 ProtocolConfig 的 Spring Bean 来指定服务提供方按照 REST 来暴露服务</li> |
| <li>通过提供一个 RegistryConfig 的 Spring Bean 来指定服务提供方所使用的服务注册机制</li> |
| </ol> |
| <h5 id="2-使用-service-来申明-dubbo-服务">2. 使用 @Service 来申明 Dubbo 服务</h5> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Service</span> <span style="color:#586e75">// #1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">UserServiceImpl</span> <span style="color:#268bd2">implements</span> UserService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ol> |
| <li>简单的使用 <code>@Service</code> 或者 <code>@Service(protocol = &quot;rest&quot;)</code> 修饰 &ldquo;UserServiceImpl&rdquo; 来申明一个 Dubbo 服务,这里 <code>protocol = &quot;rest&quot;</code> 不是必须提供的,原因是通过 Java Configuration 只配置了一个 ProtocolConfig 的示例,在这种情况下,Dubbo 会自动装配该协议到服务中</li> |
| </ol> |
| <h5 id="3-服务提供方启动类">3. 服务提供方启动类</h5> |
| <p>通过使用 <code>ProviderConfiguration</code> 来初始化一个 <code>AnnotationConfigApplicationContext</code> 实例,就可以完全摆脱 Spring XML 的配置文件,完全借助 annotation 来装配好一个 Dubbo 的服务提供方。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">RestProvider</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> IOException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> AnnotationConfigApplicationContext context <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> AnnotationConfigApplicationContext<span style="color:#719e07">(</span>ProviderConfiguration<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> context<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>in<span style="color:#719e07">.</span>read<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h5 id="4-使用-java-configuration-来配置服务消费方的-registryapplication">4. 使用 Java Configuration 来配置服务消费方的 registry、application</h5> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Configuration</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@EnableDubbo</span><span style="color:#719e07">(</span>scanBasePackages <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;org.apache.dubbo.samples.rest.comp&#34;</span><span style="color:#719e07">)</span> <span style="color:#586e75">// #1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">@ComponentScan</span><span style="color:#719e07">({</span><span style="color:#2aa198">&#34;org.apache.dubbo.samples.rest.comp&#34;</span><span style="color:#719e07">})</span> <span style="color:#586e75">// #2 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ConsumerConfiguration</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Bean</span> <span style="color:#586e75">// #3 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> RegistryConfig <span style="color:#268bd2">registryConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> RegistryConfig registryConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setAddress<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;localhost&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setPort<span style="color:#719e07">(</span>2181<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> registryConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Bean</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> ApplicationConfig <span style="color:#268bd2">applicationConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setName<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;rest-consumer&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> applicationConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ol> |
| <li>通过 <code>@EnableDubbo</code> 来指定需要扫描 Dubbo 服务引用 <code>@Reference</code> 的包名。在本例中,UserService 的引用在 &ldquo;org.apache.dubbo.samples.rest.comp&rdquo; 下</li> |
| <li>通过 <code>@ComponentScan</code> 来指定需要扫描的 Spring Bean 的包名。在本例中,包含 UserService 引用的类 UserServiceComponent 本身需要是一个 Spring Bean,以方便调用,所以,这里指定的包名也是 &ldquo;org.apache.dubbo.samples.rest.comp&rdquo;</li> |
| <li>通过提供一个 RegistryConfig 的 Spring Bean 来指定服务消费方所使用的服务发现机制</li> |
| </ol> |
| <p>这里提到的 UserServiceComponent 的 Spring Bean 定义如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Component</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">UserServiceComponent</span> <span style="color:#268bd2">implements</span> UserService <span style="color:#719e07">{</span> <span style="color:#586e75">// #1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">@Reference</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> UserService userService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> User <span style="color:#268bd2">getUser</span><span style="color:#719e07">(</span>Long id<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> userService<span style="color:#719e07">.</span>getUser<span style="color:#719e07">(</span>id<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Long <span style="color:#268bd2">registerUser</span><span style="color:#719e07">(</span>User user<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> userService<span style="color:#719e07">.</span>registerUser<span style="color:#719e07">(</span>user<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ol> |
| <li>这里比较好的实践是让这个 Spring Bean 也继承 <code>UserService</code> 接口,这样在调用的时候也可以面向接口编程</li> |
| </ol> |
| <h5 id="5-服务调用方启动类">5. 服务调用方启动类</h5> |
| <p>通过使用 <code>ConsumerConfiguration</code> 来初始化一个 <code>AnnotationConfigApplicationContext</code> 实例,就可以完全摆脱 Spring XML 的配置文件,完全借助 annotation 来装配好一个 Dubbo 的服务消费方。然后就可以通过查找 <code>UserServiceComponent</code> 类型的 Spring Bean 来发起远程调用。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">RestConsumer</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> AnnotationConfigApplicationContext context <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> AnnotationConfigApplicationContext<span style="color:#719e07">(</span>ConsumerConfiguration<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> context<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> UserService userService <span style="color:#719e07">=</span> context<span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span>UserServiceComponent<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&gt;&gt;&gt; &#34;</span> <span style="color:#719e07">+</span> userService<span style="color:#719e07">.</span>getUser<span style="color:#719e07">(</span>1L<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> User user <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> User<span style="color:#719e07">(</span>2L<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;Larry Page&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&gt;&gt;&gt; &#34;</span> <span style="color:#719e07">+</span> userService<span style="color:#719e07">.</span>registerUser<span style="color:#719e07">(</span>user<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="b-让协议跑在不同的服务器上">B. 让协议跑在不同的服务器上</h4> |
| <p>目前 REST 协议在 Dubbo 中可以跑在五种不同的 server 上,分别是:</p> |
| <ul> |
| <li>&ldquo;netty&rdquo;: 直接基于 netty 框架的 rest server,通过 <code>&lt;dubbo:protocol name=&quot;rest&quot; server=&quot;netty&quot;/&gt;</code> 来配置</li> |
| <li>&ldquo;tomcat&rdquo;: 基于嵌入式 tomcat 的 rest server,通过 <code>&lt;dubbo:protocol name=&quot;rest&quot; server=&quot;tomcat&quot;/&gt;</code> 来配置</li> |
| <li>&ldquo;jetty&rdquo;: <strong>默认选项</strong>,基于嵌入式 jetty 的 rest server,通过 <code>&lt;dubbo:protocol name=&quot;rest&quot; server=&quot;jetty&quot;/&gt;</code> 来配置</li> |
| <li>&ldquo;sunhttp&rdquo;: 使用 JDK 内置的 Sun HTTP server 作为 rest server,通过 <code>&lt;dubbo:protocol name=&quot;rest&quot; server=&quot;sunhttp&quot;/&gt;</code> 来配置,仅推荐在开发环境中使用</li> |
| <li>&ldquo;servlet”: 采用外部应用服务器的 servlet 容器来做 rest server,这个时候,除了配置 <code>&lt;dubbo:protocol name=&quot;rest&quot; server=&quot;servlet&quot;/&gt;</code> 之外,还需要在 web.xml 中做额外的配置</li> |
| </ul> |
| <p>由于以上的例子展示了 &ldquo;netty&rdquo; 作为 rest server,下面演示一下使用嵌入式 tomcat 的 rest server 的用法。</p> |
| <blockquote> |
| <p>注:本章节讨论的示例可以通过 <a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/tomcat">https://github.com/beiwei30/dubbo-rest-samples/tree/master/tomcat</a> 来获得</p> |
| </blockquote> |
| <h5 id="1-增加-tomcat-相关的依赖">1. 增加 Tomcat 相关的依赖</h5> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.tomcat.embed<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>tomcat-embed-core<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.apache.tomcat.embed<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>tomcat-embed-logging-juli<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span></code></pre></div><h5 id="2-配置-protocol-使用-tomcat-作为-rest-server">2. 配置 protocol 使用 tomcat 作为 REST server</h5> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:protocol</span> name=<span style="color:#2aa198">&#34;rest&#34;</span> port=<span style="color:#2aa198">&#34;8080&#34;</span> server=<span style="color:#2aa198">&#34;tomcat&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>启动服务提供方之后,在以下的输出将会出现与嵌入式 Tomcat 相关的日志信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>Jan 01, <span style="color:#2aa198">2019</span> 10:15:12 PM org.apache.catalina.core.StandardContext setPath |
| </span></span><span style="display:flex;"><span>WARNING: A context path must either be an empty string or start with a <span style="color:#2aa198">&#39;/&#39;</span> and <span style="color:#719e07">do</span> not end with a <span style="color:#2aa198">&#39;/&#39;</span>. The path <span style="color:#719e07">[</span>/<span style="color:#719e07">]</span> does not meet these criteria and has been changed to <span style="color:#719e07">[]</span> |
| </span></span><span style="display:flex;"><span>Jan 01, <span style="color:#2aa198">2019</span> 10:15:13 PM org.apache.coyote.AbstractProtocol init |
| </span></span><span style="display:flex;"><span>INFO: Initializing ProtocolHandler <span style="color:#719e07">[</span><span style="color:#2aa198">&#34;http-nio-8080&#34;</span><span style="color:#719e07">]</span> |
| </span></span><span style="display:flex;"><span>Jan 01, <span style="color:#2aa198">2019</span> 10:15:13 PM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector |
| </span></span><span style="display:flex;"><span>INFO: Using a shared selector <span style="color:#719e07">for</span> servlet write/read |
| </span></span><span style="display:flex;"><span>Jan 01, <span style="color:#2aa198">2019</span> 10:15:13 PM org.apache.catalina.core.StandardService startInternal |
| </span></span><span style="display:flex;"><span>INFO: Starting service <span style="color:#719e07">[</span>Tomcat<span style="color:#719e07">]</span> |
| </span></span><span style="display:flex;"><span>Jan 01, <span style="color:#2aa198">2019</span> 10:15:13 PM org.apache.catalina.core.StandardEngine startInternal |
| </span></span><span style="display:flex;"><span>INFO: Starting Servlet Engine: Apache Tomcat/8.5.31 |
| </span></span><span style="display:flex;"><span>Jan 01, <span style="color:#2aa198">2019</span> 10:15:13 PM org.apache.coyote.AbstractProtocol start |
| </span></span><span style="display:flex;"><span>INFO: Starting ProtocolHandler <span style="color:#719e07">[</span><span style="color:#2aa198">&#34;http-nio-8080&#34;</span><span style="color:#719e07">]</span> |
| </span></span></code></pre></div><h4 id="c-使用外部的-servlet-容器">C. 使用外部的 Servlet 容器</h4> |
| <p>进一步的,还可以使用外部的 servlet 容器来启动 Dubbo 的 REST 服务。</p> |
| <blockquote> |
| <p>注:本章节讨论的示例可以通过 <a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet">https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet</a> 来获得</p> |
| </blockquote> |
| <h5 id="1-修改-pomxml-改变打包方式">1. 修改 pom.xml 改变打包方式</h5> |
| <p>因为使用的是外部的 servlet 容器,需要将打包方式修改为 &ldquo;war&rdquo;</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;packaging&gt;</span>war<span style="color:#268bd2">&lt;/packaging&gt;</span> |
| </span></span></code></pre></div><h5 id="2-修改-rest-providerxml">2. 修改 rest-provider.xml</h5> |
| <p>配置 &ldquo;server&rdquo; 为 &ldquo;servlet&rdquo; 表示将使用外部的 servlet 容器。并配置 &ldquo;contextpath&rdquo; 为 &ldquo;&quot;,原因是在使用外部 servlet 容器时,Dubbo 的 REST 支持需要知道被托管的 webapp 的 contextpath 是什么。这里我们计划通过 root context path 来部署应用,所以配置其为 &ldquo;&quot;。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dubbo:protocol</span> name=<span style="color:#2aa198">&#34;rest&#34;</span> port=<span style="color:#2aa198">&#34;8080&#34;</span> server=<span style="color:#2aa198">&#34;servlet&#34;</span> contextpath=<span style="color:#2aa198">&#34;&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><h5 id="3-配置-web-infwebxml">3. 配置 WEB-INF/web.xml</h5> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#719e07">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;web-app</span> xmlns=<span style="color:#2aa198">&#34;http://xmlns.jcp.org/xml/ns/javaee&#34;</span> |
| </span></span><span style="display:flex;"><span> xmlns:xsi=<span style="color:#2aa198">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span> |
| </span></span><span style="display:flex;"><span> xsi:schemaLocation=<span style="color:#2aa198">&#34;http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd&#34;</span> |
| </span></span><span style="display:flex;"><span> version=<span style="color:#2aa198">&#34;3.1&#34;</span><span style="color:#268bd2">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;context-param&gt;</span> <span style="color:#586e75">&lt;!-- #1 --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;param-name&gt;</span>contextConfigLocation<span style="color:#268bd2">&lt;/param-name&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;param-value&gt;</span>/WEB-INF/classes/spring/rest-provider.xml<span style="color:#268bd2">&lt;/param-value&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/context-param&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;listener&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;listener-class&gt;</span>com.alibaba.dubbo.remoting.http.servlet.BootstrapListener<span style="color:#268bd2">&lt;/listener-class&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/listener&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;listener&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;listener-class&gt;</span>org.springframework.web.context.ContextLoaderListener<span style="color:#268bd2">&lt;/listener-class&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/listener&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet&gt;</span> <span style="color:#586e75">&lt;!-- #2 --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet-name&gt;</span>dispatcher<span style="color:#268bd2">&lt;/servlet-name&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet-class&gt;</span>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet<span style="color:#268bd2">&lt;/servlet-class&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;load-on-startup&gt;</span>1<span style="color:#268bd2">&lt;/load-on-startup&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/servlet&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet-mapping&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet-name&gt;</span>dispatcher<span style="color:#268bd2">&lt;/servlet-name&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;url-pattern&gt;</span>/api/*<span style="color:#268bd2">&lt;/url-pattern&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/servlet-mapping&gt;</span> |
| </span></span></code></pre></div><ol> |
| <li>配置 Dubbo 和 Spring 相关的 ContextListener,打开 Dubbo HTTP 支持,以及通过 rest-provider.xml 来装配 Dubbo 服务</li> |
| <li>配置 Dubbo HTTP 所需的 DiapatcherServlet</li> |
| </ol> |
| <p>这样做之后,不再需要 RestProvider 来启动 Dubbo 服务,可以将其从工程中删掉。对应的,现在 Dubbo 的服务将会随着 Servlet 容器的启动而启动。启动完毕之后,可以通过类似 &ldquo;http://localhost:8080/api/users/1&rdquo; 来访问暴露出的 REST 服务。需要注意的是,这个例子里假定了服务提供方的 WAR 包部署在 root context path 上,所以当该应用通过 IDE 配置的 tomcat server 启动时,需要指定 Application Context 为 &ldquo;/&quot;。</p> |
| <h4 id="d-增加-swagger-支持">D. 增加 Swagger 支持</h4> |
| <p>在上面使用外部 Servlet 容器的例子的基础上,讨论如何暴露 Swagger OpenApi 以及如何继承 Swagger UI。</p> |
| <blockquote> |
| <p>注:本章节讨论的示例可以通过 <a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet">https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet</a> 来获得</p> |
| </blockquote> |
| <h5 id="1-暴露-swagger-openapi">1. 暴露 Swagger OpenApi</h5> |
| <p>增加 swagger 相关依赖,以便通过 &ldquo;http://localhost:8080/openapi.json&rdquo; 来访问 REST 服务的描述</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;properties&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;swagger.version&gt;</span>2.0.6<span style="color:#268bd2">&lt;/swagger.version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/properties&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependencies&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>io.swagger.core.v3<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>swagger-jaxrs2<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${swagger.version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>io.swagger.core.v3<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>swagger-jaxrs2-servlet-initializer<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${swagger.version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependencies&gt;</span> |
| </span></span></code></pre></div><p>修改 WEB-INF/web.xml,增加 openapi servlet 的配置</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;web-app&gt;</span> |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet&gt;</span> <span style="color:#586e75">&lt;!-- #3 --&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet-name&gt;</span>openapi<span style="color:#268bd2">&lt;/servlet-name&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet-class&gt;</span>io.swagger.v3.jaxrs2.integration.OpenApiServlet<span style="color:#268bd2">&lt;/servlet-class&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/servlet&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet-mapping&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;servlet-name&gt;</span>openapi<span style="color:#268bd2">&lt;/servlet-name&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;url-pattern&gt;</span>/openapi.json<span style="color:#268bd2">&lt;/url-pattern&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;url-pattern&gt;</span>/openapi.yaml<span style="color:#268bd2">&lt;/url-pattern&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/servlet-mapping&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;/web-app&gt;</span> |
| </span></span></code></pre></div><p>重新启动应用之后,可以通过访问 &ldquo;http://localhost:8080/openapi.json&rdquo; 或者 &ldquo;http://localhost:8080/openapi.yaml&rdquo; 来访问暴露出的 openapi 的契约,以下是 yaml 格式的表述:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">openapi</span>: <span style="color:#2aa198">3.0.1</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">paths</span>: |
| </span></span><span style="display:flex;"><span> /api/users/{id}: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">get</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">operationId</span>: getUser |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">parameters</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">name</span>: id |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">in</span>: path |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">required</span>: <span style="color:#cb4b16">true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">schema</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type</span>: integer |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">format</span>: int64 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">responses</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">default</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">description</span>: default response |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">content</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">application/json</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">schema</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">$ref</span>: <span style="color:#2aa198">&#39;#/components/schemas/User&#39;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">text/xml</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">schema</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">$ref</span>: <span style="color:#2aa198">&#39;#/components/schemas/User&#39;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">/api/users/register</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">post</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">operationId</span>: registerUser |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">requestBody</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">description</span>: a user to register |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">content</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">application/json</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">schema</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">$ref</span>: <span style="color:#2aa198">&#39;#/components/schemas/User&#39;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">text/xml</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">schema</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">$ref</span>: <span style="color:#2aa198">&#39;#/components/schemas/User&#39;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">responses</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">default</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">description</span>: default response |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">content</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">application/json</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">schema</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type</span>: integer |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">format</span>: int64 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">text/xml</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">schema</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type</span>: integer |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">format</span>: int64 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">components</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">schemas</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">User</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type</span>: object |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">properties</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">id</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type</span>: integer |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">format</span>: int64 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">type</span>: string |
| </span></span></code></pre></div><h5 id="2-集成-swagger-ui">2. 集成 Swagger UI</h5> |
| <p>在 pom.xml 中继续增加 swagger-ui 的依赖,这里使用的是 webjars 的版本,从集成的角度来说更加简洁。webjars 的工作机制可以参阅 webjars 官网 <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;properties&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;swagger.webjar.version&gt;</span>3.20.3<span style="color:#268bd2">&lt;/swagger.webjar.version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/properties&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependencies&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;groupId&gt;</span>org.webjars<span style="color:#268bd2">&lt;/groupId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;artifactId&gt;</span>swagger-ui<span style="color:#268bd2">&lt;/artifactId&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;version&gt;</span>${swagger.webjar.version}<span style="color:#268bd2">&lt;/version&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependency&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;/dependencies&gt;</span> |
| </span></span></code></pre></div><p>在工程的 webapp/WEB-INF 根目录下增加一个 HTML 文件,内容如下。HTML 文件名可以为任何名字,没有硬性要求,如果该文件被命名为 &ldquo;swagger-ui.html&rdquo;,那么你可以通过访问 “http://localhost:8080/swagger-ui.html&rdquo; 来访问 swagger UI。本例为了演示方便起见,将其命名为 &ldquo;index.html&rdquo;,这样当访问 &ldquo;http://localhost:8080&rdquo; 时,就可以很方便的得到 swagger UI 的页面。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#719e07">&lt;!DOCTYPE html&gt;</span> |
| </span></span><span style="display:flex;"><span>&lt;<span style="color:#268bd2">html</span> lang<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;en&#34;</span>&gt; |
| </span></span><span style="display:flex;"><span>&lt;<span style="color:#268bd2">head</span>&gt; |
| </span></span><span style="display:flex;"><span> &lt;<span style="color:#268bd2">meta</span> charset<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;UTF-8&#34;</span>&gt; |
| </span></span><span style="display:flex;"><span> &lt;<span style="color:#268bd2">title</span>&gt;API UI&lt;/<span style="color:#268bd2">title</span>&gt; |
| </span></span><span style="display:flex;"><span> &lt;<span style="color:#268bd2">link</span> rel<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;stylesheet&#34;</span> type<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;text/css&#34;</span> href<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;webjars/swagger-ui/3.20.3/swagger-ui.css&#34;</span> &gt; |
| </span></span><span style="display:flex;"><span> &lt;<span style="color:#268bd2">link</span> rel<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;icon&#34;</span> type<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;image/png&#34;</span> href<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;webjars/swagger-ui/3.20.3/favicon-32x32.png&#34;</span> sizes<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;32x32&#34;</span> /&gt; |
| </span></span><span style="display:flex;"><span> &lt;<span style="color:#268bd2">link</span> rel<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;icon&#34;</span> type<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;image/png&#34;</span> href<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;webjars/swagger-ui/3.20.3/favicon-16x16.png&#34;</span> sizes<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;16x16&#34;</span> /&gt; |
| </span></span><span style="display:flex;"><span> &lt;<span style="color:#268bd2">style</span>&gt; |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">html</span> |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">box-sizing</span>: <span style="color:#cb4b16">border-box</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">overflow</span>: <span style="color:#719e07">-moz-</span>scrollbars-vertical; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">overflow-y</span>: <span style="color:#cb4b16">scroll</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">*,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">*</span>:<span style="color:#268bd2">before</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">*</span>:<span style="color:#268bd2">after</span> |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">box-sizing</span>: <span style="color:#cb4b16">inherit</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">body</span> |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">margin</span>:<span style="color:#2aa198">0</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">background</span>: <span style="color:#2aa198">#fafafa</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> &lt;/<span style="color:#268bd2">style</span>&gt; |
| </span></span><span style="display:flex;"><span>&lt;/<span style="color:#268bd2">head</span>&gt; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>&lt;<span style="color:#268bd2">body</span>&gt; |
| </span></span><span style="display:flex;"><span>&lt;<span style="color:#268bd2">div</span> id<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;swagger-ui&#34;</span>&gt;&lt;/<span style="color:#268bd2">div</span>&gt; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>&lt;<span style="color:#268bd2">script</span> src<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;webjars/swagger-ui/3.20.3/swagger-ui-bundle.js&#34;</span>&gt; &lt;/<span style="color:#268bd2">script</span>&gt; |
| </span></span><span style="display:flex;"><span>&lt;<span style="color:#268bd2">script</span> src<span style="color:#719e07">=</span><span style="color:#2aa198">&#34;webjars/swagger-ui/3.20.3/swagger-ui-standalone-preset.js&#34;</span>&gt; &lt;/<span style="color:#268bd2">script</span>&gt; |
| </span></span><span style="display:flex;"><span>&lt;<span style="color:#268bd2">script</span>&gt; |
| </span></span><span style="display:flex;"><span> <span style="color:#b58900">window</span>.onload <span style="color:#719e07">=</span> <span style="color:#268bd2">function</span> () { |
| </span></span><span style="display:flex;"><span> <span style="color:#b58900">window</span>.ui <span style="color:#719e07">=</span> SwaggerUIBundle({ |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;openapi.json&#34;</span>, |
| </span></span><span style="display:flex;"><span> dom_id<span style="color:#719e07">:</span> <span style="color:#2aa198">&#39;#swagger-ui&#39;</span>, |
| </span></span><span style="display:flex;"><span> deepLinking<span style="color:#719e07">:</span> <span style="color:#cb4b16">true</span>, |
| </span></span><span style="display:flex;"><span> presets<span style="color:#719e07">:</span> [ |
| </span></span><span style="display:flex;"><span> SwaggerUIBundle.presets.apis, |
| </span></span><span style="display:flex;"><span> SwaggerUIStandalonePreset |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> plugins<span style="color:#719e07">:</span> [ |
| </span></span><span style="display:flex;"><span> SwaggerUIBundle.plugins.DownloadUrl |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> layout<span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;StandaloneLayout&#34;</span> |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> }; |
| </span></span><span style="display:flex;"><span>&lt;/<span style="color:#268bd2">script</span>&gt; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>&lt;/<span style="color:#268bd2">body</span>&gt; |
| </span></span><span style="display:flex;"><span>&lt;/<span style="color:#268bd2">html</span>&gt; |
| </span></span></code></pre></div><p>再次重启服务器,并访问 &ldquo;http://localhost:8080&rdquo; 时,将会看到 swagger UI 页面的展示:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/swagger-ui.png" alt="swagger-ui"></p> |
| <p>通过 Swagger UI 可以很方便的浏览当前服务器提供的 REST 服务的文档信息,甚至可以直接调用来做服务测试。以 &lsquo;/api/users/{id}&rsquo; 为例,测试结果如下图所示:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/swagger-ui-execute.png" alt="swagger-ui-execute"></p> |
| <h2 id="总结">总结</h2> |
| <p>本文主要关注了在 Dubbo 中支持 REST 协议的情况。首先探索了 REST 概念的起源,澄清了 REST 是一种适合互联网的软件架构风格,进一步的说明了 REST 风格的架构可以与 HTTP 协议无关,但是 HTTP 协议的确是 REST 风格架构的最常用甚至是最佳的组合和搭档。然后讨论了如何在 Dubbo 中开发 REST HTTP 的几种典型用法,其中包括了通过不同的配置,如传统的 Spring XML,完全通过 annotation 来配置两种典型的用法,本文中没有涉及到的还有纯 API 编程方式,Spring Boot 配置方式也是完全可以的,因为篇幅原因没有提及;还讨论了如何通过不同的 REST server 来暴露 REST HTTP 服务,包括了 embedded tomcat,netty,以及外置的 servlet 容器等几种用法。最后,在外置的 servlet 容器的基础上,进一步的讨论了如何通过 Swagger 暴露 openAPI 以及集成 Swagger UI 的方法。</p> |
| <p>本文没有涉及的内容包含但不限于国际化支持、Dubbo REST 更高阶的注入扩展的用法、以及 Dubbo REST 支持未来的规划。其中 Dubbo REST 扩展的支持可以参考 <a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/extensions">https://github.com/beiwei30/dubbo-rest-samples/tree/master/extensions</a> 中的演示。以后有机会会开专门的篇幅来探讨更高级的 Swagger 的支持、以及对未来的展望。</p> |
| <div class="footnotes" role="doc-endnotes"> |
| <hr> |
| <ol> |
| <li id="fn:1"> |
| <p><a href="http://en.wikipedia.org/wiki/Roy_Fielding">http://en.wikipedia.org/wiki/Roy_Fielding</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> |
| </li> |
| <li id="fn:2"> |
| <p><a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm">http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> |
| </li> |
| <li id="fn:3"> |
| <p><a href="https://martinfowler.com/articles/richardsonMaturityModel.html">https://martinfowler.com/articles/richardsonMaturityModel.html</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> |
| </li> |
| <li id="fn:4"> |
| <p><a href="https://github.com/dangdangdotcom/dubbox">https://github.com/dangdangdotcom/dubbox</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> |
| </li> |
| <li id="fn:5"> |
| <p><a href="https://www.webjars.org/documentation#servlet3">https://www.webjars.org/documentation#servlet3</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> |
| </li> |
| </ol> |
| </div></description></item></channel></rss> |