<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – Apache Dubbo 博客</title><link>https://cn.dubbo.apache.org/zh/blog/</link><description>Recent content in Apache Dubbo 博客 on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://cn.dubbo.apache.org/zh/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: Dubbo 在 Proxyless Mesh 模式下的探索与改进</title><link>https://cn.dubbo.apache.org/zh/blog/2023/02/02/dubbo-%E5%9C%A8-proxyless-mesh-%E6%A8%A1%E5%BC%8F%E4%B8%8B%E7%9A%84%E6%8E%A2%E7%B4%A2%E4%B8%8E%E6%94%B9%E8%BF%9B/</link><pubDate>Thu, 02 Feb 2023 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2023/02/02/dubbo-%E5%9C%A8-proxyless-mesh-%E6%A8%A1%E5%BC%8F%E4%B8%8B%E7%9A%84%E6%8E%A2%E7%B4%A2%E4%B8%8E%E6%94%B9%E8%BF%9B/</guid><description>
&lt;h2 id="一背景">一、背景&lt;/h2>
&lt;p>随着 Docker 和 Kubernetes 的出现，一个庞大的单体应用可以被拆分成多个独立部署的微服务，并被打包运行于对应的容器中。不同应用之间相互通信，以共同完成某一功能模块。微服务架构与容器化部署带来的好处是显而易见的，它降低了服务间的耦合性，利于开发和维护，能更有效地利用计算资源。当然，微服务架构也存在相应的缺点：&lt;/p>
&lt;ul>
&lt;li>强依赖于 SDK，业务模块与治理模块耦合较为严重。 除了相关依赖，往往还需要在业务代码中嵌入SDK代码或配置。&lt;/li>
&lt;li>统一治理难。每次框架升级都需要修改 SDK 版本，并重新进行回归测试，确认功能正常后再对每一台机器重新部署上线。不同服务引用的 SDK 版本不统一、能力参差不齐，增大了统一治理的难度。&lt;/li>
&lt;li>缺少一套统一解决方案。目前市场不存在一整套功能完善、无死角的微服务治理与解决方案。在实际生产环境往往还需要引入多个治理组件来完成像灰度发布、故障注入等功能。&lt;/li>
&lt;/ul>
&lt;p>为解决这些痛点，Service Mesh诞生了。以经典的 Sidecar 模式为例，它通过在业务 Pod 中注入 Sidecar 容器，对代理流量实施治理和管控，将框架的治理能力下层到 Sidecar 容器中，与业务系统解耦，从而轻松实现多语言、多协议的统一流量管控、监控等需求。通过剥离SDK能力并拆解为独立进程，从而解决了强依赖于SDK的问题，从而使开发人员可以更加专注于业务本身，实现了基础框架能力的下沉，如下图所示（源自dubbo官网）：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2023/2/2/1.png" alt="image.png">&lt;/p>
&lt;p>经典的 Sidecar Mesh 部署架构有很多优势，如减少SDK耦合、业务侵入小等，但增加了一层代理，也带来了一些额外的问题，比如:&lt;/p>
&lt;ul>
&lt;li>Sidecar 代理会损耗一部分性能，当网络结构层级比较复杂时尤其明显，对性能要求很高的业务造成了一定的困扰。&lt;/li>
&lt;li>架构更加复杂，对运维人员要求高。&lt;/li>
&lt;li>对部署环境有一定的要求，需要其能支持 Sidecar 代理的运行。&lt;/li>
&lt;/ul>
&lt;p>为解决这些痛点，Proxyless Service Mesh 模式诞生了。传统服务网格通过代理的方式拦截所有的业务网络流量，代理需要感知到控制平面下发的配置资源，从而按照要求控制网络流量的走向。以istio为例，Proxyless 模式是指应用直接与负责控制平面的 istiod 进程通信，istiod 进程通过监听并获取 Kubernetes 的资源，例如 Service、Endpoint 等，并将这些资源统一通过 xDS 协议下发到不同的 RPC 框架，由 RPC 框架进行请求转发，从而实现服务发现和服务治理等能力。
Dubbo 社区是国内最早开始对 Proxyless Service Mesh 模式进行探索的社区，这是由于相比于 Service Mesh，Proxyless 模式落地成本较低，对于中小企业来说是一个较好的选择。Dubbo 在 3.1 版本中通过对 xDS 协议进行解析，新增了对 Proxyless 的支持。xDS 是一类发现服务的总称，应用通过 xDS API 可以动态获取 Listener(监听器)、Route(路由)、Cluster(集群)、Endpoint(集群成员) 以及 Secret(证书)配置。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2023/2/2/2.jpeg" alt="image.png">&lt;/p>
&lt;p>通过 Proxyless 模式，Dubbo 与 Control Plane 直接建立通信，进而实现控制面对流量管控、服务治理、可观测性、安全等的统一管控，从而规避 Sidecar 模式带来的性能损耗与部署架构复杂性。&lt;/p>
&lt;h2 id="二dubbo-xds-推送机制详解">二、Dubbo xDS 推送机制详解&lt;/h2>
&lt;p>从整体上看，istio control plane 和 dubbo 的交互时序图如上。Dubbo 里 xDS 处理的主要逻辑在 PilotExchanger 和各个 DS (LDS、RDS、CDS、EDS) 的对应协议的具体实现里。PilotExchanger 统一负责串联逻辑，主要有三大逻辑：&lt;/p>
&lt;ul>
&lt;li>获取授信证书。&lt;/li>
&lt;li>调用不同 protocol 的 getResource 获取资源。&lt;/li>
&lt;li>调用不同 protocol 得 observeResource 方法监听资源变更。&lt;/li>
&lt;/ul>
&lt;p>例如对于 lds 和 rds，PilotExchanger 会调用 lds 的 getResource 方法与 istio 建立通信连接，发送数据并解析来自 istio 的响应，解析完成后的 resource 资源会作 为rds 调用 getResource 方法的入参，并由 rds 发送数据给 istio。当 lds 发生变更时，则由 lds 的 observeResource 方法去触发自身与 rds 的变更。上述关系对于 rds 和 eds 同样如此。现有交互如下，上述过程对应图里红线的流程：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2023/2/2/3.jpeg" alt="image.png">&lt;/p>
&lt;p>在第一次成功获取资源之后，各个DS会通过定时任务去不断发送请求给 istio，并解析响应结果和保持与 istio 之间的交互，进而实现控制面对流量管控、服务治理、可观测性方面的管控，其流程对应上图蓝线部分。&lt;/p>
&lt;h2 id="三当前dubbo-proxyless实现存在的不足">三、当前Dubbo Proxyless实现存在的不足&lt;/h2>
&lt;p>Dubbo Proxyless 模式经过验证之后，已经证明了其可靠性。现有 Dubbo proxyless 的实现方案存在以下问题：&lt;/p>
&lt;ul>
&lt;li>目前与 istio 交互的逻辑是推送模式。getResource 和 observeResource 是两条不同的 stream 流，每次发送新请求都需要重新建立连接。但我们建立的 stream 流是双向流动的，istio 在监听到资源变化后由主动推送即可，LDS、RDS、EDS 分别只需要维护一条 stream 流。&lt;/li>
&lt;li>Stream 流模式改为建立持久化连接之后，需要设计一个本地的缓存池，去存储已经存在的资源。当 istio 主动推送更新后，需要去刷新缓存池的数据。&lt;/li>
&lt;li>现有observeResource 逻辑是通过定时任务去轮询istio。现在 observeResource 不再需要定时去轮询，只需要将需要监听的资源加入到缓存池，等 istio 自动推送即可，且 istio 推送回来的数据需要按照 app 切分好，实现多点监听，后续 dubbo 支持其他 DS 模式，也可复用相应的逻辑。&lt;/li>
&lt;li>目前由 istio 托管的 dubbo 应用在 istio 掉线后会抛出异常，断线后无法重新连接，只能重新部署应用，增加了运维和管理的复杂度。我们需增加断线重连的功能，等 istio 恢复正常后无需重新部署即可重连。&lt;/li>
&lt;/ul>
&lt;p>改造完成后的交互逻辑：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2023/2/2/4.jpeg" alt="image.png">&lt;/p>
&lt;h2 id="四xds-监听模式实现方案">四、Xds 监听模式实现方案&lt;/h2>
&lt;h3 id="41-资源缓存池">4.1 资源缓存池&lt;/h3>
&lt;p>目前 Dubbo 的资源类型有LDS，RDS，EDS。对于同一个进程，三种资源监听的所有资源都与 istio 对该进程所缓存的资源监听列表一一对应。因此针对这三种资源，我们应该设计分别对应的本地的资源缓存池，dubbo 尝试资源的时候先去缓存池查询，若有结果则直接返回；否则将本地缓存池的资源列表与想要发送的资源聚合后，发送给 istio 让其更新自身的监听列表。缓存池如下，其中 key 代表单个资源，T 为不同 DS 的返回结果：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> resourcesMap &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>有了缓存池我们必须有一个监听缓存池的结构或者容器，在这里我们设计为 Map 的形式，如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Consumer&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span> consumerObserveMap &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中key为想要监听的资源，value 为一个 List, 之所以设计为 List 是为了可以支持重复订阅。 List 存储的 item 为 jdk8 中的 Consumer 类型，它可以用于传递一个函数或者行为，其入参为 Map&amp;lt;String, T&amp;gt;，其 key 对应所要监听的单个资源，便于从缓存池中获取。如上文所述，PilotExchanger 负责串联整个流程，不同 DS 之间的更新关系可以用 Consumer 进行传递。以监听 LDS observeResource 为例, 大致代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 监听
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">observeResource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> resourceNames&lt;span style="color:#719e07">,&lt;/span> Consumer&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> consumer&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> isReConnect&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Observe LDS updated
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>ldsProtocol&lt;span style="color:#719e07">.&lt;/span>observeResource&lt;span style="color:#719e07">(&lt;/span>ldsResourcesName&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">(&lt;/span>newListener&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// LDS数据不一致
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>newListener&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>listenerResult&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//更新LDS数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>listenerResult &lt;span style="color:#719e07">=&lt;/span> newListener&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 触发RDS监听
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isRdsObserve&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createRouteObserve&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">},&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Stream流模式改为建立持久化连接之后，我们也需要把这个 Consumer 的行为存储在本地的缓存池中。Istio 收到来自 dubbo 的推送请求后，刷新自身缓存的资源列表并返回响应。此时 istio 返回的响应内容是聚合后的结果，Dubbo 收到响应后，将响应资源拆分为更小的资源粒度，再推送给对应的 Dubbo应用通知其进行变更。&lt;/p>
&lt;p>踩坑点：&lt;/p>
&lt;ul>
&lt;li>istio推送的数据可能为空字符串，此时缓存池子无需存储，直接跳过即可。否则dubbo会绕过缓冲池，不断向istio发送请求。&lt;/li>
&lt;li>考虑以下场景，dubbo应用同时订阅了两个接口，分别由app1和app2提供。为避免监听之间的相互覆盖，因此向istio发送数据时，需要聚合所有监听的资源名一次性发起。&lt;/li>
&lt;/ul>
&lt;h3 id="42-多点独立监听">4.2 多点独立监听&lt;/h3>
&lt;p>在第一次向istio发送请求时会调用getResource方法先去cache查询，缺失了再聚合数据去istio请求数据，istio再返回相应的结果给dubbo。我们处理istio的响应有两种实现方案：&lt;/p>
&lt;ol>
&lt;li>由用户在getResource方案中new 一个completeFuture，由cache分析是否是需要的数据，若确认是新数据则由该future回调传递结果。&lt;/li>
&lt;li>getResource建立资源的监听器consumerObserveMap，定义一个consumer并把取到的数据同步到原来的线程，cache 收到来自istio的推送后会做两件事：将数据推送所有监听器和将数据发送给该资源的监听器。
以上两种方案都能实现，但最大的区别就是用户调用onNext发送数据给istio的时候需不需要感知getResource 的存在。综上，最终选择方案2进行实现。具体实现逻辑是让dubbo与istio建立连接后，istio会推送自身监听到资源列表给dubbo，dubbo解析响应，并根据监听的不同app切分数据，并刷新本地缓存池的数据，并发送ACK响应给istio，大致流程如下：&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2023/2/2/5.svg" alt="image.png">&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ResponseObserver&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> XXX &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onNext&lt;/span>&lt;span style="color:#719e07">(&lt;/span>DiscoveryResponse value&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//接受来自istio的数据并切分
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> newResult &lt;span style="color:#719e07">=&lt;/span> decodeDiscoveryResponse&lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地缓存池数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> oldResource &lt;span style="color:#719e07">=&lt;/span> resourcesMap&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新缓存池数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> discoveryResponseListener&lt;span style="color:#719e07">(&lt;/span>oldResource&lt;span style="color:#719e07">,&lt;/span> newResult&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resourcesMap &lt;span style="color:#719e07">=&lt;/span> newResult&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// for ACK
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> requestObserver&lt;span style="color:#719e07">.&lt;/span>onNext&lt;span style="color:#719e07">(&lt;/span>buildDiscoveryRequest&lt;span style="color:#719e07">(&lt;/span>Collections&lt;span style="color:#719e07">.&lt;/span>emptySet&lt;span style="color:#719e07">(),&lt;/span> value&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">discoveryResponseListener&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> oldResult&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> newResult&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">....&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//具体实现交由LDS、RDS、EDS自身
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#268bd2">abstract&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">decodeDiscoveryResponse&lt;/span>&lt;span style="color:#719e07">(&lt;/span>DiscoveryResponse response&lt;span style="color:#719e07">){&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//比对新数据和缓存池的资源，并将不同时存在于两种池子的资源取出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Consumer&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> consumerObserveMap&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 本地缓存池不存在则跳过
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//聚合资源
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> dsResultMap &lt;span style="color:#719e07">=&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>stream&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>collect&lt;span style="color:#719e07">(&lt;/span>Collectors&lt;span style="color:#719e07">.&lt;/span>toMap&lt;span style="color:#719e07">(&lt;/span>k &lt;span style="color:#719e07">-&amp;gt;&lt;/span> k&lt;span style="color:#719e07">,&lt;/span> v &lt;span style="color:#719e07">-&amp;gt;&lt;/span> newResult&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>v&lt;span style="color:#719e07">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新缓存池数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">().&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>o &lt;span style="color:#719e07">-&amp;gt;&lt;/span> o&lt;span style="color:#719e07">.&lt;/span>accept&lt;span style="color:#719e07">(&lt;/span>dsResultMap&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>踩坑点：&lt;/p>
&lt;ul>
&lt;li>原本多个stream流的情况下，会用递增的requestId来复用stream流，改成持久化连接之后，一种resource会有多个requestid，可能会相互覆盖，因此必须去掉这个机制。&lt;/li>
&lt;li>初始实现方案并没有对资源进行切分，而是一把梭，考虑到后续对其他DS的支持，对istio返回的数据进行切分，也导致consumerObserveMap有点奇形怪状。&lt;/li>
&lt;li>三种DS在发送数据时可以共享同一channel，但监听所用到的必须是同一channel，否则数据变更时istio不会进行推送。&lt;/li>
&lt;li>建立双向stream流之后，初始方案future为全局共享。但可能有这样的场景：相同的ds两次相邻时间的onNext事件，记为A事件和B事件，可能是A事件先发送，B随后；但可能是B事件的结果先返回，不确定istio推送的时间，因此future必须是局部变量而不是全局共享。&lt;/li>
&lt;/ul>
&lt;h3 id="43-采用读写锁避免并发冲突">4.3 采用读写锁避免并发冲突&lt;/h3>
&lt;p>监听器consumerObserveMap和缓存池resourcesMap均可能产生并发冲突。对于resourcemap，由于put操作都集中在getResource方法，因此可以采用悲观锁就能锁住相应的资源，避免资源的并发监听。对于consumerObserveMap，同时存在put、remove和遍历操作，从时序上，采用读写锁可规避冲突，对于遍历操作加读锁，对于put和remove操作加写锁，即可避免并发冲突。综上，resourcesMap加悲观锁即可，consumerObserveMap涉及的操作场景如下：&lt;/p>
&lt;ul>
&lt;li>远程请求istio时候会往consumerObserveMap 新增数据，加写锁。&lt;/li>
&lt;li>CompleteFuture跨线程返回数据后，去掉监听future，加写锁。&lt;/li>
&lt;li>监听缓存池时会往consumerObserveMap新增监听，加写锁。&lt;/li>
&lt;li>断线重连时会往consumerObserveMap新增监听，加写锁。&lt;/li>
&lt;li>解析istio返回的数据，遍历缓存池并刷新数据，加读锁。&lt;/li>
&lt;/ul>
&lt;p>踩坑点：&lt;/p>
&lt;ul>
&lt;li>由于dubbo和istio建立的是是双向stream流，相同的ds两次相邻时间的onNext事件，记为A事件和B事件，可能是A事件先发送，B随后；但可能是B事件的结果先返回，不确定istio推送的时间。因此需要加锁。&lt;/li>
&lt;/ul>
&lt;h3 id="44-断线重连">4.4 断线重连&lt;/h3>
&lt;p>断线重连只需要用定时任务去定时与istio交互，尝试获取授信证书，证书获取成功即可视为istio成功重新上线，dubbo会聚合本地的资源去istio请求数据，并解析响应和刷新本地缓存池数据，最后再关闭定时任务。
踩坑点：&lt;/p>
&lt;ul>
&lt;li>采用全局共享的定时任务池，不能进行关闭，否则会影响其他业务。&lt;/li>
&lt;li>&lt;/li>
&lt;/ul>
&lt;h2 id="五感想与总结">五、感想与总结&lt;/h2>
&lt;p>在这次功能的改造中，笔者着实掉了一波头发，怎么找bug也找不到的情形不在少数。除了上述提到的坑点之外，其他的坑点包括但不局限于：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>dubbo在某一次迭代里更改了获取k8s证书的方式，授权失败。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>原本的功能没问题，merge了下master代码，grpc版本与envoy版本不兼容，各种报错，最后靠降低版本成功解决。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>原本的功能没问题，merge了下master代码，最新分支代码里metadataservice发成了triple，然而在Proxyless模式下只支持dubbo协议，debug了三四天，最后发现需要增加配置。&lt;/p>
&lt;p>&amp;hellip;&amp;hellip;
但不得不承认，Proxyless Service Mesh确实有它自身的优势和广阔的市场前景。自dubbo3.1.0 release版本之后，dubbo已经实现了Proxyless Service Mesh能力，未来dubbo社区将深度联动业务，解决更多实际生产环境中的痛点，更好地完善service mesh能力。&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Blog: Dubbo 3.1.5、3.2.0-beta.4 正式发布</title><link>https://cn.dubbo.apache.org/zh/blog/2023/01/30/dubbo-3.1.53.2.0-beta.4-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Mon, 30 Jan 2023 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2023/01/30/dubbo-3.1.53.2.0-beta.4-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;p>Dubbo 3.1.5 版本是目前 Dubbo 3 的最新稳定版本，我们建议所有的用户都升级到最新的稳定版本。Dubbo 3.2.0-beta.4 版本是目前 Dubbo 3 的最新特性版本，包括了如 Spring Boot 3、JDK 17、服务粒度的线程池隔离等新特性的支持，欢迎大家尝鲜使用。&lt;/p>
&lt;h1 id="dubbo-315">Dubbo 3.1.5&lt;/h1>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/release/3-1-5.png" alt="image.png">&lt;/p>
&lt;h3 id="新特性">新特性&lt;/h3>
&lt;ul>
&lt;li>Dubbo QoS 支持记录请求的记录，便于进行审计&lt;/li>
&lt;li>支持在服务映射失败以后定时进行重试，降低由于元数据中心抖动带来的影响&lt;/li>
&lt;li>支持在初始化 Nacos Client 的时候进行健康检查，如果失败则在一定次数限制下进行重试，降低由于 Nacos 性能问题带来的稳定性影响&lt;/li>
&lt;li>支持序列化类检查机制，默认开启日志告警模式&lt;/li>
&lt;/ul>
&lt;h3 id="bugfix">Bugfix&lt;/h3>
&lt;ul>
&lt;li>修复资源加载器的日志级别&lt;/li>
&lt;li>修复 Dubbo 配置类对 Scope Model 进行懒加载，避免触发非预期的默认模块初始化&lt;/li>
&lt;li>修复 ReferenceConfig 中获取 ClassLoader 的逻辑&lt;/li>
&lt;li>修复 Metadata Service 在获取订阅服务列表时出现 NPE 的问题&lt;/li>
&lt;li>修复对接 Spring Cloud Rest 模式的时候 Metadata 配置覆盖的问题&lt;/li>
&lt;li>修复 Spring 懒加载时可能出现死锁的问题&lt;/li>
&lt;li>修复端口重复的无效日志&lt;/li>
&lt;li>修复 Active Limit Filter 不生效的问题&lt;/li>
&lt;li>修复服务映射时 Nacos CAS 检查写入无效的问题&lt;/li>
&lt;li>修复 Zookeeper 注册中心对接的应用级服务发现在服务发布的时候出现单节点服务找不到的问题&lt;/li>
&lt;li>修复服务映射在冲突以后未等待导致的冲突率高的问题&lt;/li>
&lt;li>修复应用级服务发现下节点更新失败的问题&lt;/li>
&lt;li>修复应用级配置覆盖不生效的问题&lt;/li>
&lt;li>修复在应用级地址刷新之后原 Revision 的元数据无法获取的问题&lt;/li>
&lt;li>修复 Zookeeper 注册中心在应用级服务发现下退订阅后无法重订阅的问题&lt;/li>
&lt;li>兼容 Nacos 在频繁刷新时最终一致性错误的问题&lt;/li>
&lt;li>关闭 Nacos 本地缓存获取的开关&lt;/li>
&lt;li>修复 Triple 传递大写 Attachment 无效的问题&lt;/li>
&lt;li>修复 Triple 处理特定类反序列化错误的问题&lt;/li>
&lt;li>修复 Protobuf 依赖不存在时抛出非预期异常的问题&lt;/li>
&lt;li>修复 CountDown 功能无效的问题&lt;/li>
&lt;li>修复 Triple 在反序列化时类加载器未切换的问题&lt;/li>
&lt;/ul>
&lt;h3 id="faq">FAQ&lt;/h3>
&lt;p>本次发布中有 5 个提交涉及异常日志 FAQ 的完善。关于错误码机制请参考官网错误码机制介绍一文。（https://cn.dubbo.apache.org/zh/docs3-v2/java-sdk/faq/intro/）&lt;/p>
&lt;h3 id="代码优化">代码优化&lt;/h3>
&lt;p>本次发布中有 4 个提交涉及代码质量的优化。&lt;/p>
&lt;h3 id="依赖升级">依赖升级&lt;/h3>
&lt;ul>
&lt;li>升级 protobuf-java: 3.16.3 -&amp;gt; 3.18.3&lt;/li>
&lt;li>升级 fastjson2: 2.0.21 -&amp;gt; 2.0.23&lt;/li>
&lt;/ul>
&lt;h3 id="贡献者">贡献者&lt;/h3>
&lt;p>Dubbo 感谢以下贡献者对本次发布的贡献：@win120a, @wuwen5, @AlbumenJ, @CrazyHZM, @EarthChen, @xieshouyu, @wxbty&lt;/p>
&lt;h3 id="新贡献者">新贡献者&lt;/h3>
&lt;ul>
&lt;li>@xieshouyu 在 PR #11177 提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h1 id="dubbo-320-beta4">Dubbo 3.2.0-beta.4&lt;/h1>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/release/3-2-0-beta-4.png" alt="image.png">&lt;/p>
&lt;p>&lt;strong>注：Dubbo 3.2.0-beta.4 的代码基础和 Dubbo 3.1.5 完全一致，因此在 Dubbo 3.1.5 中包括的所有修改内容，在 Dubbo 3.2.0-beta.4 中也同样存在，后续说明中对于重复的内容讲不再赘述。&lt;/strong>&lt;/p>
&lt;h3 id="新特性-1">新特性&lt;/h3>
&lt;ul>
&lt;li>支持 bytebuddy 作为动态代理的生成平台&lt;/li>
&lt;li>支持采集超时指标进行上报&lt;/li>
&lt;li>完善 Metrics 上报应用名&lt;/li>
&lt;li>完善 Metrics 上报指标的前缀&lt;/li>
&lt;li>支持上报请求失败的指标&lt;/li>
&lt;li>完善 xDS 的监听逻辑&lt;/li>
&lt;li>完善 xDS 对接路由规则的能力&lt;/li>
&lt;/ul>
&lt;h3 id="bugfix-1">Bugfix&lt;/h3>
&lt;ul>
&lt;li>修复 Token 请求在 Rest 协议下不生效的问题&lt;/li>
&lt;li>修复 GraalVM Native Image 的配置&lt;/li>
&lt;li>修复配置类无法进行序列化的问题&lt;/li>
&lt;li>修复 IPv6 地址段检查无效的问题&lt;/li>
&lt;li>修复路由链切换时地址可能存在异常的情况&lt;/li>
&lt;li>修复缓存文件锁被异常删除的问题&lt;/li>
&lt;li>修复 Deployer Listener 并发修改的问题&lt;/li>
&lt;/ul>
&lt;h3 id="性能优化">性能优化&lt;/h3>
&lt;ul>
&lt;li>优化 ConcurrentHashMap 的使用方式，避免在 JDK 1.8 下出现性能衰减&lt;/li>
&lt;/ul>
&lt;h3 id="代码优化-1">代码优化&lt;/h3>
&lt;p>本次发布中有 8 个提交涉及代码质量的优化。&lt;/p>
&lt;h3 id="依赖升级-1">依赖升级&lt;/h3>
&lt;ul>
&lt;li>升级 protobuf-java: 3.18.3 -&amp;gt; 3.19.6&lt;/li>
&lt;li>升级 cglib-nodep: 2.2 -&amp;gt; 2.2.2&lt;/li>
&lt;li>升级 byte-buddy: 1.12.19 -&amp;gt; 1.12.22&lt;/li>
&lt;li>升级 bouncycastle-bcprov_version: 1.69 -&amp;gt; 1.70&lt;/li>
&lt;li>升级 javax.ws.rs-api: 2.0 -&amp;gt; 2.1.1&lt;/li>
&lt;li>升级 curator_version: 4.2.0 -&amp;gt; 4.3.0&lt;/li>
&lt;li>升级 maven-plugin-plugin: 3.6.0 -&amp;gt; 3.7.1&lt;/li>
&lt;li>升级 javax.el: 3.0.1-b08 -&amp;gt; 3.0.1-b12&lt;/li>
&lt;li>升级 slf4j-api: 1.7.25 -&amp;gt; 1.7.36&lt;/li>
&lt;li>升级 spring-boot-dependencies: 2.3.1.RELEASE -&amp;gt; 2.7.7&lt;/li>
&lt;li>升级 maven-enforcer-plugin: 3.0.0-M3 -&amp;gt; 3.1.0&lt;/li>
&lt;li>升级 javassist: 3.28.0-GA -&amp;gt; 3.29.2-GA&lt;/li>
&lt;li>升级 spring-boot-maven-plugin: 2.1.4.RELEASE -&amp;gt; 2.7.7&lt;/li>
&lt;li>升级 javax.el-api: 2.2.4 -&amp;gt; 2.2.5&lt;/li>
&lt;li>升级 eureka.version: 1.9.12 -&amp;gt; 1.10.18&lt;/li>
&lt;li>升级 jetty-maven-plugin: 9.4.38.v20210224 -&amp;gt; 9.4.50.v20221201&lt;/li>
&lt;li>升级 jetty_version: 9.4.43.v20210629 -&amp;gt; 9.4.50.v20221201&lt;/li>
&lt;li>升级 resteasy_version: 3.0.20.Final -&amp;gt; 3.15.3.Final&lt;/li>
&lt;/ul>
&lt;h3 id="贡献者-1">贡献者&lt;/h3>
&lt;p>Dubbo 感谢以下贡献者对本次发布的贡献：@aamingaa, @AlbumenJ, @CrazyHZM, @fomeiherz, @HHoflittlefish777, @icodening, @jojocodeX, @LXPWing, @MentosL, @mxsm, @schneiderlin, @sconvent, @ShenFeng312, @songxiaosheng, @TigerTurbo, @yuluo-yx, @zeusbee&lt;/p>
&lt;h3 id="新贡献者-1">新贡献者&lt;/h3>
&lt;ul>
&lt;li>@LXPWing 在 PR #11258 提交了第一个贡献&lt;/li>
&lt;li>@HHoflittlefish777 在 PR #11266 提交了第一个贡献&lt;/li>
&lt;li>@sconvent 在 PR #11260 提交了第一个贡献&lt;/li>
&lt;li>@yuluo-yx 在 PR #11291 提交了第一个贡献&lt;/li>
&lt;li>@fomeiherz 在 PR #11295 提交了第一个贡献&lt;/li>
&lt;li>@schneiderlin 在 PR #11324 提交了第一个贡献&lt;/li>
&lt;li>@mxsm 在 PR #11326 提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h1 id="未来版本规划">未来版本规划&lt;/h1>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/release/release-roadmap.png" alt="image.png">&lt;/p>
&lt;p>Dubbo 版本的发布规划以及在《&lt;a href="https://mp.weixin.qq.com/s?__biz=MzIwODYwNTA4MA==&amp;amp;mid=2247484424&amp;amp;idx=1&amp;amp;sn=2f5ff4846f7dafad325f78fd8cf4d1fc&amp;amp;chksm=9701deffa07657e9a46eb97bb859770b4856599566b992724013a848a730f394702938e72404&amp;amp;token=1547029975&amp;amp;lang=zh_CN#rd">聚焦稳定性，Dubbo 发版规划公布&lt;/a>》一文中正式发布，欢迎查看。&lt;/p></description></item><item><title>Blog: Dubbo 3 之 Triple 流控反压原理解析</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;p>Triple 是 Dubbo 3 提出的基于 HTTP2 的开放协议，
旨在解决 Dubbo 2 私有协议带来的互通性问题。
Triple 基于 HTTP/2 定制自己的流控，支持通过特定的异常通知客户端业务层服务端负载高情况，
保护了服务端被大流量击垮，提高系统高可用能力。&lt;/p>
&lt;h2 id="一流控反压现状">一、流控反压现状&lt;/h2>
&lt;p>客户端和服务器端在接收数据的时候有一个缓冲区来临时存储数据，
但是缓冲区的大小是有限制的，所以有可能会出现缓冲区溢出的情况，
HTTP 通过流控保护数据溢出丢失风险。&lt;/p>
&lt;h3 id="1http1-流控">1、HTTP/1 流控&lt;/h3>
&lt;p>在 HTTP/1.1 中，流量的控制依赖的是底层TCP协议，在客户端和服务器端建立连接的时候，
会使用系统默认的设置来建立缓冲区。在数据进行通信的时候，会告诉对方它的接收窗口的大小，
这个接收窗口就是缓冲区中剩余的可用空间。如果接收窗口大小为零，则说明接收方缓冲区已满，
则发送方将不再发送数据，直到客户端清除其内部缓冲区，然后请求恢复数据传输。&lt;/p>
&lt;h3 id="2http2-流控">2、HTTP/2 流控&lt;/h3>
&lt;p>HTTP/2 使用了多路复用机制，一个TCP连接可以有多个 HTTP/2 连接，
故在 HTTP/2 中，有更加精细的流控制机制，允许服务端实现自己数据流和连接级的流控制。
服务端与客户端第一次连接时，会通过发送 &lt;code>HTTP/2 SettingsFrame&lt;/code>设置初始化的流控窗口大小，
用于 &lt;code>Stream&lt;/code> 级别流控，默认为 65,535 字节。
定好流控窗口后，每次客户端发送数据就会减少流控窗口的大小，
服务端收到数据后会发送窗口更新包（&lt;code>WINDOW_UPDATE frame&lt;/code>）通知客户端更新窗口。
客户端收到窗口更新包后就会增加对应值的流控窗口，从而达到动态控制的目的。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/28/triple/1.png" alt="1.png">&lt;/p>
&lt;h2 id="二triple流控反压">二、Triple流控反压&lt;/h2>
&lt;p>Netty 基于 HTTP/2 实现了基础的流控，当服务端负载过高，客户端发送窗口为 0 时，
新增请求就无法被发送出去，会在缓存到客户端待发送请求队列中，缓存数据过大，
就会造成客户端内存溢出，影响业务程序。&lt;/p>
&lt;p>Triple 基于 Netty 实现了 HTTP/2 协议，通过 &lt;code>HTTP/2 FlowController&lt;/code>接口统一封装，
在实现分为进站（inbound）和出站（outbound）两个维度的实现。
Triple 在 inbound 流量上使用了 Netty 的默认流控实现，
在 outbound 上实现了自己流控，基于服务端负载，
将服务端流量压力透传到客户端业务层，实现客户端的业务反压，暂停业务继续发送请求，
保护服务端不被大流量击垮。&lt;/p>
&lt;h3 id="1连接初始化">1、连接初始化&lt;/h3>
&lt;p>Triple在初次建立连接时，通过 &lt;code>TripleHttpProtocol&lt;/code> 初始化 HTTP/2 配置，
默认流控窗口 &lt;code>DEFAULT_WINDOW_INIT_SIZE = MIB_8&lt;/code>，
并在服务端和客户端加入自己的 outbound 流控接口。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/28/triple/2.png" alt="2.png">&lt;/p>
&lt;h3 id="2inbound流控">2、Inbound流控&lt;/h3>
&lt;p>Inbound 流量会通过 &lt;code>DefaultHttpLocalFlowController&lt;/code> 的 &lt;code>consumeBytes&lt;/code> 方法实现流控窗口更新与发送。&lt;/p>
&lt;h4 id="1-入口传入http-流与更新数据大小">1) 入口传入HTTP 流与更新数据大小&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/28/triple/3.png" alt="3.png">&lt;/p>
&lt;h4 id="2-找到对应连接实现数据消费">2) 找到对应连接实现数据消费&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/28/triple/4.png" alt="4.png">&lt;/p>
&lt;h4 id="3-更新流控窗口">3) 更新流控窗口&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/28/triple/5.png" alt="5.png">&lt;/p>
&lt;h4 id="4-发送流控更新数据包window_update">4) 发送流控更新数据包（window_update）&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/28/triple/6.png" alt="6.png">&lt;/p>
&lt;h3 id="3outbound流控">3、Outbound流控&lt;/h3>
&lt;p>Outbound 通过 Triple 自己的流控实现 &lt;code>TriHttpRemoteFlowController&lt;/code>，
将服务端压力反馈到业务层，保护服务端被大流量击垮。&lt;/p>
&lt;h4 id="1-发送数据时判断是否还有窗口">1) 发送数据时判断是否还有窗口&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/28/triple/7.png" alt="7.png">&lt;/p>
&lt;h4 id="2-窗口为0时抛出特定异常">2) 窗口为0时抛出特定异常&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/28/triple/8.png" alt="8.png">&lt;/p>
&lt;h4 id="3-反馈客户端流控异常">3) 反馈客户端流控异常&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/28/triple/9.png" alt="9.png">&lt;/p>
&lt;h3 id="4总结">4、总结&lt;/h3>
&lt;p>Triple 通过将底层客户端发送窗口为 0 场景封装为特定流控异常，
透传至客户端上层业务，阻止客户端业务继续数据发送，
有效的保护了服务端被大流量击垮和客户端的内存溢出的问题。&lt;/p>
&lt;h2 id="三未来展望">三、未来展望&lt;/h2>
&lt;p>目前 Triple 已经基本实现了流控反压能力，未来我们将深度联动业务，
基于业务负载自适应调整反压流控，
一是在 inbound 上将流控窗口包发送时机调整到服务端业务处理完成后，
二是在 outbound 流量上关联客户端业务层，动态调整客户端发送速率。
从而实现基于服务端业务负载动态反压流控机制。&lt;/p></description></item><item><title>Blog: Dubbo 3.1.4、3.2.0-beta.3 正式发布</title><link>https://cn.dubbo.apache.org/zh/blog/2022/12/22/dubbo-3.1.43.2.0-beta.3-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Thu, 22 Dec 2022 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2022/12/22/dubbo-3.1.43.2.0-beta.3-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;p>Dubbo 3.1.4 版本是目前 Dubbo 3 的最新稳定版本，我们建议所有的用户都升级到最新的稳定版本。Dubbo 3.2.0-beta.3 版本是目前 Dubbo 3 的最新特性版本，包括了如 Spring Boot 3、JDK 17、服务粒度的线程池隔离等新特性的支持，欢迎大家尝鲜使用。&lt;/p>
&lt;h1 id="dubbo-314">Dubbo 3.1.4&lt;/h1>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/release/3-1-4.png" alt="image.png">&lt;/p>
&lt;h3 id="新特性">新特性&lt;/h3>
&lt;ul>
&lt;li>Dubbo QoS 支持指定白名单 IP 列表访问所有命令&lt;/li>
&lt;/ul>
&lt;h3 id="bugfix">Bugfix&lt;/h3>
&lt;ul>
&lt;li>修复在泛化调用时 Dubbo Metrics 采集方法名错误&lt;/li>
&lt;li>修复使用 Zookeeper 作为元数据中心时，上报接口映射可能存在相互覆盖的问题&lt;/li>
&lt;li>修复 timeout countdown 在 Triple 协议的支持&lt;/li>
&lt;li>修复 timeout countdown 存在透传的问题&lt;/li>
&lt;li>修复多注册中心请求时，由于没有可用的地址导致的 NPE 问题&lt;/li>
&lt;li>修复 Mesh 模式下 Triple 获取 remoteApplicationName 为空的问题&lt;/li>
&lt;li>修复 GraalVM Native Image 的支持&lt;/li>
&lt;li>修复端口复用时无 SSL 连接导致的 NPE 异常&lt;/li>
&lt;li>完善 JDK 编译器报错日志的输出格式&lt;/li>
&lt;li>修复 MetadataReportConfig 部分配置时应用无法启动的问题&lt;/li>
&lt;li>修复 dubbo.reference 作为默认参数在 3.x 版本中不生效的问题&lt;/li>
&lt;li>完善 Zookeeper 连接失败的日志&lt;/li>
&lt;li>修复 ReferenceConfig 中配置的 ClassLoader 可能被覆盖的问题&lt;/li>
&lt;li>修复部分属性在应用级服务发现时被缓存在实例级的地址中导致方法级配置失效的问题&lt;/li>
&lt;li>修复 Triple 协议 onError 异常的问题&lt;/li>
&lt;/ul>
&lt;h3 id="faq">FAQ&lt;/h3>
&lt;p>本次发布中有 3 个提交涉及异常日志 FAQ 的完善。关于错误码机制请参考官网错误码机制介绍一文。（https://cn.dubbo.apache.org/zh/docs3-v2/java-sdk/faq/intro/）&lt;/p>
&lt;h3 id="代码优化">代码优化&lt;/h3>
&lt;p>本次发布中有 11 个提交涉及代码质量的优化。&lt;/p>
&lt;h3 id="依赖升级">依赖升级&lt;/h3>
&lt;ul>
&lt;li>升级 fastjson2: 2.0.14 -&amp;gt; 2.0.21&lt;/li>
&lt;li>升级 resteasy-jaxrs: 3.0.19.Final -&amp;gt; 3.0.20.Final&lt;/li>
&lt;/ul>
&lt;h3 id="贡献者">贡献者&lt;/h3>
&lt;p>Dubbo 感谢以下贡献者对本次发布的贡献：@cnjxzhao, @CrazyHZM, @EarthChen, @gold-fisher, @IncrementalRefinement, @Koooooo-7, @ShenFeng312, @tonycody, @twz007, @win120a, @wlazjr&lt;/p>
&lt;h3 id="新贡献者">新贡献者&lt;/h3>
&lt;ul>
&lt;li>@twz007 在 PR #11012 提交了第一个贡献&lt;/li>
&lt;li>@IncrementalRefinement 在 PR #11046 提交了第一个贡献&lt;/li>
&lt;li>@gold-fisher 在 PR #11058 提交了第一个贡献&lt;/li>
&lt;li>@wlazjr 在 PR #11084 提交了第一个贡献&lt;/li>
&lt;li>@ShenFeng312 在 PR #11102 提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h1 id="dubbo-320-beta3">Dubbo 3.2.0-beta.3&lt;/h1>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/release/3-2-0-beta-3.png" alt="image.png">&lt;/p>
&lt;p>&lt;strong>注：Dubbo 3.2.0-beta.3 的代码基础和 Dubbo 3.1.4 完全一致，因此在 Dubbo 3.1.4 中包括的所有修改内容，在 Dubbo 3.2.0-beta.3 中也同样存在，后续说明中对于重复的内容讲不再赘述。&lt;/strong>&lt;/p>
&lt;h3 id="新特性-1">新特性&lt;/h3>
&lt;ul>
&lt;li>支持使用 jackson 作为Dubbo 内部的 JSON 序列化方式&lt;/li>
&lt;li>优化 Dubbo Logger 的选择逻辑，在存在多种日志框架的情况下会尝试读取其配置，选择一个有效的日志框架，解决在 SpringBoot 等场景下 Dubbo 日志不输出的问题。&lt;/li>
&lt;li>Triple 协议支持客户端流控&lt;/li>
&lt;li>支持发布 Dubbo Metrics 数据到 Spring Boot 的 Endpoints 上&lt;/li>
&lt;li>支持可选关闭线程池满时的线程 Dump 以及支持配置 Dump 的结果输出路径&lt;/li>
&lt;li>xDS 实现支持路由规则的解析&lt;/li>
&lt;li>支持 Dubbo QoS 命令安全性分级，默认对外暴露存活检测的端口，支持 Kubernetes 的原生接入&lt;/li>
&lt;li>支持基于 P2C 的自适应负载均衡&lt;/li>
&lt;/ul>
&lt;h3 id="bugfix-1">Bugfix&lt;/h3>
&lt;ul>
&lt;li>修复默认 Metadata 缓存未开启的问题&lt;/li>
&lt;li>修复 Metrics 指标资源路径错误的问题&lt;/li>
&lt;li>完善线程池隔离的配置，默认采用共享线程池，避免创建过多的线程&lt;/li>
&lt;li>完善 prefer-serialization 的选择逻辑，提供向前兼容的能力&lt;/li>
&lt;li>修复 Triple 协议传输时未携带版本号导致版本调用错误的问题&lt;/li>
&lt;li>完善 GraalVM Native Image 的支持&lt;/li>
&lt;/ul>
&lt;h3 id="性能优化">性能优化&lt;/h3>
&lt;ul>
&lt;li>优化在连接数高时由于获取 channels 数量导致的资源占用问题&lt;/li>
&lt;/ul>
&lt;h3 id="代码优化-1">代码优化&lt;/h3>
&lt;p>本次发布中有 5 个提交涉及代码质量的优化。&lt;/p>
&lt;h3 id="贡献者-1">贡献者&lt;/h3>
&lt;p>Dubbo 感谢以下贡献者对本次发布的贡献：@AlbumenJ, @asa3311, @conghuhu, @CrazyHZM, @gitchenjh, @haoyann, @JavaHello, @Koooooo-7, @nannanfighting, @ningboliu, @shanuo0312, @songxiaosheng, @tonycody, @XDanwar&lt;/p>
&lt;h3 id="新贡献者-1">新贡献者&lt;/h3>
&lt;ul>
&lt;li>@JavaHello 在 PR #10970 提交了第一个贡献&lt;/li>
&lt;li>@songxiaosheng 在 PR #10997提交了第一个贡献&lt;/li>
&lt;li>@Koooooo-7 在 PR #11051 提交了第一个贡献&lt;/li>
&lt;li>@ningboliu 在 PR #10745 提交了第一个贡献&lt;/li>
&lt;li>@XDanwar 在 PR #11063 提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h1 id="未来版本规划">未来版本规划&lt;/h1>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/release/release-roadmap.png" alt="image.png">&lt;/p>
&lt;p>Dubbo 版本的发布规划以及在《&lt;a href="https://mp.weixin.qq.com/s?__biz=MzIwODYwNTA4MA==&amp;amp;mid=2247484424&amp;amp;idx=1&amp;amp;sn=2f5ff4846f7dafad325f78fd8cf4d1fc&amp;amp;chksm=9701deffa07657e9a46eb97bb859770b4856599566b992724013a848a730f394702938e72404&amp;amp;token=1547029975&amp;amp;lang=zh_CN#rd">聚焦稳定性，Dubbo 发版规划公布&lt;/a>》一文中正式发布，欢迎查看。&lt;/p></description></item><item><title>Blog: Triple 协议支持 Java 异常回传的设计与实现</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>在一些业务场景, 往往需要自定义异常来满足特定的业务, 主流用法是在catch里抛出异常, 例如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">deal&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span>&lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//doSomething
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span>&lt;span style="color:#719e07">(&lt;/span>IGreeterException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> e&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>或者通过ExceptionBuilder，把相关的异常对象返回给consumer：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>provider&lt;span style="color:#719e07">.&lt;/span>send&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ExceptionBuilders&lt;span style="color:#719e07">.&lt;/span>IGreeterExceptionBuilder&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setDescription&lt;span style="color:#719e07">(&lt;/span>&amp;#39;异常描述信息&amp;#39;&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在抛出异常后, 通过捕获和instanceof来判断特定的异常, 然后做相应的业务处理，例如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greeterProxy&lt;span style="color:#719e07">.&lt;/span>echo&lt;span style="color:#719e07">(&lt;/span>REQUEST_MSG&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>IGreeterException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//做相应的处理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 Dubbo 2.x 版本，可以通过上述方法来捕获 Provider 端的异常。
而随着云原生时代的到来， Dubbo 也开启了 3.0 的里程碑。&lt;/p>
&lt;p>Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生，
在 3.0 的许多特性中，很重要的一个改动就是支持&lt;strong>新的一代Rpc协议Triple&lt;/strong>。&lt;/p>
&lt;p>Triple 协议基于 HTTP 2.0 进行构建，对网关的穿透性强，&lt;strong>兼容 gRPC&lt;/strong>，
提供 Request Response、Request Streaming、Response Streaming、
Bi-directional Streaming 等通信模型；
从 Triple 协议开始，Dubbo 还支持基于 IDL 的服务定义。&lt;/p>
&lt;p>采用 Triple 协议的用户可以在 provider 端生成用户定义的异常信息，
记录异常产生的堆栈，triple 协议可保证将用户在客户端获取到异常的message。&lt;/p>
&lt;p>Triple 的回传异常会在 &lt;code>AbstractInvoker&lt;/code> 的 &lt;code>waitForResultIfSync&lt;/code>
中把异常信息堆栈统一封装成 &lt;code>RpcException&lt;/code>，
所有来自 Provider 端的异常都会被封装成 &lt;code>RpcException&lt;/code> 类型并抛出，
这会导致用户无法根据特定的异常类型捕获来自 Provider 的异常，
只能通过捕获 RpcException 异常来返回信息，
且 Provider 携带的异常 message 也无法回传，只能获取打印的堆栈信息：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greeterProxy&lt;span style="color:#719e07">.&lt;/span>echo&lt;span style="color:#719e07">(&lt;/span>REQUEST_MSG&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RpcException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>自定义异常信息在社区中的呼声也比较高，
因此本次改动将支持自定义异常的功能, 使得服务端能抛出自定义异常后被客户端捕获到。&lt;/p>
&lt;h2 id="dubbo异常处理简介">Dubbo异常处理简介&lt;/h2>
&lt;p>我们从Consumer的角度看一下一次Triple协议 Unary请求的大致流程：&lt;/p>
&lt;p>Dubbo Consumer 从 Spring 容器中获取 bean 时获取到的是一个代理接口，
在调用接口的方法时会通过代理类远程调用接口并返回结果。&lt;/p>
&lt;p>Dubbo提供的代理工厂类是 &lt;code>ProxyFactory&lt;/code>，通过 SPI 机制默认实现的是 &lt;code>JavassistProxyFactory&lt;/code>，
&lt;code>JavassistProxyFactory&lt;/code> 创建了一个继承自 &lt;code>AbstractProxyInvoker&lt;/code> 类的匿名对象，
并重写了抽象方法 &lt;code>doInvoke&lt;/code>。
重写后的 &lt;code>doInvoke&lt;/code> 只是将调用请求转发给了 &lt;code>Wrapper&lt;/code> 类的 &lt;code>invokeMethod&lt;/code> 方法，
并生成 &lt;code>invokeMethod&lt;/code> 方法代码和其他一些方法代码。&lt;/p>
&lt;p>代码生成完毕后，通过 &lt;code>Javassist&lt;/code> 生成 &lt;code>Class&lt;/code> 对象，
最后再通过反射创建 &lt;code>Wrapper&lt;/code> 实例，随后通过 &lt;code>InvokerInvocationHandler&lt;/code> -&amp;gt; &lt;code>InvocationUtil&lt;/code> -&amp;gt; &lt;code>AbstractInvoker&lt;/code> -&amp;gt; 具体实现类发送请求到Provider端。&lt;/p>
&lt;p>Provider 进行相应的业务处理后返回相应的结果给 Consumer 端，来自 Provider 端的结果会被封装成 &lt;code>AsyncResult&lt;/code> ，在 &lt;code>AbstractInvoker&lt;/code> 的具体实现类里，
接受到来自 Provider 的响应之后会调用 &lt;code>appResponse&lt;/code> 到 &lt;code>recreate&lt;/code> 方法，若 &lt;code>appResponse&lt;/code> 里包含异常，
则会抛出给用户，大体流程如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/19/triple/1.jpeg" alt="1.jpeg">&lt;/p>
&lt;p>上述的异常处理相关环节是在 Consumer 端，在 Provider 端则是由 &lt;code>org.apache.dubbo.rpc.filter.ExceptionFilter&lt;/code> 进行处理，
它是一系列责任链 Filter 中的一环，专门用来处理异常。&lt;/p>
&lt;p>Dubbo 在 Provider 端的异常会在封装进 &lt;code>appResponse&lt;/code> 中。下面的流程图揭示了 &lt;code>ExceptionFilter&lt;/code> 源码的异常处理流程:&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/19/triple/2.jpeg" alt="2.jpeg">&lt;/p>
&lt;p>而当 &lt;code>appResponse&lt;/code> 回到了 Consumer 端，会在 &lt;code>InvocationUtil&lt;/code> 里调用 &lt;code>AppResponse&lt;/code> 的 &lt;code>recreate&lt;/code> 方法抛出异常，
最终可以在 Consumer 端捕获：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> Object &lt;span style="color:#268bd2">recreate&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Throwable &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>exception &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object stackTrace &lt;span style="color:#719e07">=&lt;/span> exception&lt;span style="color:#719e07">.&lt;/span>getStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>stackTrace &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exception&lt;span style="color:#719e07">.&lt;/span>setStackTrace&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> StackTraceElement&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> exception&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="triple-通信原理">Triple 通信原理&lt;/h2>
&lt;p>在上一节中，我们已经介绍了 Dubbo 在 Consumer 端大致发送数据的流程，
可以看到最终依靠的是 &lt;code>AbstractInvoker&lt;/code> 的实现类来发送数据。
在 Triple 协议中，&lt;code>AbstractInvoker&lt;/code> 的具体实现类是 &lt;code>TripleInvoker&lt;/code> ，
&lt;code>TripleInvoker&lt;/code> 在发送前会启动监听器，监听来自 Provider 端的响应结果，
并调用 &lt;code>ClientCallToObserverAdapter&lt;/code> 的 &lt;code>onNext&lt;/code> 方法发送消息，
最终会在底层封装成 Netty 请求发送数据。&lt;/p>
&lt;p>在正式的请求发起前，TripleServer 会注册 &lt;code>TripleHttp2FrameServerHandler&lt;/code>，
它继承自 Netty 的 &lt;code>ChannelDuplexHandler&lt;/code>，
其作用是会在 &lt;code>channelRead&lt;/code> 方法中不断读取 Header 和 Data 信息并解析，
经过层层调用，
会在 &lt;code>AbstractServerCall&lt;/code> 的 &lt;code>onMessage&lt;/code> 方法里把来自 consumer 的信息流进行反序列化，
并最终由交由 &lt;code>ServerCallToObserverAdapter&lt;/code> 的 &lt;code>invoke&lt;/code> 方法进行处理。&lt;/p>
&lt;p>在 &lt;code>invoke&lt;/code> 方法中，根据 consumer 请求的数据调用服务端相应的方法，并异步等待结果；'
若服务端抛出异常，则调用 &lt;code>onError&lt;/code> 方法进行处理，
否则，调用 &lt;code>onReturn&lt;/code> 方法返回正常的结果，大致代码逻辑如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用invoke方法请求服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Result response &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//异步等待结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> response&lt;span style="color:#719e07">.&lt;/span>whenCompleteWithContext&lt;span style="color:#719e07">((&lt;/span>r&lt;span style="color:#719e07">,&lt;/span> t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//若异常不为空
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>t &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用方法过程出现异常，调用onError方法处理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> responseObserver&lt;span style="color:#719e07">.&lt;/span>onError&lt;span style="color:#719e07">(&lt;/span>t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>response&lt;span style="color:#719e07">.&lt;/span>hasException&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用onReturn方法处理业务异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> onReturn&lt;span style="color:#719e07">(&lt;/span>response&lt;span style="color:#719e07">.&lt;/span>getException&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//正常返回结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> onReturn&lt;span style="color:#719e07">(&lt;/span>r&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>大体流程如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/19/triple/3.jpeg" alt="3.jpeg">&lt;/p>
&lt;h2 id="实现版本">实现版本&lt;/h2>
&lt;p>了解了上述原理，我们就可以进行相应的改造了，
能让 consumer 端捕获异常的&lt;strong>关键在于把异常对象以及异常信息序列化后再发送给consumer端&lt;/strong>。
常见的序列化协议很多，例如 Dubbo/HSF 默认的 hessian2 序列化；
还有使用广泛的 JSON 序列化；以及 gRPC 原生支持的 protobuf(PB) 序列化等等。
Triple协议因为兼容grpc的原因，默认采用 Protobuf 进行序列化。
上述提到的这三种典型的序列化方案作用类似，但在实现和开发中略有不同。
PB 不可由序列化后的字节流直接生成内存对象，
而 Hessian 和 JSON 都是可以的。后两者反序列化的过程不依赖“二方包”，
其序列化和反序列化的代码由 proto 文件相同，只要客户端和服务端用相同的 proto 文件进行通信，
就可以构造出通信双方可解析的结构。&lt;/p>
&lt;p>单一的 protobuf 无法序列化异常信息，
因此我们采用 &lt;code>Wrapper + PB&lt;/code> 的形式进行序列化异常信息，
抽象出一个 &lt;code>TripleExceptionWrapperUtils&lt;/code> 用于序列化异常，
并在 &lt;code>trailer&lt;/code> 中采用 &lt;code>TripleExceptionWrapperUtils&lt;/code> 序列化异常，大致代码流程如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/19/triple/4.jpeg" alt="4.jpeg">&lt;/p>
&lt;p>上面的实现方案看似非常合理，已经能把 Provider 端的异常对象和信息回传，
并在 Consumer 端进行捕获。但仔细想想还是有问题的：
通常在 HTTP2 为基础的通信协议里会对 header 大小做一定的限制，
太大的header size 会导致性能退化严重，为了保证性能，
往往以 HTTP2 为基础的协议在建立连接的时候是要协商最大 header size 的，
超过后会发送失败。对于 Triple 协议来说，在设计之初就是基于 HTTP 2.0，
能无缝兼容 Grpc，而 Grpc header 头部只有 8KB 大小，
异常对象大小可能超过限制，从而丢失异常信息；
且多一个 header 携带序列化的异常信息意味着用户能加的 header 数量会减少，
挤占了其他 header 所能占用的空间。&lt;/p>
&lt;p>经过讨论，考虑将异常信息放置在 Body，将序列化后的异常从 trailer 挪至 body，
采用 &lt;code>TripleWrapper + protobuf&lt;/code> 进行序列化，把相关的异常信息序列化后回传。
社区围绕这个问题进行了一系列的争论，读者也可尝试先思考一下：&lt;/p>
&lt;p>1.在 body 中携带回传的异常信息，其对应HTTP header状态码该设置为多少？&lt;/p>
&lt;p>2.基于 http2 构建的协议，按照主流的 grpc 实现方案，相关的错误信息放在 &lt;code>trailer&lt;/code>，理论上不存在body，上层协议也需要保持语义一致性，若此时在payload回传异常对象，且grpc并没有支持在Body回传序列化对象的功能， 会不会破坏Http和grpc协议的语义？从这个角度出发，异常信息更应该放在trailer里。&lt;/p>
&lt;p>3.作为开源社区，不能一味满足用户的需求，非标准化的用法注定是会被淘汰的，应该尽量避免更改 Protobuf的语义，是否在Wrapper层去支持序列化异常就能满足需求？&lt;/p>
&lt;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。&lt;/p>
&lt;p>更改后的版本只需在异常不为空时返回相关的异常信息，采用 &lt;code>TripleWrapper + Protobuf&lt;/code> 进行序列化异常信息，并在consumer端进行解析和反序列化，大体流程如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2022/12/19/triple/5.jpeg" alt="5.jpeg">&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>通过对 Dubbo 3.0 新增自定义异常的版本迭代中可以看出，尽管只能新增一个小小的特性，流程下并不复杂，但由于要考虑互通、兼容和协议的设计理念，因此思考和讨论的时间可能比写代码的时间更多。&lt;/p></description></item><item><title>Blog: 聚焦稳定性，Dubbo 发版规划公布</title><link>https://cn.dubbo.apache.org/zh/blog/2022/12/05/%E8%81%9A%E7%84%A6%E7%A8%B3%E5%AE%9A%E6%80%A7dubbo-%E5%8F%91%E7%89%88%E8%A7%84%E5%88%92%E5%85%AC%E5%B8%83/</link><pubDate>Mon, 05 Dec 2022 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2022/12/05/%E8%81%9A%E7%84%A6%E7%A8%B3%E5%AE%9A%E6%80%A7dubbo-%E5%8F%91%E7%89%88%E8%A7%84%E5%88%92%E5%85%AC%E5%B8%83/</guid><description>
&lt;h2 id="dubbo-简介">Dubbo 简介&lt;/h2>
&lt;p>Apache Dubbo 是一款 RPC 服务开发框架，用于解决微服务架构下的服务治理与通信问题，官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力， 利用 Dubbo 提供的丰富服务治理特性，可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展，用户可以方便的实现流量拦截、选址的各种定制逻辑。&lt;/p>
&lt;h2 id="我应该如何选择版本">我应该如何选择版本？&lt;/h2>
&lt;p>对于这个问题，一直以来 Dubbo 都没有很好地去回答。究其原因保证所有版本的稳定性是一件非常有挑战的事情。比如在 Dubbo 2.7 的一些版本中，出现了一些前后不兼容的问题，给一些用户升级带来了困扰。为此，很多用户从保障稳定性的视角出发，选择了更保守的策略，即生产环境采用被采用最多的版本，此后就不再更新，避免升级过程中出现意外。&lt;/p>
&lt;p>但是这种方式无疑是有问题的，一方面这将导致永远无法用上社区新的功能，得到技术演进的红利；另一方面，对于安全漏洞和带来不稳定因素的 bug 等已知问题如果不升级永远会存在，久而久之将给应用带来巨大的风险，很有可能导致严重的安全生产事故。&lt;/p>
&lt;p>因此，为了更好地回答这个问题，Dubbo 制定了未来的版本迭代规划，明确版本的稳定性维护机制，提升开发者使用 Dubbo 的信心。&lt;/p>
&lt;h2 id="现状">现状&lt;/h2>
&lt;p>在此前，Dubbo 总共维护了 2.6.x、2.7.x 以及 3.x 三个大版本，其中 2.7.x 为捐献进入 Apache 基金会之后的版本。&lt;/p>
&lt;p>2.6.x 在去年已经宣布生命周期结束（EOL）了，而 2.7.x 累计维护了四年的时间了。这四年的时间里，2.7.x 版本经历了多次大的修改，如 2.7.3 版本中对 Nacos 的注册方式进行了修改、2.7.6 版本中添加了应用级服务发现、2.7.9 版本中对应用级服务发现进行大的重构等。&lt;/p>
&lt;p>这些修改都对 2.7.x 版本的稳定性带来了巨大的风险隐患，导致 2.7.x 中很多版本或多或少存在不同的问题，用户无法选择一个最稳定的版本进行使用。&lt;/p>
&lt;p>因此，我们希望在 3.x 版本中将这种不稳定的情况通过发版机制给收敛掉，提高 Dubbo 版本的稳定性。&lt;/p>
&lt;h2 id="未来总体规划">未来总体规划&lt;/h2>
&lt;p>在接下来，Dubbo 将以每 6 个月为一个周期，对每个版本包括了新功能合入、稳定性维护以及安全漏洞修复三个大方向的内容。&lt;/p>
&lt;p>新功能合入是指将 Dubbo 开发过程中所有新的特性、性能优化、破坏性修改都会被合入的行为。由于新功能是需要一段时间迭代稳定的，所以新功能的合入会给对应版本带来不稳定因素，因此只能在每个版本最开始的时候进行。&lt;/p>
&lt;p>稳定性维护是指修复&lt;strong>已有&lt;/strong>功能的非预期行为，进一步提升版本的稳定性。特别的，对于一些大的问题修复，如功能需要整体重构的，或者回带来破坏性影响的提交将被视为新功能提交，执行新功能合入的流程。避免因为修复一个问题而来带更多问题的严重后果。&lt;/p>
&lt;p>安全维护是指修复使用中的安全风险问题，这一块主要是解决来自白帽子提交的漏洞问题。&lt;/p>
&lt;p>新功能合入在每个版本中会持续 &lt;strong>6&lt;/strong> 个月，稳定性维护在每个版本中会持续 &lt;strong>12&lt;/strong> 个月，安全维护在每个版本中会持续 &lt;strong>18&lt;/strong> 个月。&lt;/p>
&lt;p>因此，从整体迭代规划来看，每 6 个月 Dubbo 会开始一个新的版本迭代，与之同时的，也会有一个版本宣布生命周期结束（EOL）。&lt;/p>
&lt;h2 id="逐版本维护规划">逐版本维护规划&lt;/h2>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/release/release-roadmap.png" alt="image.png">&lt;/p>
&lt;p>上图为具体到每个版本在每个时间点的维护状态图。&lt;/p>
&lt;p>对于 Dubbo 2.7 以及 3.0 版本，目前已经进入了安全维护阶段，将在 2023 年 3 月宣布版本维护周期结束。&lt;/p>
&lt;p>对于 Dubbo 3.1 版本，目前已经是稳定性维护阶段，如前一小节所述，目前只会继续合入和稳定性修改的修改。处于稳定性维护阶段的版本也是社区推荐生产使用的最新版本。稳定性维护工作将持续到 2023 年 3 月，在此之后会有持续 6 个月的安全维护阶段，最终在 2023 年 9 月结束其版本的生命周期。&lt;/p>
&lt;p>对于 Dubbo 3.2 版本，目前仍处在新功能合入的阶段，发布的是 beta 版本。此阶段适合一些尝鲜使用，版本稳定性相较于已经进入仅稳定性维护阶段的版本稍欠。在 2023 年 3 月，Dubbo 3.2 将结束新功能合入周期，进行充分的稳定性验证，并正式发布生产可用的 GA 版本。此后在 2023 年 9 月之前会进行稳定性的维护，在 2024 年 3 月结束版本的生命周期。&lt;/p>
&lt;p>对于 Dubbo 3.3 版本，将在 2023 年 3 月紧随着 3.2 版本结束新功能合入阶段后正式开始开发。因此，Dubbo 3.3 版本的第一个 beta release 也将会在 2023 年 3 月发布。同样的，经过 6 个月的新功能合入阶段之后，Dubbo 3.3 版本将会在 2023 年 9 月正式发布生产可用 GA 版本，同时进入稳定性维护阶段。之后在 2024 年 3 月进入安全维护阶段，在 2024 年 9 月结束版本的生命周期。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>对于绝大多数的生产用户，我们建议使用&lt;strong>当前处于仅稳定性维护阶段的版本的最新小版本&lt;/strong>。如在 2022 年 9 月至 2023 年 3 月这段时间，Dubbo 3.1 是处于仅稳定性维护阶段的，因此首选 Dubbo 3.1 版本。而对于如 3.1.0、3.1.1、3.1.2 等的 Dubbo 3.1.x 的多个小版本，我们建议直接使用最新的小版本。由于各个小版本之间仅包含稳定性修复，所以越往后的版本是越稳定的。&lt;/p>
&lt;p>对于愿意尝鲜、体验新功能的用户，可以直接基于最新的开发版本进行测试。即使是开发版本，Dubbo 也有一整套的质量保障机制，如果使用过程中遇到问题，欢迎向社区提交反馈。&lt;/p>
&lt;p>目前，阿里巴巴集团已经基于 Dubbo 3.0 的稳定版本完美支撑了整个核心电商今年双十一的所有调用，现在也正在升级到 Dubbo 3.1 这个最新的稳定版本上来。我们欢迎更多的用户一起升级到 Dubbo 3.1 版本上，在升级过程中可以参考官网给出的升级指南，遇到任何问题也可以通过 issue、微信群、钉钉群及时反馈社区，我们将尽全力协助。&lt;/p>
&lt;h2 id="更多">更多&lt;/h2>
&lt;p>在版本规划的方向上，除了本次发布的迭代线路图，Dubbo 还将通过一系列的机制保障用户的使用体验是连续的、稳定的。在大版本升级方面，Dubbo 将建设&lt;strong>完善的版本升级指南&lt;/strong>，如 Dubbo 3.1 版本升级到 Dubbo 3.2 版本中的重大修改点，让用户在升级的时候能够清晰的了解其中的风险点，更好的规划升级节奏。&lt;/p>
&lt;p>此外，对于目前人在大范围使用的 Dubbo 2.7 版本，在近期 Dubbo 社区也将推出&lt;strong>针对 Dubbo 2.7 版本的升级的指南与工具&lt;/strong>，通过工程化的方式让所有的 Dubbo 2.7 用户都可以提前查出所有的版本差异点，平滑升级到 Dubbo 3 上来。&lt;/p></description></item><item><title>Blog: Dubbo 3.1.3、3.2.0-beta.2 正式发布</title><link>https://cn.dubbo.apache.org/zh/blog/2022/11/29/dubbo-3.1.33.2.0-beta.2-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Tue, 29 Nov 2022 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2022/11/29/dubbo-3.1.33.2.0-beta.2-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;h2 id="dubbo-313">Dubbo 3.1.3&lt;/h2>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/release/3-1-3.png" alt="image.png">&lt;/p>
&lt;h3 id="修改内容">修改内容&lt;/h3>
&lt;ul>
&lt;li>修复本地调用的过程中 Filter 顺序异常的问题&lt;/li>
&lt;li>支持导入协议配置项到 MetadataService&lt;/li>
&lt;li>支持在发布 MetadataService 时自动选择可用端口&lt;/li>
&lt;li>完善错误码内容&lt;/li>
&lt;li>泛化调用下支持一致性哈希负载均衡&lt;/li>
&lt;li>修复泛化调用时反序列化异常的问题&lt;/li>
&lt;li>修复由于 JVM Methods 顺序不一致导致的应用级元数据无法复用的问题&lt;/li>
&lt;li>默认关闭端口复用能力，修复 remoting 组建找不到 SPI 的问题&lt;/li>
&lt;li>修复由于引用计数异常导致的 safe gard 问题&lt;/li>
&lt;li>删除 Nacos 注册中心组建中使用的 guava 依赖&lt;/li>
&lt;li>修复接口级使用 Nacos 注册中心时由于地址聚合导致的无法下线的问题&lt;/li>
&lt;li>多个代码质量优化提交&lt;/li>
&lt;/ul>
&lt;h3 id="新贡献者">新贡献者&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://github.com/zhangzq7">@zhangzq7&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10847">#10847&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;li>&lt;a href="https://github.com/akaakking">@akaakking&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10799">#10799&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;li>&lt;a href="https://github.com/wxbty">@wxbty&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10921">#10921&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;li>&lt;a href="https://github.com/haoxz11">@haoxz11&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10937">#10937&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h2 id="dubbo-320-beta2">Dubbo 3.2.0-beta.2&lt;/h2>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/release/3-2-0-beta-2.png" alt="image.png">&lt;/p>
&lt;h3 id="修改内容-1">修改内容&lt;/h3>
&lt;ul>
&lt;li>切换到在用户线程序列化，提升协议性能&lt;/li>
&lt;li>支持 Netty3 的端口复用&lt;/li>
&lt;li>修复存在多个用户配置的 providedBy 时不生效的问题&lt;/li>
&lt;li>支持 istio 的 first-party-jwt 能力&lt;/li>
&lt;li>删除 fastjson 和 gson 的传递依赖&lt;/li>
&lt;li>支持可选 appResponse 不透传的能力&lt;/li>
&lt;li>切换到 Fastjson2 为默认序列化依赖&lt;/li>
&lt;li>完善注册中心推送的日志&lt;/li>
&lt;li>修复路由刷新时机早于服务目录刷新时机的问题&lt;/li>
&lt;li>关闭地址推空保护&lt;/li>
&lt;li>更新支持 GraalVM Native 的 SPI 生成代码&lt;/li>
&lt;li>支持使用 plain text 模式传输 xds 通道&lt;/li>
&lt;li>支持 Nacos 批量注册，修复多注册覆盖的问题&lt;/li>
&lt;li>支持 Spring Framework 6 and Spring Boot 3&lt;/li>
&lt;li>多个代码质量优化提交&lt;/li>
&lt;li>前述 Dubbo 3.1.3 的所有修改内容&lt;/li>
&lt;/ul>
&lt;h3 id="新贡献者-1">新贡献者&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://github.com/weixsun">@weixsun&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10941">#10941&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h2 id="未来的版本规划">未来的版本规划&lt;/h2>
&lt;p>3.0、3.1、3.2 有什么区别？未来会怎么发展？发版周期是怎么样的？更多的版本迭代规划也将在近期推出，欢迎关注 Apache Dubbo 官方公众号获取最新的信息。&lt;/p></description></item><item><title>Blog: Apache Dubbo 多语言体系再添新员：首个 Rust 语言版本正式发布</title><link>https://cn.dubbo.apache.org/zh/blog/2022/10/23/apache-dubbo-%E5%A4%9A%E8%AF%AD%E8%A8%80%E4%BD%93%E7%B3%BB%E5%86%8D%E6%B7%BB%E6%96%B0%E5%91%98%E9%A6%96%E4%B8%AA-rust-%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Sun, 23 Oct 2022 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2022/10/23/apache-dubbo-%E5%A4%9A%E8%AF%AD%E8%A8%80%E4%BD%93%E7%B3%BB%E5%86%8D%E6%B7%BB%E6%96%B0%E5%91%98%E9%A6%96%E4%B8%AA-rust-%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;p>Dubbo Rust 定位为 Dubbo 多语言体系的重要实现，提供高性能、易用、可扩展的 RPC 框架，同时通过接入 Dubbo Mesh 体系提供丰富的服务治理能力。本文主要为大家介绍 Dubbo Rust 项目基本情况，通过一个示例快速体验 Rust 首个正式版本特性，并给出了 Dubbo Rust 社区的近期规划，适合于关注或正在采用 Rust 语言的开发者与企业用户阅读。&lt;/p>
&lt;h2 id="1-dubbo-rust-简介">1 Dubbo Rust 简介&lt;/h2>
&lt;p>Dubbo 作为 Apache 基金会最活跃的明星项目之一，同时也是国内最受欢迎的开源微服务框架，在易用性、高性能通信、服务治理等方面有着非常大的优势，通过 Dubbo3、Dubbo Mesh 等提供了云原生友好的开发与部署模式。与此同时，Dubbo 的多语言体系也得到了快速发展，长期以来提供的有 Java、Golang 两种语言实现，Rust、Node、Python、C++ 等语言实现的支持也已在社区正式启动。&lt;/p>
&lt;ul>
&lt;li>Dubbo 官网 &lt;a href="https://dubbo.apache.org/">https://dubbo.apache.org/&lt;/a>&lt;/li>
&lt;li>Dubbo Java &lt;a href="https://github.com/apache/dubbo/">https://github.com/apache/dubbo/&lt;/a>&lt;/li>
&lt;li>Dubbo Golang &lt;a href="https://github.com/apache/dubbo-go/">https://github.com/apache/dubbo-go/&lt;/a>&lt;/li>
&lt;li>&lt;strong>Dubbo Rust &lt;a href="https://github.com/apache/dubbo-rust/">https://github.com/apache/dubbo-rust/&lt;/a>&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Dubbo Rust 目标是对齐 Dubbo3 的所有核心功能设计，包括基于 HTTP/2 的高性能通信、用户友好的微服务开发编程模式、通过接入DubboMesh提供丰富的服务治理能力等，相比于其他语言实现，Dubbo Rust 将很好的利用 Rust 语言极致性能、安全和指令级掌控能力的特点。
对于微服务框架，主流的编程语言都有对应的实现，而 Dubbo Rust 将很好的填补 Rust 领域的空白：&lt;/p>
&lt;ul>
&lt;li>Golang：在微服务框架领域已经占据着很重要的地位；开源社区出现了dubbo-go、gRPC、go-micro、go-zero等多个微服务框架&lt;/li>
&lt;li>Java：国内用户量最大的编程语言，Spring Cloud、Dubbo等优秀的微服务框架已经非常流行&lt;/li>
&lt;li>C/C++：brpc、grpc 等微服务框架&lt;/li>
&lt;li>Rust：目前没有很完善的微服务框架&lt;/li>
&lt;/ul>
&lt;p>依托 Dubbo 庞大的用户群，以及 Dubbo 体系下的 Mesh 服务治理整体方案规划。Dubbo Rust 可以轻松地融入到现有的云原生研发体系中，不会增加使用者的研发负担。下图是社区推出的 Dubbo Mesh 架构设计。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/rust/dubbo-rust-mesh.png" alt="dubbo-rust">&lt;/p>
&lt;p>在上述架构下，整体分为控制面和数据面两个部分，其中，&lt;/p>
&lt;ul>
&lt;li>控制面负责管理流量治理、地址发现、安全认证、可观测性等服务治理相关的配置信管控工作，包括与K8S等底层技术设施的对接；&lt;/li>
&lt;li>Dubbo Rust 作为数据面组件，负责接收来自控制面的配置；将配置应用到服务中；同时为服务提供基础的RPC通信能力。&lt;/li>
&lt;/ul>
&lt;p>在架构设计方面，Dubbo Rust 将围绕 Dubbo 核心设计以及 Rust 语言的特性进行设计，并将 Dubbo 框架的核心设计输出为文档，从而提升Dubbo框架的易用性。因此，Dubbo Rust 具有如下特点：易用性、高性能以及可扩展，同时面向云原生提供丰富的服务治理能力。&lt;/p>
&lt;h2 id="2-快速体验-dubbo-rust">2 快速体验 Dubbo Rust&lt;/h2>
&lt;h3 id="21-首个版本核心能力">2.1 首个版本核心能力&lt;/h3>
&lt;p>&lt;strong>Dubbo Rust 首个正式版本为 v0.2.0&lt;/strong>，v0.2.0 提供的能力包括&lt;/p>
&lt;ul>
&lt;li>基于 HTTP/2 的 Triple 协议的基础通信能力&lt;/li>
&lt;li>基于 IDL 的 RPC 定义支持，Protobuf 来生成代码，同时支持 Serde 序列化&lt;/li>
&lt;li>request-response、request/response streaming、bi-streaming 通信模型支持&lt;/li>
&lt;li>设计了简洁的、可扩展的架构，支持对 Listener、Connector、Filter、Protocol以及Invoker组件进行扩展&lt;/li>
&lt;/ul>
&lt;p>Dubbo Rust v0.2.0 的核心组件及通信流程如下图所示&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/rust/dubbo-rust-module.png" alt="dubbo-rust">&lt;/p>
&lt;p>核心架构已经基本完成，接下来的版本将重点关注核心组件的扩展以及服务治理相关组件的设计实现。&lt;/p>
&lt;h3 id="22-quick-start">2.2 Quick Start&lt;/h3>
&lt;blockquote>
&lt;p>完整示例可查看 【Dubbo官网】 -&amp;gt; 【Rust SDK 文档】。
&lt;a href="https://dubbo.apache.org/zh/docs3-v2/rust-sdk/quick-start/">https://dubbo.apache.org/zh/docs3-v2/rust-sdk/quick-start/&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>使用 Dubbo Rust 服务开发的基本步骤为&lt;/p>
&lt;ol>
&lt;li>
&lt;p>使用 IDL 定义服务&lt;/p>
&lt;/li>
&lt;li>
&lt;p>添加 Dubbo Rust 依赖到项目&lt;/p>
&lt;/li>
&lt;li>
&lt;p>编译 IDL&lt;/p>
&lt;/li>
&lt;li>
&lt;p>基于 IDL 编译生成的 stub 编写 Server &amp;amp; Client 逻辑&lt;/p>
&lt;/li>
&lt;li>
&lt;p>运行项目&lt;/p>
&lt;/li>
&lt;li>
&lt;p>使用 IDL 定义 Dubb 服务&lt;/p>
&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-protobuf" data-lang="protobuf">&lt;span style="display:flex;">&lt;span>```protobuf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ./proto/greeter.proto
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>syntax &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;proto3&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">option&lt;/span> java_multiple_files &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache.dubbo.sample.tri;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The request message containing the user&amp;#39;s name.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">message&lt;/span> &lt;span style="color:#268bd2">GreeterRequest&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#dc322f">string&lt;/span> name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The response message containing the greetings
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">message&lt;/span> &lt;span style="color:#268bd2">GreeterReply&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#dc322f">string&lt;/span> &lt;span style="color:#268bd2">message&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">service&lt;/span> Greeter{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// unary
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">rpc&lt;/span> greet(GreeterRequest) &lt;span style="color:#719e07">returns&lt;/span> (GreeterReply);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>�
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2. 增加 Dubbo Rust 依赖
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>```toml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>```toml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># ./Cargo.toml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[package]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>name = &amp;#34;example-greeter&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>version = &amp;#34;0.1.0&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>edition = &amp;#34;2021&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[dependencies]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo = &amp;#34;0.1.0&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo-config = &amp;#34;0.1.0&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[build-dependencies]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo-build = &amp;#34;0.1.0&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>�3. 编译 IDL 并根据生成的 stub 编写逻辑
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>编写 Dubbo Server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>```rust
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>#[tokio::main]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>async fn main() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> register_server(GreeterServerImpl {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &amp;#34;greeter&amp;#34;.to_string(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> });
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // Dubbo::new().start().await;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Dubbo::new()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .with_config({
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> let r = RootConfig::new();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> match r.load() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Ok(config) =&amp;gt; config,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Err(_err) =&amp;gt; panic!(&amp;#34;err: {:?}&amp;#34;, _err), // response was droped
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .await;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>struct GreeterServerImpl {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: String,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>impl Greeter for GreeterServerImpl {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> async fn greet(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;amp;self,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> request: Request&amp;lt;GreeterRequest&amp;gt;,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ) -&amp;gt; Result&amp;lt;Response&amp;lt;GreeterReply&amp;gt;, dubbo::status::Status&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> println!(&amp;#34;GreeterServer::greet {:?}&amp;#34;, request.metadata);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Ok(Response::new(GreeterReply {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> message: &amp;#34;hello, dubbo-rust&amp;#34;.to_string(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>�
编写 Dubbo Client&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-rust" data-lang="rust">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">#[tokio::main]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">async&lt;/span> &lt;span style="color:#719e07">fn&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">let&lt;/span> &lt;span style="color:#719e07">mut&lt;/span> cli &lt;span style="color:#719e07">=&lt;/span> GreeterClient::new().with_uri(&lt;span style="color:#2aa198">&amp;#34;http://127.0.0.1:8888&amp;#34;&lt;/span>.to_string());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> println!(&lt;span style="color:#2aa198">&amp;#34;# unary call&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">let&lt;/span> resp &lt;span style="color:#719e07">=&lt;/span> cli
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .greet(Request::new(GreeterRequest {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &lt;span style="color:#2aa198">&amp;#34;message from client&amp;#34;&lt;/span>.to_string(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#719e07">await&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">let&lt;/span> resp &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">match&lt;/span> resp {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">Ok&lt;/span>(resp) &lt;span style="color:#719e07">=&amp;gt;&lt;/span> resp,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">Err&lt;/span>(err) &lt;span style="color:#719e07">=&amp;gt;&lt;/span> &lt;span style="color:#719e07">return&lt;/span> println!(&lt;span style="color:#2aa198">&amp;#34;{:?}&amp;#34;&lt;/span>, err),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">let&lt;/span> (_parts, body) &lt;span style="color:#719e07">=&lt;/span> resp.into_parts();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> println!(&lt;span style="color:#2aa198">&amp;#34;Response: {:?}&amp;#34;&lt;/span>, body);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样，一个简单的 Dubbo Rust 示例就开发完成了，可以到 Dubbo 官网查看完整文档。&lt;/p>
&lt;h2 id="3-roadmap-与未来规划">3 Roadmap 与未来规划&lt;/h2>
&lt;p>Dubbo Rust Roadmap 规划分为三个阶段：&lt;/p>
&lt;ul>
&lt;li>首先，提供作为 RPC 框架的基础能力，此阶段重点完成的包括基于 HTTP/2 的 RPC 通信、基于 IDL 的 RPC 定义、其他必要的 RPC 内核组件等&lt;/li>
&lt;li>其实，是完善 Dubbo Rust 作为微服务框架的高级功能，此阶段包括微服务定义、配置、功能设计等，如服务超时、异步调用、上下文传递等，具体可参见 Dubbo Java 的高级特性。&lt;/li>
&lt;li>第三阶段重点是引入丰富的服务治理能力支持，如流量治理、限流降级、可观测性等，这一目标将主要通过融入 Dubbo Mesh 体系，即适配 Dubbo Mesh 控制面实现。&lt;/li>
&lt;/ul>
&lt;p>其中，第一阶段的工作已经基本完成，大家可通过上文的 Quick Start 进行深入体验，第二、第三阶段的工作已经在社区全面开展，欢迎感兴趣的社区开发者参与进来，具体联系方式参见下文。&lt;/p>
&lt;p>下图是侧重从第一阶段（RPC框架）、第二阶段（微服务开发框架）的视角对当前 Dubbo Rust 功能完备性的评估和任务拆解。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/rust/dubbo-rust-tasks.png" alt="dubbo-rust">&lt;/p>
&lt;p>上图中都是 Dubbo Rust 核心设计的重要组件，保证 Dubbo Rust 具备微服务框架中完整的 RPC 通信能力以及服务治理能力。&lt;/p>
&lt;ul>
&lt;li>Protocol、Filter、Listener、Connector 等组件都是 RPC 通信核心能力&lt;/li>
&lt;li>服务注册发现、负载均衡、Cluster、Metadata 为后续服务治理能力做铺垫&lt;/li>
&lt;/ul>
&lt;p>除了上图列出的模块以外，还有一些非功能需求也需要支持，例如：&lt;/p>
&lt;ul>
&lt;li>Dubbo 多语言框架之间相互通信测试&lt;/li>
&lt;li>性能验证与持续的 benchmark 机制&lt;/li>
&lt;li>整体架构的持续优化，如核心配置简化以及相应的文档完善&lt;/li>
&lt;/ul>
&lt;h2 id="4-参与-dubbo-rust-社区">4 参与 Dubbo Rust 社区&lt;/h2>
&lt;p>和 Rust 语言一样，Dubbo Rust 是一个非常有活力、非常前沿的社区，另一方面，依赖 Apache Dubbo 社区背后庞大的开发者群体和企业用户，Dubbo Rust 有着非常深厚的用户基础和发展潜力。Dubbo Rust 的快速发展期待社区贡献者的加入
参与 Dubbo Rust 社区可以收获&lt;/p>
&lt;ul>
&lt;li>见证 Dubbo Rust 开源项目的建设以及发展&lt;/li>
&lt;li>在大型项目中通过实际使用学习 Rust 语言，加深对 Rust 语言的理解&lt;/li>
&lt;li>获得提名为 Apache Dubbo CommitterPMC&lt;/li>
&lt;li>借助 Dubbo 社区提高个人曝光度，提高个人技术影响力&lt;/li>
&lt;li>与阿里巴巴等企业专家的面对面交流机会，快速提高技术视野&lt;/li>
&lt;/ul>
&lt;p>参与 Dubbo Rust 社区的方式有如下几种&lt;/p>
&lt;ul>
&lt;li>搜索并加入钉钉群并参与社区双周会，钉钉群号 &lt;strong>44694199&lt;/strong>&lt;/li>
&lt;li>到 GitHub 提 Issue 或贡献代码 &lt;a href="https://github.com/apache/dubbo-rust">https://github.com/apache/dubbo-rust&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Blog: Proxyless Mesh在Dubbo中的实践</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</guid><description>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>随着 Dubbo 3.1 的 release，Dubbo 在云原生的路上又迈出了重要的一步。在这个版本中添加了 Proxyless Mesh 的新特性，Dubbo Proxyless Mesh 直接实现 xDS 协议解析，
实现 Dubbo 与 Control Plane 的直接通信，进而实现控制面对流量管控、服务治理、可观测性、安全等的统一管控，规避 Sidecar 模式带来的性能损耗与部署架构复杂性。&lt;/p>
&lt;h2 id="什么是service-mesh">什么是Service Mesh&lt;/h2>
&lt;p>Service Mesh 又译作 “服务网格”，作为服务间通信的基础设施层。Buoyant 公司的 CEO Willian Morgan 在他的这篇文章 &lt;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? &lt;/a>
中解释了什么是 Service Mesh，为什么云原生应用需要 Service Mesh。&lt;/p>
&lt;p>&lt;strong>下面是 Willian Morgan 对 Service Mesh 的解释。&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>A Service Mesh is a dedicated infrastructure layer for handling service-to-service communication.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>It’s responsible for the reliable delivery of requests through the complex topology of services
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>that comprise a modern, cloud native application. In practice, the Service Mesh is typically implemented
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>as an array of lightweight network proxies that are deployed alongside application code, without the
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application needing to be aware.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>翻译成中文&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>服务网格（Service Mesh）是处理服务间通信的基础设施层。它负责构成现代云原生应用程序的复杂服务拓扑来可靠地交付请求。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>在实践中，Service Mesh 通常以轻量级网络代理阵列的形式实现，这些代理与应用程序代码部署在一起，对应用程序来说无需感知代理的存在。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说到 Service Mesh 一定离不开 Sidecar 经典架构模式。它通过在业务 Pod 中注入 Sidecar 容器，接管业务容器的通信流量，同时 Sidecar 容器与网格平台的控制平面对接，
基于控制平面下发的策略，对代理流量实施治理和管控，将原有服务框架的治理能力下层到 Sidecar 容器中，从而实现了基础框架能力的下沉，与业务系统解耦。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/1.png" alt="Service Mesh">&lt;/p>
&lt;p>经典的 Sidecar Mesh 部署架构有很多优势，如平滑升级、多语言、业务侵入小等，但也带来了一些额外的问题，比如:&lt;/p>
&lt;ul>
&lt;li>Proxy 带来的性能损耗，在复杂拓扑的网络调用中将变得尤其明显&lt;/li>
&lt;li>流量拦截带来的架构复杂性&lt;/li>
&lt;li>Sidecar 生命周期管理&lt;/li>
&lt;li>部署环境受限，并不是所有环境都满足 Sidecar 流量拦截条件&lt;/li>
&lt;/ul>
&lt;p>针对 Sidecar Mesh 模型的问题，Dubbo 社区自很早之前就做了 Dubbo 直接对接到控制面的设想与思考，并在国内开源社区率先提出了 Proxyless Mesh 的概念，当然就 Proxyless 概念的说法而言，最开始是谷歌提出来的。&lt;/p>
&lt;h2 id="dubbo-proxyless-mesh">Dubbo Proxyless Mesh&lt;/h2>
&lt;p>Dubbo Proxyless 模式是指 Dubbo 直接与 Istiod通信，通过 xDS协议实现服务发现和服务治理等能力。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/2.png" alt="Proxyless">&lt;/p>
&lt;p>Proxyless 模式使得微服务又回到了 2.x 时代的部署架构，同 Dubbo 经典服务治理模式非常相似，所以说这个模式并不新鲜， Dubbo 从最开始就是这样的设计模式。
这样做可以极大的提升应用性能，降低网络延迟。有人说这种做法又回到了原始的基于 SDK 的微服务模式，其实非也，它依然使用了 Envoy 的 xDS API，
但是因为不再需要向应用程序中注入 Sidecar 代理，因此可以减少应用程序性能的损耗。&lt;/p>
&lt;p>但相比于 Mesh 架构，Dubbo 经典服务治理模式并没有强调控制面的统一管控，而这点恰好是 Service Mesh 所强调的，强调对流量、可观测性、证书等的标准化管控与治理，也是 Mesh 理念先进的地方。&lt;/p>
&lt;p>在 Dubbo Proxyless 架构模式下，Dubbo 进程将直接与控制面通信，Dubbo 进程之间也继续保持直连通信模式，我们可以看出 Proxyless 架构的优势：&lt;/p>
&lt;ul>
&lt;li>没有额外的 Proxy 中转损耗，因此更适用于性能敏感应用&lt;/li>
&lt;li>更有利于遗留系统的平滑迁移&lt;/li>
&lt;li>架构简单，容易运维部署&lt;/li>
&lt;li>适用于几乎所有的部署环境&lt;/li>
&lt;/ul>
&lt;h2 id="服务发现">服务发现&lt;/h2>
&lt;p>xDS 接入以注册中心的模式对接，节点发现同其他注册中心的服务自省模型一样，对于 xDS 的负载均衡和路由配置通过 ServiceInstance 的动态运行时配置传出，
在构建 Invoker 的时候将配置参数传入配置地址。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/3.png" alt="服务发现">&lt;/p>
&lt;h2 id="证书管理">证书管理&lt;/h2>
&lt;p>零信任架构下，需要严格区分工作负载的识别和信任，而签发 X.509 证书是推荐的一种认证方式。在 Kubernetes 集群中，服务间是通过 DNS 名称互相访问的，而网络流量可能被 DNS 欺骗、BGP/路由劫持、ARP 欺骗等手段劫持，为了将服务名称（DNS 名称）与服务身份强关联起来，Istio 使用置于 X.509 证书中的安全命名机制。SPIFFE是 Istio 所采用的安全命名的规范，它也是云原生定义的一种标准化的、可移植的工作负载身份规范。&lt;/p>
&lt;p>Secure Production Identity Framework For Everyone (SPIFFE) 是一套服务之间相互进行身份识别的标准，主要包含以下内容：&lt;/p>
&lt;ul>
&lt;li>SPIFFE ID 标准，SPIFFE ID 是服务的唯一标识，具体实现使用 URI 资源标识符&lt;/li>
&lt;li>SPIFFE Verifiable Identity Document (SVID) 标准，将 SPIFFE ID 编码到一个加密的可验证的数据格式中&lt;/li>
&lt;li>颁发与撤销 SVID 的 API 标准（SVID 是 SPIFFE ID 的识别凭证）&lt;/li>
&lt;/ul>
&lt;p>SPIFFE ID 规定了形如 &lt;code>spiffe://&amp;lt;trust domain&amp;gt;/&amp;lt;workload identifier&amp;gt;&lt;/code> 的 URI 格式，作为工作负载（Workload）的唯一标识。
而 Istio 在自身的生态中只使用到了 SPIFFE ID 作为安全命名，其数据格式由自己实现，通信格式采用 CNCF 支持的 xDS 协议规范（证书认证通信更具体来说是 xDS 的 SDS）。&lt;/p>
&lt;p>Istio 使用形如 &lt;code>spiffe://&amp;lt;trust_domain&amp;gt;/ns/&amp;lt;namespace&amp;gt;/sa/&amp;lt;service_account&amp;gt;&lt;/code> 格式的 SPIFFE ID 作为安全命名，注入到 X.509 证书的 subjectAltName 扩展中。
其中&amp;quot;trust_domain&amp;quot;参数通过 Istiod 环境变量TRUST_DOMAIN 注入，用于在多集群环境中交互。&lt;/p>
&lt;p>以下是 Dubbo Proxyless Mesh 证书颁发的过程&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/4.png" alt="证书颁发">&lt;/p>
&lt;ul>
&lt;li>创建 RSA 私钥（Istio 还不支持 ECDSA 私钥）&lt;/li>
&lt;li>构建 CSR（Certificate signing request）模板&lt;/li>
&lt;li>自签名 CSR 生成证书&lt;/li>
&lt;li>创建 Kubernetes Secret 资源储存 CA 证书和私钥（CA Service处理）&lt;/li>
&lt;/ul>
&lt;h2 id="案例实践">案例实践&lt;/h2>
&lt;p>接下来我将带领大家通过一个例子使已有的项目快速跑在 Proxyless Mesh 模式下。&lt;/p>
&lt;h2 id="环境准备">环境准备&lt;/h2>
&lt;h3 id="安装docker">安装docker&lt;/h3>
&lt;p>&lt;a href="https://www.docker.com/">https://www.docker.com/&lt;/a>&lt;/p>
&lt;h3 id="安装minikube">安装minikube&lt;/h3>
&lt;p>墙裂推荐：&lt;a href="https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/">https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/&lt;/a>&lt;/p>
&lt;h3 id="安装istio">安装istio&lt;/h3>
&lt;p>&lt;a href="https://istio.io/latest/docs/setup/getting-started/">https://istio.io/latest/docs/setup/getting-started/&lt;/a>&lt;/p>
&lt;p>❗❗❗ 安装 Istio 的时候需要开启 first-party-jwt 支持（使用 istioctl 工具安装的时候加上 &amp;ndash;set values.global.jwtPolicy=first-party-jwt 参数），否则将导致客户端认证失败的问题。
参考命令如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>curl -L https://istio.io/downloadIstio | sh -
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">cd&lt;/span> istio-1.xx.x
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">PATH&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#268bd2">$PWD&lt;/span>/bin:&lt;span style="color:#268bd2">$PATH&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>istioctl install --set &lt;span style="color:#268bd2">profile&lt;/span>&lt;span style="color:#719e07">=&lt;/span>demo --set values.global.jwtPolicy&lt;span style="color:#719e07">=&lt;/span>first-party-jwt -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="代码准备">代码准备&lt;/h2>
&lt;h3 id="xds-provider">xds-provider&lt;/h3>
&lt;h4 id="定义一个接口">定义一个接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">GreetingService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现对应的接口">实现对应的接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@DubboService&lt;/span>&lt;span style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AnnotatedGreetingService&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> GreetingService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;greeting service received: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hello, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;! from host: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> NetUtils&lt;span style="color:#719e07">.&lt;/span>getLocalHost&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写启动类">编写启动类&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">(&lt;/span>ProviderConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo service started&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> CountDownLatch&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">).&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@EnableDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>scanBasePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.impl&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@PropertySource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;classpath:/spring/dubbo-provider.properties&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写配置信息">编写配置信息&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo.application.name=dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 由于 Dubbo 3 应用级服务发现的元数据无法从 istio 中获取，需要走服务自省模式。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 这要求了 Dubbo MetadataService 的端口在全集群的是统一的。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.metadataServicePort=20885
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 走xds协议
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address=xds://istiod.istio-system.svc:15012
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.name=tri
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.port=50051
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 对齐k8s pod生命周期，由于 Kubernetes probe 探活机制的工作原理限制，
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 探活请求的发起方不是 localhost，所以需要配置 qosAcceptForeignIp 参数开启允许全局访问
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosEnable=true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosAcceptForeignIp=true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写deploymentyml和serviceyml">编写Deployment.yml和Service.yml&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> replicas: 3
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> matchLabels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> template:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> labels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> containers:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> image: apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> livenessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /live
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> readinessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /ready
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startupProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /startup
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failureThreshold: 30
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 10
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clusterIP: None
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ports:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: grpc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol: TCP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 50051
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targetPort: 50051
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写dockerfile">编写Dockerfile&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>FROM openjdk:8-jdk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ADD ./target/dubbo-samples-xds-provider-1.0-SNAPSHOT.jar dubbo-samples-xds-provider-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-provider-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="xds-consumer">xds-consumer&lt;/h3>
&lt;h4 id="定义一个接口-1">定义一个接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public interface GreetingService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String sayHello(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现对应的接口-1">实现对应的接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>@Component(&amp;#34;annotatedConsumer&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class GreetingServiceConsumer {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 这里特别注意的是、由于当前 Dubbo 版本受限于 istio 的通信模型无法获取接口所对应的应用名，
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 因此需要配置 providedBy 参数来标记此服务来自哪个应用。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @DubboReference(version = &amp;#34;1.0.0&amp;#34;, providedBy = &amp;#34;dubbo-samples-xds-provider&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private GreetingService greetingService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public String doSayHello(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return greetingService.sayHello(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写启动类-1">编写启动类&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public class ConsumerBootstrap {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public static void main(String[] args) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> GreetingServiceConsumer greetingServiceConsumer = context.getBean(GreetingServiceConsumer.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> while (true) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> try {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String hello = greetingServiceConsumer.doSayHello(&amp;#34;xDS Consumer&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&amp;#34;result: &amp;#34; + hello);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread.sleep(100);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } catch (Throwable t) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t.printStackTrace();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Configuration
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @EnableDubbo(scanBasePackages = &amp;#34;org.apache.dubbo.samples.action&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @PropertySource(&amp;#34;classpath:/spring/dubbo-consumer.properties&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @ComponentScan(value = {&amp;#34;org.apache.dubbo.samples.action&amp;#34;})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> static class ConsumerConfiguration {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写配置信息-1">编写配置信息&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo.application.name=dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.metadataServicePort=20885
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address=xds://istiod.istio-system.svc:15012
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.consumer.timeout=3000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.consumer.check=false
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosEnable=true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosAcceptForeignIp=true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写deploymentyml和serviceyml-1">编写Deployment.yml和Service.yml&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> replicas: 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> matchLabels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> template:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> labels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> containers:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> image: apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> livenessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /live
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> readinessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /ready
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startupProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /startup
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failureThreshold: 30
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 10
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clusterIP: None
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ports:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: grpc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol: TCP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 50051
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targetPort: 50051
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写dockerfile-1">编写Dockerfile&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>FROM openjdk:8-jdk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ADD ./target/dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>✅ 到目前为止我们的环境和代码就全都准备完毕了！&lt;/p>
&lt;h2 id="构建镜像">构建镜像&lt;/h2>
&lt;h3 id="1启动docker">1、启动docker&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/5.png" alt="启动docker">&lt;/p>
&lt;h3 id="2启动minikube">2、启动minikube&lt;/h3>
&lt;p>因为minikube是一个本地的k8s，他启动需要一个虚拟引擎，这里我们用docker来管理。我们通过如下命令启动&lt;/p>
&lt;p>&lt;code>minikube start&lt;/code>&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/6.png" alt="启动minikube">&lt;/p>
&lt;p>我们可以在docker里看到minikube&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/7.png" alt="minikube">&lt;/p>
&lt;h3 id="3检查istio的状态">3、检查istio的状态&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/8.png" alt="istio的状态">&lt;/p>
&lt;h3 id="4构建镜像">4、构建镜像&lt;/h3>
&lt;p>在本地找到代码所在位置、依次执行以下命令&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到provider所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ./dubbo-samples-xds-provider/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 构建provider的镜像
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker build -t apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/9.png" alt="构建provider">&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到consumer所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ../dubbo-samples-xds-consumer/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 构建consumer的镜像
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker build -t apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/10.png" alt="构建consumer">&lt;/p>
&lt;h3 id="5检查本地镜像">5、检查本地镜像&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/11.png" alt="检查本地镜像">&lt;/p>
&lt;h3 id="6创建namespace">6、创建namespace&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 初始化命名空间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/dubbo-samples-xds/deploy/Namespace.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 切换命名空间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubens dubbo-demo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果不创建namespace，那么会看到如下错误&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/12.png" alt="错误">&lt;/p>
&lt;h2 id="部署容器">部署容器&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到provider所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ./dubbo-samples-xds-provider/src/main/resources/k8s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Deployment.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Service.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 部署provider的Deployment和Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f Deployment.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f Service.yml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/13.png" alt="部署provider">&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到consumer所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ../../../../../dubbo-samples-xds-consumer/src/main/resources/k8s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># dubbo-samples-xds/dubbo-samples-xds-consumer/src/main/resources/k8s/Deployment.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 部署consumer的Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f Deployment.yml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/14.png" alt="部署provider">&lt;/p>
&lt;p>在minikube dashboard看到我们已经部署的pod&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/15.png" alt="部署provider">&lt;/p>
&lt;h2 id="观察consumer效果">观察consumer效果&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>kubectl logs xxx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.6
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.6
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="总结展望">总结&amp;amp;展望&lt;/h2>
&lt;p>本文主要剖析了 Dubbo Proxyless Mesh 的架构、服务发现以及证书管理等核心流程，最后通过示例给大家演示了如何使用 Dubbo Proxyless。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/16.png" alt="部署provider">&lt;/p>
&lt;p>随着 Dubbo 3.1 的 release，Dubbo 在云原生的路上又迈出了重要的一步。在今年年底，Dubbo Mesh 将发布具有服务发现能力的版本，
届时将面向所有 Dubbo 用户提供从低版本平滑迁移到 Mesh 架构的能力；在明年年初春季的时候将发布带有治理能力的版本；在明年年底前发布带热插件更新能力的版本，
希望有兴趣见证 Dubbo 云原生之路的同学可以积极参与社区贡献！&lt;/p>
&lt;p>更多关于 Dubbo Mesh 的动态可以关注 Apache Dubbo 社区官方公众号（ApacheDubbo），及时获取最新消息。&lt;/p></description></item><item><title>Blog: 22-Dubbo3消费者自动感应决策应用级服务发现原理</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="22-dubbo3消费者自动感应决策应用级服务发现原理">22-Dubbo3消费者自动感应决策应用级服务发现原理&lt;/h1>
&lt;h2 id="221-简介">22.1 简介&lt;/h2>
&lt;p>这里要说的内容对Dubbo2迁移到Dubbo3的应用比较有帮助，消费者应用级服务发现做了一些自动决策的逻辑来决定当前消费者是应用级发现还是接口级服务发现，这里与前面说的提供者双注册的原理是对等的，提供者默认同时进行应用级注册和接口级注册，消费者对提供者注册的数据来决定使用应用级发现或者接口级发现。这些都是默认的行为，当然对于消费者来说还可以自定义其他的迁移规则，具体的需要我们详细来看逻辑。&lt;/p>
&lt;p>如果说对于迁移过程比较感兴趣可以直接去官网看文档相对来说还是比较清晰:&lt;a href="https://dubbo.apache.org/zh/docs/migration/migration-service-discovery/">https://dubbo.apache.org/zh/docs/migration/migration-service-discovery/&lt;/a>&lt;/p>
&lt;p>这里再借官网的图来用用，迁移过程主要如下所示：
第一个图是提供者双注册的图：
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/provider-registration.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>第二个图是消费者订阅决策的图：
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/consumer-subscription.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>第三个图就是精确到消费者订阅的代码层的逻辑了，消费者服务间调用通过一个Invoker类型对象来进行对象，如下图所示消费者代理对象通过创建一个迁移容错的调用器对象来对应用级或者接口级订阅进行适配如下所示
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/migration-cluster-invoker.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>第二个图和第三个图是重点要关注的这一个文章的内容主要就是说这里的逻辑。&lt;/p>
&lt;p>关于代码位置如果不知道是如何调用到这一块逻辑的可以查看博文&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/21-dubbo-xiao-fei-zhe-yin-yong-fu-wu-de-ru-kou/">《21-Dubbo3消费者引用服务入口》&lt;/a>&lt;/p>
&lt;p>这里直接将代码位置定位到：RegistryProtocol类型的interceptInvoker方法中：
如下所示：&lt;/p>
&lt;p>RegistryProtocol类型的interceptInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">interceptInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> URL consumerUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//目前存在的扩展类型为RegistryProtocolListener监听器的实现类型MigrationRuleListener
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryProtocolListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> listeners &lt;span style="color:#719e07">=&lt;/span> findRegistryProtocolListeners&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>listeners&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RegistryProtocolListener listener &lt;span style="color:#719e07">:&lt;/span> listeners&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listener&lt;span style="color:#719e07">.&lt;/span>onRefer&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该方法尝试加载所有RegistryProtocolListener定义，这些定义通过与定义的交互来控制调用器的行为，然后使用这些侦听器更改MigrationInvoker的状态和行为。当前可用的监听器是MigrationRuleListener，用于通过动态变化的规则控制迁移行为。&lt;/p>
&lt;h2 id="222-migrationrulelistener-类型的onrefer方法">22.2 MigrationRuleListener 类型的onRefer方法&lt;/h2>
&lt;p>直接来看代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onRefer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RegistryProtocol registryProtocol&lt;span style="color:#719e07">,&lt;/span> ClusterInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> URL consumerUrl&lt;span style="color:#719e07">,&lt;/span> URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个对应invoker对象的MigrationRuleHandler类型对象 然后将其存放在缓存Map&amp;lt;MigrationInvoker, MigrationRuleHandler&amp;gt;类型对象handles中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MigrationRuleHandler&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> migrationRuleHandler &lt;span style="color:#719e07">=&lt;/span> handlers&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">((&lt;/span>MigrationInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;)&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> _key &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">((&lt;/span>MigrationInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;)&lt;/span> invoker&lt;span style="color:#719e07">).&lt;/span>setMigrationRuleListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MigrationRuleHandler&lt;span style="color:#719e07">&amp;lt;&amp;gt;((&lt;/span>MigrationInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;)&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//迁移规则执行 rule是封装了迁移的配置规则的信息对应类型MigrationRule类型，在初始化对象的时候进行了配置初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> migrationRuleHandler&lt;span style="color:#719e07">.&lt;/span>doMigrate&lt;span style="color:#719e07">(&lt;/span>rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于这个igrationRule的文可以直接看官方的文档比较详细：&lt;a href="https://dubbo.apache.org/zh/docs/v3.0/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">地址迁移规则说明&lt;/a>&lt;/p>
&lt;p>这个迁移规则是为了更细粒度的迁移决策：
相关配置可以参考下面这个样例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">key&lt;/span>: 消费者应用名（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">step&lt;/span>: 状态名（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">threshold&lt;/span>: 决策阈值（默认1.0）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">proportion&lt;/span>: 灰度比例（默认100）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">delay&lt;/span>: 延迟决策时间（默认0）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">force&lt;/span>: 强制切换（默认 false）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">interfaces&lt;/span>: 接口粒度配置（可选）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">serviceKey: 接口名（接口 + &lt;/span>: + 版本号）（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">threshold&lt;/span>: 决策阈值
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">proportion&lt;/span>: 灰度比例
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">delay&lt;/span>: 延迟决策时间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: 强制切换
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: 状态名（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">serviceKey: 接口名（接口 + &lt;/span>: + 版本号）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: 状态名
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">applications&lt;/span>: 应用粒度配置（可选）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">serviceKey&lt;/span>: 应用名（消费的上游应用名）（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">threshold&lt;/span>: 决策阈值
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">proportion&lt;/span>: 灰度比例
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">delay&lt;/span>: 延迟决策时间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: 强制切换
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: 状态名（必填）
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>不过为了简单起见暂时先不详细说这个配置细节，我们继续往下看&lt;/p>
&lt;h2 id="223-迁移规则处理器执行迁移规则migrationrulehandler类型的domigrate方法">22.3 迁移规则处理器执行迁移规则MigrationRuleHandler类型的doMigrate方法&lt;/h2>
&lt;h3 id="2231-迁移规则的模版方法">22.3.1 迁移规则的模版方法：&lt;/h3>
&lt;p>MigrationRuleHandler类型的doMigrate方法代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doMigrate&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MigrationRule rule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//默认情况下这个类型是MigrationInvoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>migrationInvoker &lt;span style="color:#719e07">instanceof&lt;/span> ServiceDiscoveryMigrationInvoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> refreshInvoker&lt;span style="color:#719e07">(&lt;/span>MigrationStep&lt;span style="color:#719e07">.&lt;/span>FORCE_APPLICATION&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">1.0f&lt;/span>&lt;span style="color:#719e07">,&lt;/span> rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//迁移步骤，MigrationStep 一共有3种枚举情况：FORCE_INTERFACE, APPLICATION_FIRST, FORCE_APPLICATION
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// initial step : APPLICATION_FIRST
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MigrationStep step &lt;span style="color:#719e07">=&lt;/span> MigrationStep&lt;span style="color:#719e07">.&lt;/span>APPLICATION_FIRST&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">float&lt;/span> threshold &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1f&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取配置的类型 默认走APPLICATION_FIRST
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> step &lt;span style="color:#719e07">=&lt;/span> rule&lt;span style="color:#719e07">.&lt;/span>getStep&lt;span style="color:#719e07">(&lt;/span>consumerURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//threshold: 决策阈值（默认-1.0）计算与获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> threshold &lt;span style="color:#719e07">=&lt;/span> rule&lt;span style="color:#719e07">.&lt;/span>getThreshold&lt;span style="color:#719e07">(&lt;/span>consumerURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to get step and threshold info from rule: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> rule&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷洗调用器对象 来进行决策服务发现模式
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>refreshInvoker&lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">,&lt;/span> threshold&lt;span style="color:#719e07">,&lt;/span> rule&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// refresh success, update rule
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> setMigrationRule&lt;span style="color:#719e07">(&lt;/span>rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2232-服务发现调用器对象的选择决策服务发现策略">22.3.2 服务发现调用器对象的选择（决策服务发现策略）&lt;/h3>
&lt;p>这里就是关键代码了：通过迁移配置和当前提供者注册信息来决定创建什么类型的调用器对象（Invoker)来为后续服务调用做准备&lt;/p>
&lt;p>MigrationRuleHandler的refreshInvoker，注意默认情况下这个step参数为APPLICATION_FIRST&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">refreshInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MigrationStep step&lt;span style="color:#719e07">,&lt;/span> Float threshold&lt;span style="color:#719e07">,&lt;/span> MigrationRule newRule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>step &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> threshold &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Step or threshold of migration rule cannot be null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MigrationStep originStep &lt;span style="color:#719e07">=&lt;/span> currentStep&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">((&lt;/span>currentStep &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> currentStep &lt;span style="color:#719e07">!=&lt;/span> step&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> &lt;span style="color:#719e07">!&lt;/span>currentThreshold&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>threshold&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> success &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">switch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> APPLICATION_FIRST&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//默认和配置了应用级优先的服务发现则走这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> migrationInvoker&lt;span style="color:#719e07">.&lt;/span>migrateToApplicationFirstInvoker&lt;span style="color:#719e07">(&lt;/span>newRule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> FORCE_APPLICATION&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置了应用级服务发现则走这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> success &lt;span style="color:#719e07">=&lt;/span> migrationInvoker&lt;span style="color:#719e07">.&lt;/span>migrateToForceApplicationInvoker&lt;span style="color:#719e07">(&lt;/span>newRule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> FORCE_INTERFACE&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置了接口级服务发现则走这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">default&lt;/span>&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> success &lt;span style="color:#719e07">=&lt;/span> migrationInvoker&lt;span style="color:#719e07">.&lt;/span>migrateToForceInterfaceInvoker&lt;span style="color:#719e07">(&lt;/span>newRule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>success&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setCurrentStepAndThreshold&lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">,&lt;/span> threshold&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Succeed Migrated to &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> step &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; mode. Service Name: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> consumerURL&lt;span style="color:#719e07">.&lt;/span>getDisplayServiceKey&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> report&lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">,&lt;/span> originStep&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// migrate failed, do not save new step and rule
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Migrate to &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> step &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; mode failed. Probably not satisfy the threshold you set &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+&lt;/span> threshold &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;. Please try re-publish configuration if you still after check.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> report&lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">,&lt;/span> originStep&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> success&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore if step is same with previous, will continue override rule for MigrationInvoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到这个代码做了判断的逻辑分别对应了Dubbo3消费者迁移的一个状态逻辑：
三种状态分别如下枚举类型：
当前共存在三种状态，&lt;/p>
&lt;ul>
&lt;li>FORCE_INTERFACE（强制接口级）&lt;/li>
&lt;li>APPLICATION_FIRST（应用级优先）&lt;/li>
&lt;li>FORCE_APPLICATION（强制应用级）&lt;/li>
&lt;/ul>
&lt;p>通过代码我们可以看到默认情况下都会走APPLICATION_FIRST（应用级优先）的策略，这里我们也重点来说 APPLICATION_FIRST（应用级优先）来看下Dubbo3是如何决策使用接口级还是应用级发现模型来兼容迁移的服务的。&lt;/p>
&lt;h3 id="2233-应用级优先的服务发现规则逻辑">22.3.3 应用级优先的服务发现规则逻辑&lt;/h3>
&lt;p>这个规则就是智能选择应用级还是接口级的代码了，对应类型为MigrationInvoker的migrateToApplicationFirstInvoker方法，接下来我们详细看下：&lt;/p>
&lt;p>MigrationInvoker类型的migrateToApplicationFirstInvoker方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">migrateToApplicationFirstInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MigrationRule newRule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CountDownLatch latch &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CountDownLatch&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新接口级服务发现Invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> refreshInterfaceInvoker&lt;span style="color:#719e07">(&lt;/span>latch&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新应用级服务发现Invoker类型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> refreshServiceDiscoveryInvoker&lt;span style="color:#719e07">(&lt;/span>latch&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// directly calculate preferred invoker, will not wait until address notify
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// calculation will re-occurred when address notify later
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//计算当前使用应用级还是接口级服务发现的Invoker对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> calcPreferredInvoker&lt;span style="color:#719e07">(&lt;/span>newRule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2234-刷新接口级服务发现invoker">22.3.4 刷新接口级服务发现Invoker&lt;/h3>
&lt;p>MigrationInvoker类型的refreshInterfaceInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refreshInterfaceInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>CountDownLatch latch&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clearListener&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>needRefresh&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isDebugEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>debug&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Re-subscribing interface addresses for interface &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>invoker &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker&lt;span style="color:#719e07">.&lt;/span>destroy&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker &lt;span style="color:#719e07">=&lt;/span> registryProtocol&lt;span style="color:#719e07">.&lt;/span>getInvoker&lt;span style="color:#719e07">(&lt;/span>cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setListener&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> latch&lt;span style="color:#719e07">.&lt;/span>countDown&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>reportService&lt;span style="color:#719e07">.&lt;/span>hasReporter&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportService&lt;span style="color:#719e07">.&lt;/span>reportConsumptionStatus&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportService&lt;span style="color:#719e07">.&lt;/span>createConsumptionReport&lt;span style="color:#719e07">(&lt;/span>consumerUrl&lt;span style="color:#719e07">.&lt;/span>getServiceInterface&lt;span style="color:#719e07">(),&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getVersion&lt;span style="color:#719e07">(),&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#2aa198">&amp;#34;interface&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>step &lt;span style="color:#719e07">==&lt;/span> APPLICATION_FIRST&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> calcPreferredInvoker&lt;span style="color:#719e07">(&lt;/span>rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2235-刷新应用级服务发现invoker类型对象">22.3.5 刷新应用级服务发现Invoker类型对象&lt;/h3>
&lt;p>MigrationInvoker类型的refreshServiceDiscoveryInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refreshServiceDiscoveryInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>CountDownLatch latch&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clearListener&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>needRefresh&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isDebugEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>debug&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Re-subscribing instance addresses, current interface &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceDiscoveryInvoker&lt;span style="color:#719e07">.&lt;/span>destroy&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceDiscoveryInvoker &lt;span style="color:#719e07">=&lt;/span> registryProtocol&lt;span style="color:#719e07">.&lt;/span>getServiceDiscoveryInvoker&lt;span style="color:#719e07">(&lt;/span>cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setListener&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> latch&lt;span style="color:#719e07">.&lt;/span>countDown&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>reportService&lt;span style="color:#719e07">.&lt;/span>hasReporter&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportService&lt;span style="color:#719e07">.&lt;/span>reportConsumptionStatus&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportService&lt;span style="color:#719e07">.&lt;/span>createConsumptionReport&lt;span style="color:#719e07">(&lt;/span>consumerUrl&lt;span style="color:#719e07">.&lt;/span>getServiceInterface&lt;span style="color:#719e07">(),&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getVersion&lt;span style="color:#719e07">(),&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#2aa198">&amp;#34;app&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>step &lt;span style="color:#719e07">==&lt;/span> APPLICATION_FIRST&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> calcPreferredInvoker&lt;span style="color:#719e07">(&lt;/span>rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2236-计算当前使用应用级还是接口级服务发现的invoker对象">22.3.6 计算当前使用应用级还是接口级服务发现的Invoker对象&lt;/h3>
&lt;p>MigrationInvoker类型的的calcPreferredInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">calcPreferredInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MigrationRule migrationRule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> invoker &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>MigrationAddressComparator&lt;span style="color:#719e07">&amp;gt;&lt;/span> detectors &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">(&lt;/span>consumerUrl &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">:&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>MigrationAddressComparator&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>detectors&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// pick preferred invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// the real invoker choice in invocation will be affected by promotion
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>detectors&lt;span style="color:#719e07">.&lt;/span>stream&lt;span style="color:#719e07">().&lt;/span>allMatch&lt;span style="color:#719e07">(&lt;/span>comparator &lt;span style="color:#719e07">-&amp;gt;&lt;/span> comparator&lt;span style="color:#719e07">.&lt;/span>shouldMigrate&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> migrationRule&lt;span style="color:#719e07">)))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>currentAvailableInvoker &lt;span style="color:#719e07">=&lt;/span> serviceDiscoveryInvoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>currentAvailableInvoker &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>currentAvailableInvoker是后期服务调用使用的Invoker对象&lt;/p>
&lt;p>原文地址：&lt;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消费者自动感应决策应用级服务发现原理&lt;/a>&lt;/p></description></item><item><title>Blog: 21-Dubbo3消费者引用服务入口</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="21-dubbo3消费者引用服务入口">21-Dubbo3消费者引用服务入口&lt;/h1>
&lt;h2 id="211-简介">21.1 简介&lt;/h2>
&lt;p>前面我们通过Demo说了一个服务引用配置的创建。另外也在前面的文章说了服务提供者的启动完整过程，不过在说服务提供者启动的过程中并未提到服务消费者是如何发现服务，如果调用服务的，这里先就不再说关于服务消费者启动的一个细节了，直接来看前面未提到的服务消费者是如何引用到服务提供者提供的服务的。
先来回顾下样例代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ConsumerApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> runWithBootstrap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">runWithBootstrap&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setGeneric&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setQosEnable&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setQosPort&lt;span style="color:#719e07">(-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>applicationConfig&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://8.131.79.126:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>reference&lt;span style="color:#719e07">(&lt;/span>reference&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DemoService demoService &lt;span style="color:#719e07">=&lt;/span> bootstrap&lt;span style="color:#719e07">.&lt;/span>getCache&lt;span style="color:#719e07">().&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>reference&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String message &lt;span style="color:#719e07">=&lt;/span> demoService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// generic invoke
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GenericService genericService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>GenericService&lt;span style="color:#719e07">)&lt;/span> demoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object genericInvokeResult &lt;span style="color:#719e07">=&lt;/span> genericService&lt;span style="color:#719e07">.&lt;/span>$invoke&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]{&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()},&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo generic invoke&amp;#34;&lt;/span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>genericInvokeResult&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这段代码我们前面详细说了服务引用的配置ReferenceConfig和Dubbo启动器启动应用的过程DubboBootstrap，后面我们直接定位到消费者引用服务的代码位置来看。&lt;/p>
&lt;h2 id="212-入口代码">21.2 入口代码&lt;/h2>
&lt;h3 id="2121-defaultmoduledeployer的start方法">21.2.1 DefaultModuleDeployer的start方法&lt;/h3>
&lt;p>第一个要关注的就是模块发布器DefaultModuleDeployer的start方法，这个start方法包含了Dubbo应用启动的过程&lt;/p>
&lt;p>DefaultModuleDeployer的start方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> Future &lt;span style="color:#268bd2">start&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>省略掉若干代码
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onModuleStarting&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// initialize
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// export services
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> exportServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// prepare application instance
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// exclude internal module to avoid wait itself
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>moduleModel &lt;span style="color:#719e07">!=&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">().&lt;/span>getInternalModule&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>prepareInternalModule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// refer services
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>省略掉若干代码
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> startFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个方法大部分代码已经省略，也不会详细去说了，感兴趣的可以看之前讲到的博客，这里主要来看引用服务方法referServices&lt;/p>
&lt;h3 id="2122-defaultmoduledeployer的referservices方法">21.2.2 DefaultModuleDeployer的referServices方法&lt;/h3>
&lt;p>下面就要来看消费者应用如何引用的服务的入口了，这个方法主要从大的方面做了一些服务引用生命周期的代码，看懂了这个方法我们就可以不依赖Dubbo负载的启动逻辑可以单独调用ReferenceConfigBase类型的对应方法来刷新，启动，销毁引用的服务了这里先来看下代码再详细介绍内容：&lt;/p>
&lt;p>DefaultModuleDeployer的referServices方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">referServices&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个是获取配置的所有的ReferenceConfigBase类型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getReferences&lt;span style="color:#719e07">().&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>rc &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> referenceConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ReferenceConfig&lt;span style="color:#719e07">&amp;lt;?&amp;gt;)&lt;/span> rc&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>referenceConfig&lt;span style="color:#719e07">.&lt;/span>isRefreshed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新引用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referenceConfig&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">.&lt;/span>shouldInit&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>referAsync &lt;span style="color:#719e07">||&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>shouldReferAsync&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExecutorService executor &lt;span style="color:#719e07">=&lt;/span> executorRepository&lt;span style="color:#719e07">.&lt;/span>getServiceReferExecutor&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>Void&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>runAsync&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//间接的通过缓存对象来引用服务配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referenceCache&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; refer async catch error : &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">},&lt;/span> executor&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncReferringFutures&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>future&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//间接的通过缓存对象来引用服务配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referenceCache&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; refer catch error.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//出现异常销毁引用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referenceCache&lt;span style="color:#719e07">.&lt;/span>destroy&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> t&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这个代码中我们核心需要关心的就是SimpleReferenceCache类型的get方法了，在获取服务对象之外包装了一层缓存。&lt;/p>
&lt;p>如果出现了异常则执行referenceCache的destroy方法进行销毁引用配置。&lt;/p>
&lt;h2 id="213-开始引用服务">21.3 开始引用服务&lt;/h2>
&lt;h3 id="2131-simplereferencecache是什么">21.3.1 SimpleReferenceCache是什么？&lt;/h3>
&lt;p>一个用于缓存引用ReferenceConfigBase的util工具类。
ReferenceConfigBase是一个重对象，对于频繁创建ReferenceConfigBase的框架来说，有必要缓存这些对象。
如果需要使用复杂的策略，可以实现并使用自己的ReferenceConfigBase缓存
这个Cache是引用服务的开始如果我们想在代码中自定义一些服务引用的逻辑，可以直接创建SimpleReferenceCache类型对象然后调用其get方法进行引用服务。那这个缓存对象是和缓存与引用服务的可以继续往下看。&lt;/p>
&lt;h3 id="2132-引用服务之前的缓存处理逻辑">21.3.2 引用服务之前的缓存处理逻辑？&lt;/h3>
&lt;p>关于逻辑的处理，看代码有时候比文字更清晰明了，这里可以直接来看 SimpleReferenceCache类型的get方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@SuppressWarnings&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;unchecked&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T &lt;span style="color:#268bd2">get&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ReferenceConfigBase&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> rc&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个生成的key规则是这样的 服务分组/服务接口:版本号 详细的代码就不看了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//例如： group/link.elastic.dubbo.entity.DemoService:1.0
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> generator&lt;span style="color:#719e07">.&lt;/span>generateKey&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务类型 如果是泛化调用则这个类型为GenericService
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> type &lt;span style="color:#719e07">=&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>getInterfaceClass&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务是否为单例的这里默认值都为空，为单例模式
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> singleton &lt;span style="color:#719e07">=&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>getSingleton&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>getSingleton&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> T proxy &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Check existing proxy of the same &amp;#39;key&amp;#39; and &amp;#39;type&amp;#39; first.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>singleton&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//一般为单例的 这个方法是从缓存中获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> proxy &lt;span style="color:#719e07">=&lt;/span> get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//非单例容易造成内存泄露，无法从缓存中获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Using non-singleton ReferenceConfig and ReferenceCache at the same time may cause memory leak. &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;Call ReferenceConfig#get() directly for non-singleton ReferenceConfig instead of using ReferenceCache#get(ReferenceConfig)&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//前面是从缓存中拿，如果缓存中获取不到则开始引用服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>proxy &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取或者创建值，为引用类型referencesOfType对象（类型为Map&amp;lt;Class&amp;lt;?&amp;gt;, List&amp;lt;ReferenceConfigBase&amp;lt;?&amp;gt;&amp;gt;&amp;gt;）缓存对象生成值（值不存咋时候会生成一个）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ReferenceConfigBase&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> referencesOfType &lt;span style="color:#719e07">=&lt;/span> referenceTypeMap&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> _t &lt;span style="color:#719e07">-&amp;gt;&lt;/span> Collections&lt;span style="color:#719e07">.&lt;/span>synchronizedList&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//每次走到这里都会添加一个ReferenceConfigBase 引用配置对象（单例的从缓存中拿到就可以直接返回了）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referencesOfType&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//与前面一样 前面是类型映射，这里是key映射
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ReferenceConfigBase&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> referenceConfigList &lt;span style="color:#719e07">=&lt;/span> referenceKeyMap&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> _k &lt;span style="color:#719e07">-&amp;gt;&lt;/span> Collections&lt;span style="color:#719e07">.&lt;/span>synchronizedList&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfigList&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//开始引用服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> proxy &lt;span style="color:#719e07">=&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> proxy&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到这个逻辑使用了享元模式（其实就是先查缓存，缓存不存在则创建对象存入缓存）来进行引用对象的管理这样一个过程，这里一共有两个缓存对象referencesOfType和referenceConfigList
key分别为引用类型和引用的服务的key，值是引用服务的基础配置对象列表List&amp;lt;ReferenceConfigBase&lt;?>&amp;gt;&lt;/p>
&lt;p>后面可以详细看下如果借助ReferenceConfigBase类型对象来进行具体类型的引用。&lt;/p>
&lt;h2 id="214-初始化引用服务的过程">21.4 初始化引用服务的过程&lt;/h2>
&lt;h3 id="2141-初始化引用服务的调用入口">21.4.1 初始化引用服务的调用入口&lt;/h3>
&lt;p>引用服务的逻辑其实是相对复杂一点的，包含了服务发现，引用对象的创建等等，接下来就让我们详细看下:&lt;/p>
&lt;p>ReferenceConfig类型的get方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">get&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>destroyed&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;The invoker of ReferenceConfig(&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) has already destroyed!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//ref类型为 transient volatile T ref;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ref &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ensure start module, compatible with old api usage
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这个前面已经调用了模块发布器启动过了，这里有这么一行代码是有一定作用的，如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化下面执行逻辑的时候出现问题，这个代码其实就是启动模块进行一些基础配置的初始化操作 比如元数据中心默认配置选择，注册中心默认配置选择这些都是比较重要的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> getScopeModel&lt;span style="color:#719e07">().&lt;/span>getDeployer&lt;span style="color:#719e07">().&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ref &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> init&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ref&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里有一段代码是：getScopeModel().getDeployer().start();
这个前面已经调用了模块发布器启动过了，这里有这么一行代码是有一定作用的，如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化下面执行逻辑的时候出现问题，这个代码其实就是启动模块进行一些基础配置的初始化操作 比如元数据中心默认配置选择，注册中心默认配置选择这些都是比较重要的。&lt;/p>
&lt;p>另外可以看到的是这里使用了双重校验锁来保证单例对象的创建，发现Dubbo种大量的使用了双重校验锁的逻辑。&lt;/p>
&lt;h3 id="2142-初始化引用服务">21.4.2 初始化引用服务&lt;/h3>
&lt;p>这个就直接看代码了这，初始化过程相对复杂一点，我们一点点来看
ReferenceConfig类型init()方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化标记变量保证只初始化一次，这里又是加锁🔐又是加标记变量的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialized &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>isRefreshed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init serviceMetadata
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//初始化ServiceMetadata类型对象serviceMetadata 为其设置服务基本属性比如版本号，分组，服务接口名
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initServiceMetadata&lt;span style="color:#719e07">(&lt;/span>consumer&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//继续初始化元数据信息 服务接口类型和key
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>setServiceType&lt;span style="color:#719e07">(&lt;/span>getServiceInterfaceClass&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// TODO, uncomment this line once service key is unified
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>setServiceKey&lt;span style="color:#719e07">(&lt;/span>URL&lt;span style="color:#719e07">.&lt;/span>buildKey&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置转Map类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> referenceParameters &lt;span style="color:#719e07">=&lt;/span> appendConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init service-application mapping
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//来自本地存储和url参数的初始化映射。 参数转URL配置初始化 Dubbo中喜欢用url作为配置的一种处理方式
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initServiceAppsMapping&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地内存模块服务存储库
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ModuleServiceRepository repository &lt;span style="color:#719e07">=&lt;/span> getScopeModel&lt;span style="color:#719e07">().&lt;/span>getServiceRepository&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//ServiceModel和ServiceMetadata在某种程度上是相互重复的。我们将来应该合并它们。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceDescriptor serviceDescriptor&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>NATIVE_STUB&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>getProxy&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceDescriptor &lt;span style="color:#719e07">=&lt;/span> StubSuppliers&lt;span style="color:#719e07">.&lt;/span>getServiceDescriptor&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> repository&lt;span style="color:#719e07">.&lt;/span>registerService&lt;span style="color:#719e07">(&lt;/span>serviceDescriptor&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地存储库注册服务接口类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceDescriptor &lt;span style="color:#719e07">=&lt;/span> repository&lt;span style="color:#719e07">.&lt;/span>registerService&lt;span style="color:#719e07">(&lt;/span>interfaceClass&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//消费者模型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> consumerModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConsumerModel&lt;span style="color:#719e07">(&lt;/span>serviceMetadata&lt;span style="color:#719e07">.&lt;/span>getServiceKey&lt;span style="color:#719e07">(),&lt;/span> proxy&lt;span style="color:#719e07">,&lt;/span> serviceDescriptor&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> getScopeModel&lt;span style="color:#719e07">(),&lt;/span> serviceMetadata&lt;span style="color:#719e07">,&lt;/span> createAsyncMethodInfo&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地存储库注册消费者模型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> repository&lt;span style="color:#719e07">.&lt;/span>registerConsumer&lt;span style="color:#719e07">(&lt;/span>consumerModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//与前面代码一样基础初始化服务元数据对象为其设置附加参数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>getAttachments&lt;span style="color:#719e07">().&lt;/span>putAll&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建服务的代理对象 ！！！核心代码在这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ref &lt;span style="color:#719e07">=&lt;/span> createProxy&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为服务元数据对象设置代理对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>setTarget&lt;span style="color:#719e07">(&lt;/span>ref&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>addAttribute&lt;span style="color:#719e07">(&lt;/span>PROXY_CLASS_REF&lt;span style="color:#719e07">,&lt;/span> ref&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerModel&lt;span style="color:#719e07">.&lt;/span>setProxyObject&lt;span style="color:#719e07">(&lt;/span>ref&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerModel&lt;span style="color:#719e07">.&lt;/span>initMethodModels&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查invoker对象初始结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkInvokerAvailable&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="215-referenceconfig创建服务引用代理对象的原理">21.5 ReferenceConfig创建服务引用代理对象的原理&lt;/h2>
&lt;h3 id="2151-代理对象的创建过程">21.5.1 代理对象的创建过程&lt;/h3>
&lt;p>这里就要继续看 ReferenceConfig类型的创建代理方法createProxy了
直接贴一下源码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> T &lt;span style="color:#268bd2">createProxy&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> referenceParameters&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地引用 这里为false
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>shouldJvmRefer&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createInvokerForLocal&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> urls&lt;span style="color:#719e07">.&lt;/span>clear&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//url存在则为点对点引用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// user specified URL, could be peer-to-peer address, or register center&amp;#39;s address.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> parseUrl&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if protocols not in jvm checkRegistry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这里不是local协议默认这里为空
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>LOCAL_PROTOCOL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>getProtocol&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从注册表中获取URL并将其聚合。这个其实就是初始化一下注册中心的url配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> aggregateUrlFromRegistry&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个代码非常重要 创建远程引用，创建远程引用调用器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> createInvokerForRemote&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Referred dubbo service: [&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> referenceParameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>INTERFACE_KEY&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;].&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">(&lt;/span>Boolean&lt;span style="color:#719e07">.&lt;/span>parseBoolean&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>GENERIC_KEY&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">?&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34; it&amp;#39;s GenericService reference&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34; it&amp;#39;s not GenericService reference&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL consumerUrl &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfigURL&lt;span style="color:#719e07">(&lt;/span>CONSUMER_PROTOCOL&lt;span style="color:#719e07">,&lt;/span> referenceParameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>REGISTER_IP_KEY&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceParameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>INTERFACE_KEY&lt;span style="color:#719e07">),&lt;/span> referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerUrl &lt;span style="color:#719e07">=&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>getScopeModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerUrl &lt;span style="color:#719e07">=&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>setServiceModel&lt;span style="color:#719e07">(&lt;/span>consumerModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetadataUtils&lt;span style="color:#719e07">.&lt;/span>publishServiceDefinition&lt;span style="color:#719e07">(&lt;/span>consumerUrl&lt;span style="color:#719e07">,&lt;/span> consumerModel&lt;span style="color:#719e07">.&lt;/span>getServiceModel&lt;span style="color:#719e07">(),&lt;/span> getApplicationModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// create service proxy
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> proxyFactory&lt;span style="color:#719e07">.&lt;/span>getProxy&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> ProtocolUtils&lt;span style="color:#719e07">.&lt;/span>isGeneric&lt;span style="color:#719e07">(&lt;/span>generic&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2152-创建远程引用创建远程引用调用器">21.5.2 创建远程引用，创建远程引用调用器&lt;/h3>
&lt;p>ReferenceConfig类型的createInvokerForRemote方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">createInvokerForRemote&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个url 为注册协议如registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&amp;amp;dubbo=2.0.2&amp;amp;pid=6204&amp;amp;qos.enable=false&amp;amp;qos.port=-1&amp;amp;registry=zookeeper&amp;amp;release=3.0.9&amp;amp;timestamp=1657439419495
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>urls&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL curUrl &lt;span style="color:#719e07">=&lt;/span> urls&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个SPI对象是由字节码动态生成的自适应对象Protocol$Adaptie直接看看不到源码，后续可以解析一个字节码生成的类型，这里后续来调用链路即可
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invoker &lt;span style="color:#719e07">=&lt;/span> protocolSPI&lt;span style="color:#719e07">.&lt;/span>refer&lt;span style="color:#719e07">(&lt;/span>interfaceClass&lt;span style="color:#719e07">,&lt;/span> curUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>curUrl&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> invokers &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invokers&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker &lt;span style="color:#719e07">=&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">,&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>DEFAULT&lt;span style="color:#719e07">).&lt;/span>join&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> StaticDirectory&lt;span style="color:#719e07">(&lt;/span>curUrl&lt;span style="color:#719e07">,&lt;/span> invokers&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> invokers &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL registryUrl &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>URL url &lt;span style="color:#719e07">:&lt;/span> urls&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// For multi-registry scenarios, it is not checked whether each referInvoker is available.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// Because this invoker may become available later.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invokers&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>protocolSPI&lt;span style="color:#719e07">.&lt;/span>refer&lt;span style="color:#719e07">(&lt;/span>interfaceClass&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// use last registry url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registryUrl &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>registryUrl &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// registry url is available
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// for multi-subscription scenario, use &amp;#39;zone-aware&amp;#39; policy by default
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String cluster &lt;span style="color:#719e07">=&lt;/span> registryUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>CLUSTER_KEY&lt;span style="color:#719e07">,&lt;/span> ZoneAwareCluster&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -&amp;gt; FailoverClusterInvoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// (RegistryDirectory, routing happens here) -&amp;gt; Invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invoker &lt;span style="color:#719e07">=&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">(),&lt;/span> cluster&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">).&lt;/span>join&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> StaticDirectory&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">,&lt;/span> invokers&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// not a registry url, must be direct invoke.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>invokers&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;invokers == null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL curUrl &lt;span style="color:#719e07">=&lt;/span> invokers&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">).&lt;/span>getUrl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String cluster &lt;span style="color:#719e07">=&lt;/span> curUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>CLUSTER_KEY&lt;span style="color:#719e07">,&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>DEFAULT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker &lt;span style="color:#719e07">=&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">,&lt;/span> cluster&lt;span style="color:#719e07">).&lt;/span>join&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> StaticDirectory&lt;span style="color:#719e07">(&lt;/span>curUrl&lt;span style="color:#719e07">,&lt;/span> invokers&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2153-invoker对象创建的全过程">21.5.3 Invoker对象创建的全过程&lt;/h3>
&lt;p>为了更好理解Protocol$Adaptie内部的引用执行过程这里我把Debug的链路截图了过来
按照固定的顺序先执行AOP的逻辑再执行具体的逻辑:&lt;/p>
&lt;ul>
&lt;li>Protocol$Adaptie的refer方法&lt;/li>
&lt;li>ProtocolSerializationWrapper AOP类型的协议序列化器refer方法&lt;/li>
&lt;li>ProtocolFilterWrapper AOP类型的协议过滤器的refer方法&lt;/li>
&lt;li>QosProtocolWrapper AOP类型的QOS协议包装器的refer方法&lt;/li>
&lt;li>ProtocolListenerWrapper APO类型监听器包装器的refer方法&lt;/li>
&lt;li>RegistryProtocol 注册协议的refer方法 （会添加容错逻辑）&lt;/li>
&lt;li>RegistryProtocol 注册协议的doRefer方法（调用方法创建Invoker对象）&lt;/li>
&lt;/ul>
&lt;p>&lt;a href="https://cn.dubbo.apache.org/imgs/blog/source-blog/21-createInvokerRemote.png">&lt;/a>&lt;/p>
&lt;p>这里我们不再详细说这个引用链的具体过程直接定位到RegistryProtocol中创建Invoker类型的地方。
先来看RegistryProtocol类型的refer方法，如下代码所示：&lt;/p>
&lt;p>RegistryProtocol类型的refer方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@SuppressWarnings&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;unchecked&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">refer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个url已经被转换为具体的注册中心协议类型了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&amp;amp;dubbo=2.0.2&amp;amp;pid=7944&amp;amp;qos.enable=false&amp;amp;qos.port=-1&amp;amp;release=3.0.9&amp;amp;timestamp=1657440673100
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> url &lt;span style="color:#719e07">=&lt;/span> getRegistryUrl&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取用于操作Zookeeper的Registry类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Registry registry &lt;span style="color:#719e07">=&lt;/span> getRegistry&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RegistryService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> proxyFactory&lt;span style="color:#719e07">.&lt;/span>getInvoker&lt;span style="color:#719e07">((&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// group=&amp;#34;a,b&amp;#34; or group=&amp;#34;*&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> qs &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;)&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getAttribute&lt;span style="color:#719e07">(&lt;/span>REFER_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String group &lt;span style="color:#719e07">=&lt;/span> qs&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>GROUP_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>group&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">((&lt;/span>COMMA_SPLIT_PATTERN&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>group&lt;span style="color:#719e07">)).&lt;/span>length &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">1&lt;/span> &lt;span style="color:#719e07">||&lt;/span> &lt;span style="color:#2aa198">&amp;#34;*&amp;#34;&lt;/span>&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>group&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> doRefer&lt;span style="color:#719e07">(&lt;/span>Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">(),&lt;/span> MergeableCluster&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">),&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> qs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//降级容错的逻辑处理对象 类型为Cluster 实际类型为MockClusterWrapper 内部包装的是FailoverCluster
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//后续调用服务失败时候会先失效转移再降级
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Cluster cluster &lt;span style="color:#719e07">=&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">(),&lt;/span> qs&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>CLUSTER_KEY&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里才是具体的Invoker对象的创建
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> doRefer&lt;span style="color:#719e07">(&lt;/span>cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> qs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>RegistryProtocol类型的doRefer方法创建Invoker对象
直接来看代码了&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">doRefer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Cluster cluster&lt;span style="color:#719e07">,&lt;/span> Registry registry&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> parameters&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> consumerAttribute &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getAttributes&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerAttribute&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>REFER_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String p &lt;span style="color:#719e07">=&lt;/span> isEmpty&lt;span style="color:#719e07">(&lt;/span>parameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>PROTOCOL_KEY&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">?&lt;/span> CONSUMER &lt;span style="color:#719e07">:&lt;/span> parameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>PROTOCOL_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL consumerUrl &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfigURL &lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> p&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> parameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>REGISTER_IP_KEY&lt;span style="color:#719e07">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> getPath&lt;span style="color:#719e07">(&lt;/span>parameters&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> parameters&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerAttribute
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>putAttribute&lt;span style="color:#719e07">(&lt;/span>CONSUMER_URL_KEY&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//重点看这一行 带迁移性质的Invoker对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> migrationInvoker &lt;span style="color:#719e07">=&lt;/span> getMigrationInvoker&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这一行回来执行迁移规则创建应用级优先的服务发现Invoker对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> interceptInvoker&lt;span style="color:#719e07">(&lt;/span>migrationInvoker&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里代码比较重要的其实只有两行getMigrationInvoker和interceptInvoker方法
比较核心也是Dubbo3比较重要的消费者启动逻辑基本都在这个方法里面interceptInvoker，这个方法执行了消费者应用级发现和接口级发现迁移的逻辑，会自动帮忙决策一个Invoker类型对象，不过这个逻辑这里先简单看下，后续单独整个文章来说。&lt;/p>
&lt;p>这里我们先来看 ClusterInvoker对象的创建，下面先看代码：&lt;/p>
&lt;p>RegistryProtocol类型的getMigrationInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> ClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getMigrationInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RegistryProtocol registryProtocol&lt;span style="color:#719e07">,&lt;/span> Cluster cluster&lt;span style="color:#719e07">,&lt;/span> Registry registry&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> URL consumerUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceDiscoveryMigrationInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>registryProtocol&lt;span style="color:#719e07">,&lt;/span> cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>详细的逻辑这里就不再看了，我们继续看RegistryProtocol类型的interceptInvoker方法：&lt;/p>
&lt;p>具体代码如下：
RegistryProtocol类型的interceptInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">interceptInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> URL consumerUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取激活的注册协议监听器扩展里面registry.protocol.listener，这里激活的类型为MigrationRuleListener
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryProtocolListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> listeners &lt;span style="color:#719e07">=&lt;/span> findRegistryProtocolListeners&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>listeners&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RegistryProtocolListener listener &lt;span style="color:#719e07">:&lt;/span> listeners&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里执行MigrationRuleListener类型的onRefer方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> listener&lt;span style="color:#719e07">.&lt;/span>onRefer&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该方法尝试加载所有RegistryProtocolListener定义，这些定义通过与定义的交互来控制调用器的行为，然后使用这些侦听器更改MigrationInvoker的状态和行为。
当前可用的监听器是MigrationRuleListener，用于通过动态变化的规则控制迁移行为。&lt;/p>
&lt;p>可以看到核心的逻辑集中在了这个位置MigrationRuleListener类型的onRefer方法，这个这里就不深入往下说了，后续会有个文章专门来看Dubbo2迁移Dubbo3时候处理的逻辑。&lt;/p>
&lt;p>Invoker对象的创建完成其实就代表了服务引用执行完成，不过这里核心的协议并没有来说&lt;/p>
&lt;p>原文地址：&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/21-dubbo-xiao-fei-zhe-yin-yong-fu-wu-de-ru-kou/">21-Dubbo3消费者引用服务入口&lt;/a>&lt;/p></description></item><item><title>Blog: 20-Dubbo3服务引用配置ReferenceConfig</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="20-dubbo3服务引用配置referenceconfig">20-Dubbo3服务引用配置ReferenceConfig&lt;/h1>
&lt;h2 id="201-简介">20.1 简介&lt;/h2>
&lt;p>前面简单介绍了一下消费者的例子，消费者创建的第一步就是先进行消费者信息的配置对应类型为ReferenceConfig，这里详细来看ReferenceConfig包含哪些信息？先简单了解下消费者配置的类型关系如下图所示：引用配置与服务配置类型都是通过继承接口配置来扩展的，在分析生产者的时候详细介绍过服务相关的配置，这里来详细看消费者引用者的相关配置信息.
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/20-refe.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>前面例子说了消费者配置对象的创建主要是通过如下代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个配置类型的对象创建过程并没有太多的逻辑这里主要来说下各种配置信息：
服务消费者引用服务配置。对应的配置类： &lt;code>org.apache.dubbo.config.ReferenceConfig&lt;/code>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性&lt;/th>
&lt;th>对应URL参数&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>是否必填&lt;/th>
&lt;th>缺省值&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>描述&lt;/th>
&lt;th>兼容性&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>id&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>&lt;strong>必填&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>配置关联&lt;/td>
&lt;td>服务引用BeanId&lt;/td>
&lt;td>1.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>interface&lt;/td>
&lt;td>&lt;/td>
&lt;td>class&lt;/td>
&lt;td>&lt;strong>必填&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务接口名&lt;/td>
&lt;td>1.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>version&lt;/td>
&lt;td>version&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务版本，与服务提供者的版本一致&lt;/td>
&lt;td>1.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>group&lt;/td>
&lt;td>group&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务分组，当一个接口有多个实现，可以用分组区分，必需和服务提供方一致&lt;/td>
&lt;td>1.0.7以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>timeout&lt;/td>
&lt;td>long&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的timeout&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务方法调用超时时间(毫秒)&lt;/td>
&lt;td>1.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>retries&lt;/td>
&lt;td>retries&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的retries&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>远程服务调用重试次数，不包括第一次调用，不需要重试请设为0&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>connections&lt;/td>
&lt;td>connections&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的connections&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>对每个提供者的最大连接数，rmi、http、hessian等短连接协议表示限制连接数，dubbo等长连接协表示建立的长连接个数&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>loadbalance&lt;/td>
&lt;td>loadbalance&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的loadbalance&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>负载均衡策略，可选值：random,roundrobin,leastactive，分别表示：随机，轮询，最少活跃调用&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>async&lt;/td>
&lt;td>async&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的async&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>是否异步执行，不可靠异步，只是忽略返回值，不阻塞执行线程&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>generic&lt;/td>
&lt;td>generic&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的generic&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>是否缺省泛化接口，如果为泛化接口，将返回GenericService&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>check&lt;/td>
&lt;td>check&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的check&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>启动时检查提供者是否存在，true报错，false忽略&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>url&lt;/td>
&lt;td>url&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>点对点直连服务提供者地址，将绕过注册中心&lt;/td>
&lt;td>1.0.6以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>stub&lt;/td>
&lt;td>stub&lt;/td>
&lt;td>class/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务接口客户端本地代理类名，用于在客户端执行本地逻辑，如本地缓存等，该本地代理类的构造函数必须允许传入远程代理对象，构造函数如：public XxxServiceLocal(XxxService xxxService)&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>mock&lt;/td>
&lt;td>mock&lt;/td>
&lt;td>class/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务接口调用失败Mock实现类名，该Mock类必须有一个无参构造函数，与Local的区别在于，Local总是被执行，而Mock只在出现非业务异常(比如超时，网络异常等)时执行，Local在远程调用之前执行，Mock在远程调用后执行。&lt;/td>
&lt;td>Dubbo1.0.13及其以上版本支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cache&lt;/td>
&lt;td>cache&lt;/td>
&lt;td>string/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>以调用参数为key，缓存返回结果，可选：lru, threadlocal, jcache等&lt;/td>
&lt;td>Dubbo2.1.0及其以上版本支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>validation&lt;/td>
&lt;td>validation&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>是否启用JSR303标准注解验证，如果启用，将对方法参数上的注解进行校验&lt;/td>
&lt;td>Dubbo2.1.0及其以上版本支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>proxy&lt;/td>
&lt;td>proxy&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>javassist&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>选择动态代理实现策略，可选：javassist, jdk&lt;/td>
&lt;td>2.0.2以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>client&lt;/td>
&lt;td>client&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>客户端传输类型设置，如Dubbo协议的netty或mina。&lt;/td>
&lt;td>Dubbo2.0.0以上版本支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>registry&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省将从所有注册中心获服务列表后合并结果&lt;/td>
&lt;td>配置关联&lt;/td>
&lt;td>从指定注册中心注册获取服务列表，在多个注册中心时使用，值为&lt;a href="dubbo:registry">dubbo:registry&lt;/a>的id属性，多个注册中心ID用逗号分隔&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>owner&lt;/td>
&lt;td>owner&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>调用服务负责人，用于服务治理，请填写负责人公司邮箱前缀&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>actives&lt;/td>
&lt;td>actives&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>0&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>每服务消费者每服务每方法最大并发调用数&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cluster&lt;/td>
&lt;td>cluster&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>failover&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>集群方式，可选：failover/failfast/failsafe/failback/forking&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>filter&lt;/td>
&lt;td>reference.filter&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>default&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务消费方远程调用过程拦截器名称，多个名称用逗号分隔&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>listener&lt;/td>
&lt;td>invoker.listener&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>default&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务消费方引用服务监听器名称，多个名称用逗号分隔&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>layer&lt;/td>
&lt;td>layer&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务调用者所在的分层。如：biz、dao、intl:web、china:acton。&lt;/td>
&lt;td>2.0.7以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>init&lt;/td>
&lt;td>init&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>是否在afterPropertiesSet()时饥饿初始化引用，否则等到有人注入或引用该实例时再初始化。&lt;/td>
&lt;td>2.0.10以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>protocol&lt;/td>
&lt;td>protocol&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>只调用指定协议的服务提供方，其它协议忽略。&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>原文地址：&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/20-dubbo3-fu-wu-yin-yong-pei-zhi-referenceconfig/">20-Dubbo3服务引用配置ReferenceConfig&lt;/a>&lt;/p></description></item><item><title>Blog: 19 重新来过从一个服务消费者的Demo说起</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="19-重新来过从一个服务消费者的demo说起">19 重新来过从一个服务消费者的Demo说起&lt;/h1>
&lt;p>为了更方便了解原理,我们先来编写一个Demo,从例子中来看源码实现:，前面说了提供者现在已经有服务注册上去了，那接下来我们编写一个消费者的例子来进行服务发现与服务RPC调用。&lt;/p>
&lt;h2 id="191-启动zookeeper">19.1 启动Zookeeper&lt;/h2>
&lt;p>为了Demo可以正常启动,需要我们先在本地启动一个Zookeeper如下图所示:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/19-zk.png" alt="在这里插入图片描述">&lt;/p>
&lt;h2 id="192-服务消费者">19.2 服务消费者&lt;/h2>
&lt;p>接下来给大家贴一下示例源码,这个源码来源于Dubbo源码目录的 dubbo-demo/dubbo-demo-api 目录下面的dubbo-demo-api-consumer子项目,这里我做了删减,方便看核心代码:
首先我们定义一个服务接口如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 同步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 用于异步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>completedFuture&lt;span style="color:#719e07">(&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>服务实现类如下&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.RpcContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.Logger&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.LoggerFactory&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Logger logger &lt;span style="color:#719e07">=&lt;/span> LoggerFactory&lt;span style="color:#719e07">.&lt;/span>getLogger&lt;span style="color:#719e07">(&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, request from consumer: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getServiceContext&lt;span style="color:#719e07">().&lt;/span>getRemoteAddress&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getServiceContext&lt;span style="color:#719e07">().&lt;/span>getLocalAddress&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="193-启用服务消费者">19.3 启用服务消费者&lt;/h2>
&lt;p>有了服务接口之后我们来启用服务,启用服务的源码如下:
这里如果要启动消费者,主要要修改QOS端口这里我已经配置可以直接复用&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> link.elastic.dubbo.consumer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> link.elastic.dubbo.entity.DemoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.constants.CommonConstants&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.ApplicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.MetadataReportConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.ProtocolConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.ReferenceConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.RegistryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.bootstrap.DubboBootstrap&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.service.GenericService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ConsumerApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> runWithBootstrap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">runWithBootstrap&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setGeneric&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setQosEnable&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setQosPort&lt;span style="color:#719e07">(-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>applicationConfig&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://8.131.79.126:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>reference&lt;span style="color:#719e07">(&lt;/span>reference&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DemoService demoService &lt;span style="color:#719e07">=&lt;/span> bootstrap&lt;span style="color:#719e07">.&lt;/span>getCache&lt;span style="color:#719e07">().&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>reference&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String message &lt;span style="color:#719e07">=&lt;/span> demoService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// generic invoke
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GenericService genericService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>GenericService&lt;span style="color:#719e07">)&lt;/span> demoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object genericInvokeResult &lt;span style="color:#719e07">=&lt;/span> genericService&lt;span style="color:#719e07">.&lt;/span>$invoke&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]{&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()},&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo generic invoke&amp;#34;&lt;/span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>genericInvokeResult&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="14-启用服务后写入zookeeper的节点数据">1.4 启用服务后写入Zookeeper的节点数据&lt;/h2>
&lt;p>启动服务,这个时候我们打开Zookeeper图形化客户端来看看这个服务在Zookeeper上面写入来哪些数据,如下图:
在这里插入图片描述
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/19-zk2.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>写入Zookeper上的节点用于服务在分布式场景下的协调,这些节点是比较重要的。&lt;/p>
&lt;p>如果了解过Dubbo的同学,应该会知道Dubbo在低版本的时候会向注册中心中写入服务接口,具体路径在上面的 &lt;strong>dubbo目录下&lt;/strong> ,然后在 &lt;strong>/dubbo/服务接口/&lt;/strong> 路径下写入如下信息:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>服务提供者&lt;/strong>配置信息URL形式&lt;/li>
&lt;li>&lt;strong>服务消费者&lt;/strong>的配置信息URL形式&lt;/li>
&lt;li>服务&lt;strong>路由信息&lt;/strong>&lt;/li>
&lt;li>&lt;strong>配置信息&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>上面这个图就是Dubbo3的注册信息了,后面我们也会围绕细节来说明下,这里可以看下新增了:&lt;/p>
&lt;ul>
&lt;li>/dubbo/metadata &lt;strong>元数据信息&lt;/strong>&lt;/li>
&lt;li>/dubbo/mapping 服务和应用的&lt;strong>映射信息&lt;/strong>&lt;/li>
&lt;li>/dubbo/config &lt;strong>注册中心配置&lt;/strong>&lt;/li>
&lt;li>/services目录&lt;strong>应用信息&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>在这里可以大致了解下,在后面会有更详细的源码解析这个示例代码.通过透析代码来看透Dubbo3服务注册原理,服务提供原理。&lt;/p>
&lt;p>原文地址：&lt;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说起&lt;/a>&lt;/p></description></item><item><title>Blog: 18-Dubbo3元数据服务MetadataService的导出</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="18-dubbo3元数据服务metadataservice的导出">18-Dubbo3元数据服务MetadataService的导出&lt;/h1>
&lt;h2 id="181-简介">18.1 简介&lt;/h2>
&lt;p>MetadataService
此服务用于公开Dubbo进程内的元数据信息。典型用途包括：&lt;/p>
&lt;ul>
&lt;li>使用者查询提供者的元数据信息，以列出接口和每个接口的配置&lt;/li>
&lt;li>控制台（dubbo admin）查询特定进程的元数据，或聚合所有进程的数据。在Dubbo2.x的时候，所有的服务数据都是以接口的形式注册在注册中心.&lt;/li>
&lt;/ul>
&lt;p>Dubbo3将部分数据抽象为元数据的形式来将数据存放在元数据中心，然后元数据由服务提供者提供给消费者而不是再由注册中心进行推送，如下图所示：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/18-metadata.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/18-metadata3.png" alt="在这里插入图片描述">
引入 MetadataService 元数据服务服务的好处
• 由中心化推送转向点对点拉取（Consumer - Proroder）
• 易于扩展更多的参数
• 更多的数据量
• 对外暴露更多的治理数据&lt;/p>
&lt;h2 id="182-metadataservice的导出过程">18.2 MetadataService的导出过程&lt;/h2>
&lt;p>了解元数据的到处过程，这个就要继续前面博客往后的代码了前面博客说了一个服务发布之后的服务信息的双注册数据，这里继续看下导出服务之后的代码：
先来简单回顾下模块发布的启动生命周期方法：&lt;/p>
&lt;p>DefaultModuleDeployer类型的start方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> Future &lt;span style="color:#268bd2">start&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onModuleStarting&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// initialize
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// export services
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> exportServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// prepare application instance
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// exclude internal module to avoid wait itself
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>moduleModel &lt;span style="color:#719e07">!=&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">().&lt;/span>getInternalModule&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>prepareInternalModule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// refer services
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if no async export/refer services, just set started
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>asyncExportingFutures&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> asyncReferringFutures&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onModuleStarted&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">....&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> startFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面的博客我们已经说了服务提供者导出服务的方法如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// export services
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> exportServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在导出服务之后如果代码中配置了引用服务的代码将会执行引用服务的功能，调用代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>referServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>不过我们样例代码并没有介绍引用服务的功能，这里先不说，等服务提供者完全启动成功之后我们再来看消费者的逻辑。&lt;/p>
&lt;p>接下来我们要看的是模块启动成功之后的方法 onModuleStarted();，在这个方法中会去发布服务元数据信息。&lt;/p>
&lt;h2 id="183-模块启动成功时候的逻辑-onmodulestarted">18.3 模块启动成功时候的逻辑 onModuleStarted();&lt;/h2>
&lt;p>这里我们直接先看代码再来分析下逻辑：&lt;/p>
&lt;p>DefaultModuleDeployer类型的onModuleStarted方法如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onModuleStarted&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//状态判断是否为启动中如果是则将状态设置为STARTED
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isStarting&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//先修改状态
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> setStarted&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; has started.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//状态修改成功之后开始通知应用程序发布器模块发布器启动成功了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>notifyModuleChanged&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">,&lt;/span> DeployState&lt;span style="color:#719e07">.&lt;/span>STARTED&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// complete module start future after application state changed
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> completeStartFuture&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>应用程序发布器处理启动成功的逻辑：
DefaultApplicationDeployer类型的notifyModuleChanged方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">notifyModuleChanged&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ModuleModel moduleModel&lt;span style="color:#719e07">,&lt;/span> DeployState state&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据所有模块的状态来判断应用发布器的状态
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkState&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">,&lt;/span> state&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// notify module state changed or module changed
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//通知所有模块状态更新
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>stateLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stateLock&lt;span style="color:#719e07">.&lt;/span>notifyAll&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>应用发布器模型DefaultApplicationDeployer检查状态方法checkState代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">checkState&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ModuleModel moduleModel&lt;span style="color:#719e07">,&lt;/span> DeployState moduleState&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//存在写操作 先加个锁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>stateLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//非内部模块，并且模块的状态是发布成功了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>moduleModel&lt;span style="color:#719e07">.&lt;/span>isInternal&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> moduleState &lt;span style="color:#719e07">==&lt;/span> DeployState&lt;span style="color:#719e07">.&lt;/span>STARTED&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prepareApplicationInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//应用下所有模块状态进行汇总计算
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> DeployState newState &lt;span style="color:#719e07">=&lt;/span> calculateState&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">switch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>newState&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> STARTED&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onStarted&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> STARTING&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onStarting&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> STOPPING&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onStopping&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> STOPPED&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onStopped&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> FAILED&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Throwable error &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ModuleModel errorModule &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ModuleModel module &lt;span style="color:#719e07">:&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getModuleModels&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ModuleDeployer deployer &lt;span style="color:#719e07">=&lt;/span> module&lt;span style="color:#719e07">.&lt;/span>getDeployer&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>deployer&lt;span style="color:#719e07">.&lt;/span>isFailed&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> deployer&lt;span style="color:#719e07">.&lt;/span>getError&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> error &lt;span style="color:#719e07">=&lt;/span> deployer&lt;span style="color:#719e07">.&lt;/span>getError&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> errorModule &lt;span style="color:#719e07">=&lt;/span> module&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onFailed&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; found failed module: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> errorModule&lt;span style="color:#719e07">.&lt;/span>getDesc&lt;span style="color:#719e07">(),&lt;/span> error&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> PENDING&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// cannot change to pending from other state
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// setPending();
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="184-准备发布元数据信息和应用实例信息">18.4 准备发布元数据信息和应用实例信息&lt;/h2>
&lt;p>前面有个代码调用比较重要：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>prepareApplicationInstance&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DefaultApplicationDeployer类型的prepareApplicationInstance方法如下所示&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">prepareApplicationInstance&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//已经注册过应用实例数据了 直接返回 （下面CAS逻辑判断了）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>hasPreparedApplicationInstance&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册开关控制默认为true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//通过将registerConsumer默认设置为“false”来关闭纯使用者进程实例的注册。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isRegisterConsumerInstance&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportMetadataService&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>hasPreparedApplicationInstance&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register the local ServiceInstance if required
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registerServiceInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1841-导出元数据服务方法exportmetadataservice">18.4.1 导出元数据服务方法exportMetadataService&lt;/h3>
&lt;p>这里我们就先直接来贴一下代码：&lt;/p>
&lt;p>DefaultApplicationDeployer类型的exportMetadataService方法如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportMetadataService&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isStarting&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里监听器我们主要关注的类型是ExporterDeployListener类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>DeployListener&lt;span style="color:#719e07">&amp;lt;&lt;/span>ApplicationModel&lt;span style="color:#719e07">&amp;gt;&lt;/span> listener &lt;span style="color:#719e07">:&lt;/span> listeners&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>listener &lt;span style="color:#719e07">instanceof&lt;/span> ApplicationDeployListener&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 回调监听器的模块启动成功方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">((&lt;/span>ApplicationDeployListener&lt;span style="color:#719e07">)&lt;/span> listener&lt;span style="color:#719e07">).&lt;/span>onModuleStarted&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; an exception occurred when handle starting event&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面我们主要关注ExporterDeployListener类型的监听器的回调方法，这里我贴一下代码：
ExporterDeployListener类型的onModuleStarted方法如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onModuleStarted&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// start metadata service exporter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//MetadataServiceDelegation类型为实现提供远程RPC服务以方便元数据信息的查询功能的类型。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MetadataServiceDelegation metadataService &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getOrRegisterBean&lt;span style="color:#719e07">(&lt;/span>MetadataServiceDelegation&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metadataServiceExporter &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataServiceExporter &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConfigurableMetadataServiceExporter&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> metadataService&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// fixme, let&amp;#39;s disable local metadata service export at this moment
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//默认我们是没有配置这个元数据类型的这里元数据类型默认为local 条件是不是remote则开始导出，在前面的博客&amp;lt;&amp;lt;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&amp;gt;&amp;gt; 中有提到这个配置下面我再说下
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>REMOTE_METADATA_STORAGE_TYPE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>getMetadataType&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">)))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataServiceExporter&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在前面的博客&lt;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/">&amp;laquo;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&amp;raquo;&lt;/a> 中有提到这个配置下面我再说下&lt;/p>
&lt;p>metadata-type&lt;/p>
&lt;p>metadata 传递方式，是以 Provider 视角而言的，Consumer 侧配置无效，可选值有：&lt;/p>
&lt;ul>
&lt;li>remote - Provider 把 metadata 放到远端&lt;strong>注册中心&lt;/strong>，Consumer 从&lt;strong>注册中心获取&lt;/strong>。&lt;/li>
&lt;li>local - Provider &lt;strong>把 metadata 放在本地&lt;/strong>，&lt;strong>Consumer 从 Provider 处直接获取&lt;/strong> 。&lt;/li>
&lt;/ul>
&lt;p>可以看到默认的local配置元数据信息的获取是由消费者从提供者拉的，那提供者怎么拉取对应服务的元数据信息那就要要用到这个博客说到的MetadataService服务，传递方式为remote的方式其实就要依赖注册中心了相对来说增加了注册中心的压力。&lt;/p>
&lt;h3 id="1842-可配置元数据服务的导出configurablemetadataserviceexporter的export">18.4.2 可配置元数据服务的导出ConfigurableMetadataServiceExporter的export&lt;/h3>
&lt;p>前面了解了导出服务的调用链路，这里详细看下ConfigurableMetadataServiceExporter的export过程源码如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> ConfigurableMetadataServiceExporter &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//元数据服务配置已经存在或者已经导出或者不可导出情况下是无需导出的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serviceConfig &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> &lt;span style="color:#719e07">!&lt;/span>isExported&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建服务配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceConfig &lt;span style="color:#719e07">=&lt;/span> buildServiceConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// export
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//导出服务 ,导出服务的具体过程这里就不再说了可以看上一个博客，这个导出服务的过程会绑定端口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataService&lt;span style="color:#719e07">.&lt;/span>setMetadataURL&lt;span style="color:#719e07">(&lt;/span>serviceConfig&lt;span style="color:#719e07">.&lt;/span>getExportedUrls&lt;span style="color:#719e07">().&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;The MetadataService exports urls : &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>getExportedUrls&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isWarnEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;The MetadataService has been exported : &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>getExportedUrls&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1843-元数据服务配置对象的创建">18.4.3 元数据服务配置对象的创建&lt;/h3>
&lt;p>前面我们看到了构建元数据服务对象的代码调用ServiceConfig&lt;MetadataService>，接下来我们详细看下构建源码如下所示：
ConfigurableMetadataServiceExporter类型的buildServiceConfig构建元数据服务配置对象方法如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataService&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">buildServiceConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//1 获取当前的应用配置 然后初始化应用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> getApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建服务配置对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataService&lt;span style="color:#719e07">&amp;gt;&lt;/span> serviceConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//设置域模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>getInternalModule&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>applicationConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//2 创建注册中心配置对象 然后并初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RegistryConfig registryConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;N/A&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setId&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;internal-metadata-registry&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//3 创建服务配置对象，并初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setRegistry&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setRegister&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//4 生成协议配置 ，这里会配置一下元数据使用的服务端口号默认使用其他服务的端口20880
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>generateMetadataProtocol&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>MetadataService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setDelay&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里也是需要注意的地方服务引用的类型为MetadataServiceDelegation
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>metadataService&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setGroup&lt;span style="color:#719e07">(&lt;/span>applicationConfig&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setVersion&lt;span style="color:#719e07">(&lt;/span>MetadataService&lt;span style="color:#719e07">.&lt;/span>VERSION&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//5 生成方法配置 这里目前提供的服务方法为getAndListenInstanceMetadata方法 后续可以看下这个方法的视线
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setMethods&lt;span style="color:#719e07">(&lt;/span>generateMethodConfig&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setConnections&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// separate connection
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setExecutes&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">100&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// max tasks running at the same time
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> serviceConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个服务配置对象的创建非常像我们第一个博客提到的服务配置过程，不过这个元数据服务对象有几个比较特殊的配置&lt;/p>
&lt;ul>
&lt;li>注册中心的配置register设置为了false 则为不向注册中心注册具体的服务配置信息&lt;/li>
&lt;li>对每个提供者的最大连接数connections为1&lt;/li>
&lt;li>服务提供者每服务每方法最大可并行执行请求数executes为100&lt;/li>
&lt;/ul>
&lt;p>在使用过程中可以知道上面这几个配置值&lt;/p>
&lt;h2 id="185-应用级数据注册---registerserviceinstance">18.5 应用级数据注册 registerServiceInstance()&lt;/h2>
&lt;p>在前面导出元数据服务之后也会调用一行代码来注册应用级数据来保证应用上线&lt;/p>
&lt;p>主要涉及到的代码为DefaultApplicationDeployer类型中的registerServiceInstance方法如下所示&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">registerServiceInstance&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//标记变量设置为true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registered &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceInstanceMetadataUtils&lt;span style="color:#719e07">.&lt;/span>registerMetadataAndInstance&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Register instance error&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>registered&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// scheduled task for updating Metadata and ServiceInstance
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> asyncMetadataFuture &lt;span style="color:#719e07">=&lt;/span> frameworkExecutorRepository&lt;span style="color:#719e07">.&lt;/span>getSharedScheduledExecutor&lt;span style="color:#719e07">().&lt;/span>scheduleWithFixedDelay&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore refresh metadata on stopping
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>isDestroyed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>isDestroyed&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registered&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceInstanceMetadataUtils&lt;span style="color:#719e07">.&lt;/span>refreshMetadataAndInstance&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>isDestroyed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Refresh instance and metadata error&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">},&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ConfigurationUtils&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> METADATA_PUBLISH_DELAY_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_METADATA_PUBLISH_DELAY&lt;span style="color:#719e07">),&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个方法先将应用元数据注册到注册中心，然后开始开启定时器每隔30秒同步一次元数据向注册中心。&lt;/p>
&lt;h3 id="1851-服务实例元数据工具类注册服务发现的元数据信息">18.5.1 服务实例元数据工具类注册服务发现的元数据信息&lt;/h3>
&lt;p>前面通过调用类型ServiceInstanceMetadataUtils工具类的registerMetadataAndInstance方法来进行服务实例数据和元数据的注册这里我们详细看下代码如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">registerMetadataAndInstance&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LOGGER&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Start registering instance address to registry.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RegistryManager registryManager &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>RegistryManager&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register service instance
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//注意这里服务发现的类型只有ServiceDiscoveryRegistry类型的注册协议才满足 registryManager.getServiceDiscoveries().forEach(ServiceDiscovery::register);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1852-abstractservicediscovery中的服务发现数据注册的模版方法">18.5.2 AbstractServiceDiscovery中的服务发现数据注册的模版方法&lt;/h3>
&lt;p>AbstractServiceDiscovery类型的注册方法register()方法这个是一个模版方法，真正执行的注册逻辑封装在了doRegister方法中由扩展的服务发现子类来完成&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">register&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RuntimeException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//第一步创建应用的实例信息等待下面注册到注册中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceInstance &lt;span style="color:#719e07">=&lt;/span> createServiceInstance&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataInfo&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isValidInstance&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceInstance&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;No valid instance found, stop registering instance address to registry.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否需要更新
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> revisionUpdated &lt;span style="color:#719e07">=&lt;/span> calOrUpdateInstanceRevision&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceInstance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>revisionUpdated&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportMetadata&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataInfo&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//应用的实例信息注册到注册中心之上 ，这个
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> doRegister&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceInstance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1853-应用级实例对象创建">18.5.3 应用级实例对象创建&lt;/h3>
&lt;p>可以看到在AbstractServiceDiscovery服务发现的第一步创建应用的实例信息等待下面注册到注册中心&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceInstance &lt;span style="color:#719e07">=&lt;/span> createServiceInstance&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataInfo&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>最终创建的serviceInstance类型为ServiceInstance 这个是Dubbo封装的一个接口，具体实现类型为DefaultServiceInstance，我们可以看下应用级的元数据有哪些&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> ServiceInstance &lt;span style="color:#268bd2">createServiceInstance&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataInfo metadataInfo&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里的服务名字为：dubbo-demo-api-provider
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> DefaultServiceInstance instance &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DefaultServiceInstance&lt;span style="color:#719e07">(&lt;/span>serviceName&lt;span style="color:#719e07">,&lt;/span> applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//应用服务的元数据 ，可以看下面debug的数据信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance&lt;span style="color:#719e07">.&lt;/span>setServiceMetadata&lt;span style="color:#719e07">(&lt;/span>metadataInfo&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//metadataType的值为local 这个方法是将元数据类型存储到英勇的元数据对象中 对应内容为dubbo.metadata.storage-type:local
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> setMetadataStorageType&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">,&lt;/span> metadataType&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 这个是自定义元数据数据 我们也可以通过实现扩展ServiceInstanceCustomizer来自定义一些元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceInstanceMetadataUtils&lt;span style="color:#719e07">.&lt;/span>customizeInstance&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">,&lt;/span> applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个方法的主要目的就是将应用的元数据信息都封装到ServiceInstance类型中，不过额外提供了一个扩展性比较好的方法可以自定义元数据信息&lt;/p>
&lt;p>前面的metadataInfo对象的信息如下图所示：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/18-metadata2.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>自定义元数据类型Dubbo官方提供了一个默认的实现类型为：ServiceInstanceMetadataCustomizer&lt;/p>
&lt;p>最终封装好的元数据信息如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>DefaultServiceInstance&lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>serviceName&lt;span style="color:#719e07">=&lt;/span>&amp;#39;dubbo&lt;span style="color:#719e07">-&lt;/span>demo&lt;span style="color:#719e07">-&lt;/span>api&lt;span style="color:#719e07">-&lt;/span>provider&amp;#39;&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>host&lt;span style="color:#719e07">=&lt;/span>&amp;#39;&lt;span style="color:#2aa198">192.168.1.169&lt;/span>&amp;#39;&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>port&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">20880&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>enabled&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>healthy&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#719e07">={&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dubbo&lt;span style="color:#719e07">.&lt;/span>metadata&lt;span style="color:#719e07">-&lt;/span>service&lt;span style="color:#719e07">.&lt;/span>url&lt;span style="color:#719e07">-&lt;/span>params&lt;span style="color:#719e07">={&lt;/span>&lt;span style="color:#2aa198">&amp;#34;connections&amp;#34;&lt;/span>&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#2aa198">&amp;#34;1&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;version&amp;#34;&lt;/span>&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#2aa198">&amp;#34;2.0.2&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;release&amp;#34;&lt;/span>&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#2aa198">&amp;#34;3.0.9&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;side&amp;#34;&lt;/span>&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#2aa198">&amp;#34;provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;port&amp;#34;&lt;/span>&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#2aa198">&amp;#34;20880&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;protocol&amp;#34;&lt;/span>&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">},&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dubbo&lt;span style="color:#719e07">.&lt;/span>endpoints&lt;span style="color:#719e07">=[&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;port&amp;#34;&lt;/span>&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#2aa198">20880&lt;/span>&lt;span style="color:#719e07">,&lt;/span>&lt;span style="color:#2aa198">&amp;#34;protocol&amp;#34;&lt;/span>&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dubbo&lt;span style="color:#719e07">.&lt;/span>metadata&lt;span style="color:#719e07">.&lt;/span>storage&lt;span style="color:#719e07">-&lt;/span>type&lt;span style="color:#719e07">=&lt;/span>local&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timestamp&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1656227493387&lt;/span>&lt;span style="color:#719e07">}}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1854-应用级实例数据配置变更的的版本号获取">18.5.4 应用级实例数据配置变更的的版本号获取&lt;/h3>
&lt;p>前面创建元应用的实例信息后开始创建版本号来判断是否需要更新，对应AbstractServiceDiscovery类型的calOrUpdateInstanceRevision&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">calOrUpdateInstanceRevision&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ServiceInstance instance&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取元数据版本号对应字段dubbo.metadata.revision
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String existingInstanceRevision &lt;span style="color:#719e07">=&lt;/span> getExportedServicesRevision&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取实例的服务元数据信息：metadata{app=&amp;#39;dubbo-demo-api-provider&amp;#39;,revision=&amp;#39;null&amp;#39;,size=1,services=[link.elastic.dubbo.entity.DemoService:dubbo]}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MetadataInfo metadataInfo &lt;span style="color:#719e07">=&lt;/span> instance&lt;span style="color:#719e07">.&lt;/span>getServiceMetadata&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//必须在不同线程之间同步计算此实例的状态，如同一实例的修订和修改。此方法的使用仅限于某些点，例如在注册期间。始终尝试使用此选项。改为getRevision（）。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String newRevision &lt;span style="color:#719e07">=&lt;/span> metadataInfo&lt;span style="color:#719e07">.&lt;/span>calAndGetRevision&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//版本号发生了变更（元数据发生了变更）版本号是md5元数据信息计算出来HASH验证
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>newRevision&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>existingInstanceRevision&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//版本号添加到dubbo.metadata.revision字段中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance&lt;span style="color:#719e07">.&lt;/span>getMetadata&lt;span style="color:#719e07">().&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>EXPORTED_SERVICES_REVISION_PROPERTY_NAME&lt;span style="color:#719e07">,&lt;/span> metadataInfo&lt;span style="color:#719e07">.&lt;/span>getRevision&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="18541-元数据版本号的计算与hash校验-calandgetrevision">18.5.4.1 元数据版本号的计算与HASH校验 calAndGetRevision&lt;/h4>
&lt;p>这个方法其实比较重要，决定了什么时候会更新元数据，Dubbo使用了一种Hash验证的方式将元数据转MD5值与之前的存在的版本号（也是元数据转MD5得到的） 如果数据发生了变更则MD5值会发生变化 以此来更新元数据，不过发生了MD5冲突的话就会导致配置不更新这个冲突的概率非常小。
好了直接来看代码吧：
MetadataInfo类型的calAndGetRevision方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> String &lt;span style="color:#268bd2">calAndGetRevision&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>revision &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>updated&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> revision&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> updated &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//应用下没有服务则使用一个空的版本号
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmptyMap&lt;span style="color:#719e07">(&lt;/span>services&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>revision &lt;span style="color:#719e07">=&lt;/span> EMPTY_REVISION&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> StringBuilder sb &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> StringBuilder&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//app是应用名
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> sb&lt;span style="color:#719e07">.&lt;/span>append&lt;span style="color:#719e07">(&lt;/span>app&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> ServiceInfo&lt;span style="color:#719e07">&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#719e07">new&lt;/span> TreeMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>services&lt;span style="color:#719e07">).&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sb&lt;span style="color:#719e07">.&lt;/span>append&lt;span style="color:#719e07">(&lt;/span>entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">().&lt;/span>toDescString&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String tempRevision &lt;span style="color:#719e07">=&lt;/span> RevisionResolver&lt;span style="color:#719e07">.&lt;/span>calRevision&lt;span style="color:#719e07">(&lt;/span>sb&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEquals&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>revision&lt;span style="color:#719e07">,&lt;/span> tempRevision&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//元数据重新注册的话我们可以看看这个日志metadata revision change
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;metadata revision changed: %s -&amp;gt; %s, app: %s, services: %d&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>revision&lt;span style="color:#719e07">,&lt;/span> tempRevision&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>app&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>services&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>revision &lt;span style="color:#719e07">=&lt;/span> tempRevision&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>rawMetadataInfo &lt;span style="color:#719e07">=&lt;/span> JsonUtils&lt;span style="color:#719e07">.&lt;/span>getJson&lt;span style="color:#719e07">().&lt;/span>toJson&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> revision&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>RevisionResolver类型的Md5运算计算版本号&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>md5Utils&lt;span style="color:#719e07">.&lt;/span>getMd5&lt;span style="color:#719e07">(&lt;/span>metadata&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1855-reportmetadata">18.5.5 reportMetadata&lt;/h3>
&lt;p>回到18.5.2 AbstractServiceDiscovery中的模版方法register，这里我们来看下reportMetadata方法，不过这个方法目前并不会走到，因为我们默认的配置元数据是local不会直接把应用的元数据注册在元数据中心&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">reportMetadata&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataInfo metadataInfo&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metadataReport &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//订阅元数据的标识符
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> SubscriberMetadataIdentifier identifier &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> SubscriberMetadataIdentifier&lt;span style="color:#719e07">(&lt;/span>serviceName&lt;span style="color:#719e07">,&lt;/span> metadataInfo&lt;span style="color:#719e07">.&lt;/span>getRevision&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否远程发布元数据，这里我们是本地注册这个就不会在元数据中心发布这个元数据信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">((&lt;/span>DEFAULT_METADATA_STORAGE_TYPE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>metadataType&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> metadataReport&lt;span style="color:#719e07">.&lt;/span>shouldReportMetadata&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">||&lt;/span> REMOTE_METADATA_STORAGE_TYPE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>metadataType&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataReport&lt;span style="color:#719e07">.&lt;/span>publishAppMetadata&lt;span style="color:#719e07">(&lt;/span>identifier&lt;span style="color:#719e07">,&lt;/span> metadataInfo&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1856-扩展的注册中心来注册应用级服务发现数据doregister方法">18.5.6 扩展的注册中心来注册应用级服务发现数据doRegister方法&lt;/h3>
&lt;p>前面我们说了AbstractServiceDiscovery中的模版方法register，在register会调用一个doRegister方法来注册应用级数据，这个方法是需要扩展注册中心的服务发现来自行实现的，我们这里以官方实现的Zookeeper服务发现模型为例:&lt;/p>
&lt;p>ZookeeperServiceDiscovery中的doRegister方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doRegister&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ServiceInstance serviceInstance&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceDiscovery&lt;span style="color:#719e07">.&lt;/span>registerService&lt;span style="color:#719e07">(&lt;/span>build&lt;span style="color:#719e07">(&lt;/span>serviceInstance&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcException&lt;span style="color:#719e07">(&lt;/span>REGISTRY_EXCEPTION&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Failed register instance &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> serviceInstance&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面我们介绍了ZookeeperServiceDiscovery发现的构造器连接注册中心，这里来看下服务注册，
应用级实例数据注册一共分为两步
第一步是：Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance
第二步是：执行registerService方法将数据注册到注册中心&lt;/p>
&lt;p>先来看第一步：Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance
关于Curator的服务发现原理可以参考官网的文章博客&lt;a href="https://curator.apache.org/curator-x-discovery/index.html">curator-x-discovery&lt;/a>&lt;/p>
&lt;p>&lt;strong>什么是发现服务？&lt;/strong>
在 SOA/分布式系统中，服务需要找到彼此。即，Web 服务可能需要找到缓存服务等。DNS 可以用于此，但对于不断变化的服务来说，它远不够灵活。服务发现系统提供了一种机制：&lt;/p>
&lt;ul>
&lt;li>注册其可用性的服务&lt;/li>
&lt;li>定位特定服务的单个实例&lt;/li>
&lt;li>在服务实例更改时通知&lt;/li>
&lt;/ul>
&lt;p>服务实例由类表示：ServiceInstance。ServiceInstances 具有名称、id、地址、端口和/或 ssl 端口，以及可选的有效负载（用户定义）。ServiceInstances 通过以下方式序列化并存储在 ZooKeeper 中：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>base path
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">|&lt;/span>_______ service A name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">|&lt;/span>__________ instance &lt;span style="color:#2aa198">1&lt;/span> id &lt;span style="color:#719e07">--&amp;gt;&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serialized ServiceInstance&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">|&lt;/span>__________ instance &lt;span style="color:#2aa198">2&lt;/span> id &lt;span style="color:#719e07">--&amp;gt;&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serialized ServiceInstance&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">|&lt;/span>__________ &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">|&lt;/span>_______ service B name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">|&lt;/span>__________ instance &lt;span style="color:#2aa198">1&lt;/span> id &lt;span style="color:#719e07">--&amp;gt;&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serialized ServiceInstance&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">|&lt;/span>__________ instance &lt;span style="color:#2aa198">2&lt;/span> id &lt;span style="color:#719e07">--&amp;gt;&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serialized ServiceInstance&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">|&lt;/span>__________ &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">|&lt;/span>_______ &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个应用最终注册应用级服务数据如下：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/18-metadata4.png" alt="在这里插入图片描述">
这里需要注意的是这个 应用的IP+端口的服务元数据信息是临时节点
build方法内容对应着上图的JSON数据 可以看菜build方法封装的过程：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>curator&lt;span style="color:#719e07">.&lt;/span>x&lt;span style="color:#719e07">.&lt;/span>discovery&lt;span style="color:#719e07">.&lt;/span>ServiceInstance&lt;span style="color:#719e07">&amp;lt;&lt;/span>ZookeeperInstance&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">build&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ServiceInstance serviceInstance&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceInstanceBuilder builder&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String serviceName &lt;span style="color:#719e07">=&lt;/span> serviceInstance&lt;span style="color:#719e07">.&lt;/span>getServiceName&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String host &lt;span style="color:#719e07">=&lt;/span> serviceInstance&lt;span style="color:#719e07">.&lt;/span>getHost&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> port &lt;span style="color:#719e07">=&lt;/span> serviceInstance&lt;span style="color:#719e07">.&lt;/span>getPort&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> metadata &lt;span style="color:#719e07">=&lt;/span> serviceInstance&lt;span style="color:#719e07">.&lt;/span>getSortedMetadata&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String id &lt;span style="color:#719e07">=&lt;/span> generateId&lt;span style="color:#719e07">(&lt;/span>host&lt;span style="color:#719e07">,&lt;/span> port&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//ZookeeperInstance是Dubbo封装的用于存放payload数据 包含服务id，服务名字和元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ZookeeperInstance zookeeperInstance &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ZookeeperInstance&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">,&lt;/span> serviceName&lt;span style="color:#719e07">,&lt;/span> metadata&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> builder &lt;span style="color:#719e07">=&lt;/span> builder&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>id&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>name&lt;span style="color:#719e07">(&lt;/span>serviceName&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>address&lt;span style="color:#719e07">(&lt;/span>host&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>port&lt;span style="color:#719e07">(&lt;/span>port&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>payload&lt;span style="color:#719e07">(&lt;/span>zookeeperInstance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException&lt;span style="color:#719e07">(&lt;/span>e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> builder&lt;span style="color:#719e07">.&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在《18.5 应用级数据注册 registerServiceInstance() 》 小节中介绍了应用元数据信息的注册调用代码，其实后面还有个update的逻辑定期30秒同步元数据到元数据中心，这里就不详细介绍了。&lt;/p>
&lt;p>原文地址：&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/18-dubbo3-yuan-shu-ju-fu-wu-metadataservice-de-dao-chu/">18-Dubbo3元数据服务MetadataService的导出&lt;/a>&lt;/p></description></item><item><title>Blog: 17-Dubbo服务提供者的双注册原理</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="17-dubbo服务提供者的双注册原理">17-Dubbo服务提供者的双注册原理&lt;/h1>
&lt;h2 id="171-简介">17.1 简介&lt;/h2>
&lt;p>上个博客&lt;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的三大中心之元数据中心源码解析》&lt;/a>导出服务端的时候多次提到了元数据中心，注册信息的注册。
Dubbo3出来时间不太长，对于现在的用户来说大部分使用的仍旧是Dubbo2.x，
Dubbo3 比较有特色也是会直接使用到的功能就是&lt;strong>应用级服务发现&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>应用级服务发现
&lt;em>从服务/接口粒度到应用粒度的升级，使得 Dubbo 在集群可伸缩性、连接异构微服务体系上更具优势。应用粒度能以更低的资源消耗支持超百万实例规模集群程； 实现与 Spring Cloud、Kubernetes Service 等异构微服务体系的互联互通。&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>对于直接使用Dubbo3的用户还好，可以仅仅开启应用级注册，但是对于Dubbo2.x的用户升级到Dubbo3的用户来说前期都是要开启双注册来慢慢迁移的，既注册传统的接口信息到注册中心，又注册应用信息到注册中心，同时注册应用与接口关系的元数据信息。
关于双注册与服务迁移的过程的使用可以参考官网：
&lt;a href="https://dubbo.apache.org/zh/docs/migration/migration-service-discovery/">应用级地址发现迁移指南&lt;/a>&lt;/p>
&lt;p>关于官网提供者双注册的图我这里贴一下，方便了解：
&lt;img src="https://cn.dubbo.apache.org/imgs/v3/migration/provider-registration.png" alt="在这里插入图片描述">&lt;/p>
&lt;h2 id="172-双注册配置的读取">17.2 双注册配置的读取&lt;/h2>
&lt;h3 id="1721-注册中心地址作为元数据中心">17.2.1 注册中心地址作为元数据中心&lt;/h3>
&lt;p>这个配置的解析过程在前面的博客介绍元数据中心的时候很详细的说了相关链接：&lt;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的三大中心之元数据中心源码解析&lt;/a>&lt;/p>
&lt;p>对应代码位于：DefaultApplicationDeployer类型的startMetadataCenter()方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startMetadataCenter&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> useRegistryAsMetadataCenterIfNecessary&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...省略掉其他代码防止受到干扰
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>具体逻辑是这个方法：
useRegistryAsMetadataCenterIfNecessary&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">useRegistryAsMetadataCenterIfNecessary&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置缓存中查询元数据配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Collection&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> metadataConfigs &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getMetadataConfigs&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...省略掉空判断
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> defaultRegistries &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getDefaultRegistries&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>defaultRegistries&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//多注册中心遍历
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> defaultRegistries
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>stream&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>filter&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>isUsedRegistryAsMetadataCenter&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心配置映射为元数据中心 映射就是获取需要的配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>map&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>registryAsMetadataCenter&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将元数据中心配置存储在配置缓存中方便后续使用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>metadataReportConfig &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...省略掉具体的逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于元数据中心地址的获取，主要经过如下逻辑：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>查询：&lt;/strong> 所有可用的默认注册中心列表&lt;/li>
&lt;li>&lt;strong>遍历：&lt;/strong> 多注册中心遍历&lt;/li>
&lt;li>&lt;strong>筛选：&lt;/strong> 选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)&lt;/li>
&lt;li>&lt;strong>转化：&lt;/strong> 注册中心配置RegistryConfig映射转换为元数据中心配置类型MetadataReportConfig&lt;/li>
&lt;/ul>
&lt;p>MetadataReportConfig 映射就是获取需要的配置。&lt;/p>
&lt;p>最后会把查询到的元数据中心配置存储在配置缓存中方便后续使用。&lt;/p>
&lt;h3 id="1722-双注册模式配置">17.2.2 双注册模式配置&lt;/h3>
&lt;p>双注册配置类型是这个&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>dubbo&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">-&lt;/span>mode&lt;span style="color:#719e07">=&lt;/span>all
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>默认值为all代表应用级注册和接口级注册，当前在完全迁移到应用级注册之后可以将服务直接迁移到应用级配置上去。
配置值解释：&lt;/p>
&lt;ul>
&lt;li>all 双注册&lt;/li>
&lt;li>instance 应用级注册&lt;/li>
&lt;li>interface 接口级注册&lt;/li>
&lt;/ul>
&lt;p>后面的代码如果想要看更详细的代码可以看博客&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/16-mo-kuai-fa-bu-qi-fa-bu-fu-wu-quan-guo-cheng/">《16-模块发布器发布服务全过程》&lt;/a>
关于这个配置的使用我们详细来看下，在Dubbo服务注册时候会先通过此配置查询需要注册服务地址，具体代码位于ServiceConfig的doExportUrls()方法中：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doExportUrls&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//省略掉前面的代码...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> registryURLs &lt;span style="color:#719e07">=&lt;/span> ConfigValidationUtils&lt;span style="color:#719e07">.&lt;/span>loadRegistries&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//省略掉后面的代码...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后就是具体注册中心地址的获取过程我们看下：
ConfigValidationUtils的加载注册中心地址方法loadRegistries&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">loadRegistries&lt;/span>&lt;span style="color:#719e07">(&lt;/span>AbstractInterfaceConfig interfaceConfig&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> provider&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// check &amp;amp;&amp;amp; override if necessary
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//省略掉前面的代码...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这里会获取到一个接口配置注册地址例如：registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=9008&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653703292768
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> registries &lt;span style="color:#719e07">=&lt;/span> interfaceConfig&lt;span style="color:#719e07">.&lt;/span>getRegistries&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//省略掉中间的的代码...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> genCompatibleRegistries&lt;span style="color:#719e07">(&lt;/span>interfaceConfig&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">(),&lt;/span> registryList&lt;span style="color:#719e07">,&lt;/span> provider&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ConfigValidationUtils的双注册地址的获取genCompatibleRegistries方法.
前面代码获取到了一个注册中心地址列表例如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>registry:&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=9008&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653703292768
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下面可以看下如果根据配置来转换为应用级注册地址+接口级注册地址&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">genCompatibleRegistries&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel scopeModel&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> registryList&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> provider&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>registryList&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryList&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>registryURL &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>provider&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// for registries enabled service discovery, automatically register interface compatible addresses.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String registerMode&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>SERVICE_REGISTRY_PROTOCOL&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为了更好理解这里简化掉服务发现注册地址配置的逻辑判断过程仅仅看当前例子提供的值走的逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//双注册模式配置查询 对应参数为dubbo.application.register-mode 默认值为all
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registerMode &lt;span style="color:#719e07">=&lt;/span> registryURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_MODE_KEY&lt;span style="color:#719e07">,&lt;/span> ConfigurationUtils&lt;span style="color:#719e07">.&lt;/span>getCachedDynamicProperty&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">,&lt;/span> DUBBO_REGISTER_MODE_DEFAULT_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_REGISTER_MODE_ALL&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果用户配置了一个错误的注册模式配置则只走接口级配置 这里默认值为interface
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isValidRegisterMode&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registerMode &lt;span style="color:#719e07">=&lt;/span> DEFAULT_REGISTER_MODE_INTERFACE&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个逻辑是满足应用级注册就添加一个应用级注册的地址
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">((&lt;/span>DEFAULT_REGISTER_MODE_INSTANCE&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> DEFAULT_REGISTER_MODE_ALL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registryNotExists&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">,&lt;/span> registryList&lt;span style="color:#719e07">,&lt;/span> SERVICE_REGISTRY_PROTOCOL&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL serviceDiscoveryRegistryURL &lt;span style="color:#719e07">=&lt;/span> URLBuilder&lt;span style="color:#719e07">.&lt;/span>from&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>SERVICE_REGISTRY_PROTOCOL&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>removeParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_TYPE_KEY&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryRegistryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个逻辑是满足接口级注册配置就添加一个接口级注册地址
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>DEFAULT_REGISTER_MODE_INTERFACE&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> DEFAULT_REGISTER_MODE_ALL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//省略掉若干代码和括号
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到这里简化的配置比较容易理解了&lt;/p>
&lt;ul>
&lt;li>双注册模式配置查询 对应参数为dubbo.application.register-mode ，默认值为all&lt;/li>
&lt;li>如果用户配置了一个错误的注册模式配置则只走接口级配置 这里默认值为interface&lt;/li>
&lt;li>满足应用级注册就添加一个应用级注册的地址&lt;/li>
&lt;li>满足接口级注册配置就添加一个接口级注册地址&lt;/li>
&lt;/ul>
&lt;p>这个方法是根据服务注册模式来判断使用接口级注册地址还是应用级注册地址分别如下所示：
配置信息：
dubbo.application.register-mode
配置值：&lt;/p>
&lt;ul>
&lt;li>interface
&lt;ul>
&lt;li>接口级注册&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>instance
&lt;ul>
&lt;li>应用级注册&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>all
&lt;ul>
&lt;li>接口级别和应用级都注册&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>最终的注册地址配置如下：
接口级注册地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>registry:&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=9008&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653703292768
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>应用级注册地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">-&lt;/span>discovery&lt;span style="color:#719e07">-&lt;/span>registry&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=10275&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653704425920
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="173-双注册服务数据的注册">17.3 双注册服务数据的注册&lt;/h2>
&lt;h3 id="1731-双注册代码逻辑调用简介">17.3.1 双注册代码逻辑调用简介&lt;/h3>
&lt;p>前面说了这个注册服务的配置地址会由Dubbo内部进行判断如果判断是all的话会自动将一个配置的注册地址转变为两个一个是传统的接口级注册，一个是应用级注册使用的配置地址&lt;/p>
&lt;p>然后我们先看注册中心，注册服务数据的源码
如果想要查看源码细节可以在RegistryProtocol类型的export(final Invoker&lt;T> originInvoker) 方法的如下代码位置打断点：&lt;/p>
&lt;p>RegistryProtocol的export方法的注册中心注册数据代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// url to registry 注册服务对外的接口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果url为service-discovery-registry发现则这个实现类型为ServiceDiscoveryRegistry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Registry registry &lt;span style="color:#719e07">=&lt;/span> getRegistry&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务发现的提供者url: dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;amp;application=dubbo-demo-api-provider&amp;amp;background=false&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=link.elastic.dubbo.entity.DemoService&amp;amp;methods=sayHello,sayHelloAsync&amp;amp;pid=19559&amp;amp;release=3.0.8&amp;amp;service-name-mapping=true&amp;amp;side=provider&amp;amp;timestamp=1654938441023
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> URL registeredProviderUrl &lt;span style="color:#719e07">=&lt;/span> getUrlToRegistry&lt;span style="color:#719e07">(&lt;/span>providerUrl&lt;span style="color:#719e07">,&lt;/span> registryUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// decide if we need to delay publish (provider itself and registry should both need to register)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//register参数是否 注册数据到注册中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> register &lt;span style="color:#719e07">=&lt;/span> providerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registryUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>register&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里有两种情况 接口级注册会将接口级服务提供者数据直接注册到Zookeper上面，服务发现（应用级注册）这里仅仅会将注册数据转换为服务元数据等后面来发布元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> register&lt;span style="color:#719e07">(&lt;/span>registry&lt;span style="color:#719e07">,&lt;/span> registeredProviderUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register stated url on provider model
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//向提供者模型注册提供者配置ProviderModel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registerStatedUrl&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">,&lt;/span> registeredProviderUrl&lt;span style="color:#719e07">,&lt;/span> register&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exporter&lt;span style="color:#719e07">.&lt;/span>setRegisterUrl&lt;span style="color:#719e07">(&lt;/span>registeredProviderUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exporter&lt;span style="color:#719e07">.&lt;/span>setSubscribeUrl&lt;span style="color:#719e07">(&lt;/span>overrideSubscribeUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>registry&lt;span style="color:#719e07">.&lt;/span>isServiceDiscovery&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Deprecated! Subscribe to override rules in 2.6.x or before.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registry&lt;span style="color:#719e07">.&lt;/span>subscribe&lt;span style="color:#719e07">(&lt;/span>overrideSubscribeUrl&lt;span style="color:#719e07">,&lt;/span> overrideSubscribeListener&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在上个博客中我们整体说了下服务注册时候的一个流程，关于数据向注册中心的注册细节这里可以详细看下&lt;/p>
&lt;h3 id="1732--注册中心领域对象的初始化">17.3.2 注册中心领域对象的初始化&lt;/h3>
&lt;p>前面的代码使用url来获取注册中心操作对象如下调用代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// url to registry 注册服务对外的接口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> Registry registry &lt;span style="color:#719e07">=&lt;/span> getRegistry&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对应代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> Registry &lt;span style="color:#268bd2">getRegistry&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> URL registryUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里分为两步先获取注册中心工厂对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RegistryFactory registryFactory &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>RegistryFactory&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> registryUrl&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()).&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用注册中心工厂对象获取注册中心操作对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> registryFactory&lt;span style="color:#719e07">.&lt;/span>getRegistry&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于参数URL有两个在前面已经说过，url信息如下：&lt;/p>
&lt;p>接口级注册地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>registry:&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=9008&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653703292768
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>应用级注册地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">-&lt;/span>discovery&lt;span style="color:#719e07">-&lt;/span>registry&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=10275&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653704425920
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注册中心工厂对象与注册中心操作对象的获取与执行我们通过Debug来看比较麻烦，这里涉及到很多扩展机制动态生成的代码我们无法看到，这里我直接来贴一下比较关键的一些类型，以Zookeeper注册中心来举例子：&lt;/p>
&lt;p>先来看下注册工厂相关的类型：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register.png" alt="在这里插入图片描述">&lt;/p>
&lt;ul>
&lt;li>RegistryFactory 注册中心对象获取&lt;/li>
&lt;li>AbstractRegistryFactory 模板类型封装注册中心对象获取的基本逻辑，比如缓存和基本的逻辑判断&lt;/li>
&lt;li>ServiceDiscoveryRegistryFactory 用于创建服务发现注册中心工厂对象 用于创建ServiceDiscoveryRegistry对象&lt;/li>
&lt;li>ZookeeperRegistryFactory 用于创建ZookeeperRegistry类型对象&lt;/li>
&lt;li>NacosRegistryFactory Nacos注册中心工厂对象 用于创建NacosRegistry&lt;/li>
&lt;/ul>
&lt;p>接下来看封装了注册中心操作逻辑的注册中心领域对象：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register2.png" alt="在这里插入图片描述">&lt;/p>
&lt;ul>
&lt;li>Node 节点信息开放接口 比如节点 url的获取 ，销毁&lt;/li>
&lt;li>RegistryService 注册服务接口，比如注册，订阅，查询等操作&lt;/li>
&lt;li>Registry 注册中心接口，是否服务发现查询，注册，取消注册方法&lt;/li>
&lt;li>AbstractRegistry 注册中心逻辑抽象模板类型，封装了注册，订阅，通知的基本逻辑，和本地缓存注册中心信息的基本逻辑&lt;/li>
&lt;li>FailbackRegistry 封装了失败重试的逻辑&lt;/li>
&lt;li>NacosRegistry 封装了以nacos作为注册中心的基本逻辑&lt;/li>
&lt;li>ServiceDiscoveryRegistry 应用级服务发现注册中心逻辑，现在不需要这种网桥实现，协议可以直接与服务发现交互。ServiceDiscoveryRegistry是一种非常特殊的注册表实现，用于以兼容的方式将旧的接口级服务发现模型与3.0中引入的新服务发现模型连接起来。
它完全符合注册表SPI的扩展规范，但与zookeeper和Nacos的具体实现不同，因为它不与任何真正的第三方注册表交互，而只与过程中ServiceDiscovery的相关组件交互。简而言之，它架起了旧接口模型和新服务发现模型之间的桥梁：register()方法主要通过与MetadataService交互，将接口级数据聚合到MetadataInfo中subscribe() 触发应用程序级服务发现模型的整个订阅过程。-根据ServiceNameMapping将接口映射到应用程序。-启动新的服务发现侦听器（InstanceListener），并使NotifierListener成为InstanceListener的一部分。&lt;/li>
&lt;li>CacheableFailbackRegistry 提供了一些本地内存缓存的逻辑 对注册中心有用，注册中心的sdk将原始字符串作为提供程序实例返回，例如zookeeper和etcd&lt;/li>
&lt;li>ZookeeperRegistry Zookeeper作为注册中心的基本操作逻辑封装&lt;/li>
&lt;/ul>
&lt;p>了解了这几个领域对象这里我们回到代码逻辑，这里直接看将会执行的一些核心逻辑：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> Registry &lt;span style="color:#268bd2">getRegistry&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> URL registryUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里分为两步先获取注册中心工厂对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RegistryFactory registryFactory &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>RegistryFactory&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> registryUrl&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()).&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用注册中心工厂对象获取注册中心操作对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> registryFactory&lt;span style="color:#719e07">.&lt;/span>getRegistry&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面注册中心工厂不论那种协议的地址信息获取到的都是一个RegistryFactory$Adaptive类型（由扩展机制的字节码工具自动生成的代码）&lt;/p>
&lt;p>如果getRegistry参数为应用级注册地址。如下所示将获取到的类型为ServiceDiscoveryRegistryFactory逻辑来获取注册中心：
（这个逻辑是@Adaptive注解产生的了逻辑具体原理可以看扩展机制中@Adaptive的实现）&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">-&lt;/span>discovery&lt;span style="color:#719e07">-&lt;/span>registry&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=10275&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653704425920
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>getRegistry方法优先走的逻辑是这里：AbstractRegistryFactory模板类型中的getRegistry方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Registry &lt;span style="color:#268bd2">getRegistry&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>registryManager &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Unable to fetch RegistryManager from ApplicationModel BeanFactory. &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;Please check if `setApplicationModel` has been override.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//销毁状态直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Registry defaultNopRegistry &lt;span style="color:#719e07">=&lt;/span> registryManager&lt;span style="color:#719e07">.&lt;/span>getDefaultNopRegistryIfDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> defaultNopRegistry&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> defaultNopRegistry&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> URLBuilder&lt;span style="color:#719e07">.&lt;/span>from&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setPath&lt;span style="color:#719e07">(&lt;/span>RegistryService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>addParameter&lt;span style="color:#719e07">(&lt;/span>INTERFACE_KEY&lt;span style="color:#719e07">,&lt;/span> RegistryService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>removeParameter&lt;span style="color:#719e07">(&lt;/span>TIMESTAMP_KEY&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>removeAttribute&lt;span style="color:#719e07">(&lt;/span>EXPORT_KEY&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>removeAttribute&lt;span style="color:#719e07">(&lt;/span>REFER_KEY&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个key为 service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> createRegistryCacheKey&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Registry registry &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//check配置 是否检查注册中心连通 默认为true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> check &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>CHECK_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getPort&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Lock the registry access process to ensure a single instance of the registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//给写操作加锁方式并发写问题
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registryManager&lt;span style="color:#719e07">.&lt;/span>getRegistryLock&lt;span style="color:#719e07">().&lt;/span>lock&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//锁内检查是否销毁的逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// double check
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// fix https://github.com/apache/dubbo/issues/7265.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> defaultNopRegistry &lt;span style="color:#719e07">=&lt;/span> registryManager&lt;span style="color:#719e07">.&lt;/span>getDefaultNopRegistryIfDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> defaultNopRegistry&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> defaultNopRegistry&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//锁内检查是否缓存中存在存在则直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registry &lt;span style="color:#719e07">=&lt;/span> registryManager&lt;span style="color:#719e07">.&lt;/span>getRegistry&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>registry &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> registry&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//create registry by spi/ioc
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//使用url创建注册中心操作的逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registry &lt;span style="color:#719e07">=&lt;/span> createRegistry&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//check配置检查
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>check&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Can not create registry &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LOGGER&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to obtain or create registry &amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Release the lock
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registryManager&lt;span style="color:#719e07">.&lt;/span>getRegistryLock&lt;span style="color:#719e07">().&lt;/span>unlock&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>check &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registry &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Can not create registry &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>registry &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryManager&lt;span style="color:#719e07">.&lt;/span>putRegistry&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> registry&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>逻辑其实吧比较简单，概括下上面的逻辑：&lt;/p>
&lt;ul>
&lt;li>销毁逻辑判断&lt;/li>
&lt;li>缓存获取，存在则直接返回&lt;/li>
&lt;li>根据注册中心url配置，创建注册中心操作对象&lt;/li>
&lt;li>注册中心连接失败的check配置逻辑处理&lt;/li>
&lt;li>将注册中心操作对象存入缓存&lt;/li>
&lt;/ul>
&lt;p>上面比较重要的逻辑是createRegistry这个
整个调用过程我给大家看下Debug的详情，这里很多逻辑由扩展机制产生的这里直接看下逻辑调用栈，有几个需要关注的地方我圈了起来：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register3.png" alt="在这里插入图片描述">
我们继续看服务发现的注册中心工厂对象的获取，代码如下：
ServiceDiscoveryRegistryFactory类型的createRegistry方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> Registry &lt;span style="color:#268bd2">createRegistry&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//判断url是否是这个前缀：service-discovery-registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>hasServiceDiscoveryRegistryProtocol&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//切换下协议：将服务发现协议切换为配置的注册中心协议这里是Zookeeper如下：
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;interface=org.apache.dubbo.registry.RegistryService&amp;amp;pid=39884&amp;amp;release=3.0.8
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String protocol &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_REGISTRY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>protocol&lt;span style="color:#719e07">).&lt;/span>removeParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建服务发现注册中心对象对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceDiscoveryRegistry&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过以上代码可以看到其实最终创建的是一个ServiceDiscoveryRegistry注册中心对象，这个url协议被转换为了对应注册中心的协议，也就是说双注册会有两个协议一个是原先的接口级注册注册中心对象（这个还未说到）和这里对应注册中心协议的服务发现注册中心对象ServiceDiscoveryRegistry&lt;/p>
&lt;h3 id="1733-servicediscoveryregistry">17.3.3 ServiceDiscoveryRegistry&lt;/h3>
&lt;p>ServiceDiscoveryRegistry服务发现注册中心对象的初始化过程：&lt;/p>
&lt;h4 id="17331-servicediscoveryregistry的构造器">17.3.3.1 ServiceDiscoveryRegistry的构造器：&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ServiceDiscoveryRegistry&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL registryURL&lt;span style="color:#719e07">,&lt;/span> ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据url创建一个服务发现对象类型为ServiceDiscovery
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceDiscovery &lt;span style="color:#719e07">=&lt;/span> createServiceDiscovery&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个类型为是serviceNameMapping类型是MetadataServiceNameMapping类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceNameMapping &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>AbstractServiceNameMapping&lt;span style="color:#719e07">)&lt;/span> ServiceNameMapping&lt;span style="color:#719e07">.&lt;/span>getDefaultExtension&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ServiceDiscoveryRegistry中创建服务发现对象createServiceDiscovery方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> ServiceDiscovery &lt;span style="color:#268bd2">createServiceDiscovery&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getServiceDiscovery&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">.&lt;/span>addParameter&lt;span style="color:#719e07">(&lt;/span>INTERFACE_KEY&lt;span style="color:#719e07">,&lt;/span> ServiceDiscovery&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>removeParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_TYPE_KEY&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ServiceDiscoveryRegistry中创建服务发现对象getServiceDiscovery方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> ServiceDiscovery &lt;span style="color:#268bd2">getServiceDiscovery&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//服务发现工厂对象的获取这里是ServiceDiscoveryFactory类型，
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceDiscoveryFactory factory &lt;span style="color:#719e07">=&lt;/span> getExtension&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务发现工厂对象获取服务发现对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> factory&lt;span style="color:#719e07">.&lt;/span>getServiceDiscovery&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ServiceDiscoveryFactory和ServiceDiscovery类型可以往后看&lt;/p>
&lt;h4 id="17332-父类型failbackregistry的构造器">17.3.3.2 父类型FailbackRegistry的构造器&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">FailbackRegistry&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//重试间隔配置retry.period ，默认为5秒
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>retryPeriod &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_RETRY_PERIOD_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_REGISTRY_RETRY_PERIOD&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// since the retry task will not be very much. 128 ticks is enough.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//因为重试任务不会太多。128个刻度就足够了。Dubbo封装的时间轮用于高效率的重试，这个在Kafka也自定义实现了后续可以单独来看看
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> retryTimer &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashedWheelTimer&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> NamedThreadFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;DubboRegistryRetryTimer&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">),&lt;/span> retryPeriod&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">128&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="17333-abstractregistry的构造器">17.3.3.3 AbstractRegistry的构造器&lt;/h4>
&lt;p>参数url如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>zookeeper:&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;interface=org.apache.dubbo.registry.RegistryService&amp;amp;pid=39884&amp;amp;release=3.0.8
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractRegistry&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setUrl&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryManager &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getOrDefaultApplicationModel&lt;span style="color:#719e07">().&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>RegistryManager&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否本地缓存默认为true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> localCacheEnabled &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_LOCAL_FILE_CACHE_ENABLED&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryCacheExecutor &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getOrDefaultFrameworkModel&lt;span style="color:#719e07">().&lt;/span>getBeanFactory&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>FrameworkExecutorRepository&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSharedExecutor&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>localCacheEnabled&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Start file save timer 是否同步缓存默认为false
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> syncSaveFile &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_FILESAVE_SYNC_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//默认缓存的文件路径与文件名字为：/Users/song/.dubbo/dubbo-registry-dubbo-demo-api-provider-127.0.0.1-2181.cache
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String defaultFilename &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>USER_HOME&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span> DUBBO_REGISTRY &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#719e07">.&lt;/span>getApplication&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;-&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">().&lt;/span>replaceAll&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;:&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;-&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span> CACHE&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//未指定缓存的文件名字则用默认的文件名字
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String filename &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>FILE_KEY&lt;span style="color:#719e07">,&lt;/span> defaultFilename&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> File file &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//父目录创建，保证目录存在
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>filename&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> file &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> File&lt;span style="color:#719e07">(&lt;/span>filename&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>file&lt;span style="color:#719e07">.&lt;/span>exists&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> file&lt;span style="color:#719e07">.&lt;/span>getParentFile&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>file&lt;span style="color:#719e07">.&lt;/span>getParentFile&lt;span style="color:#719e07">().&lt;/span>exists&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>file&lt;span style="color:#719e07">.&lt;/span>getParentFile&lt;span style="color:#719e07">().&lt;/span>mkdirs&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Invalid registry cache file &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> file &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, cause: Failed to create directory &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> file&lt;span style="color:#719e07">.&lt;/span>getParentFile&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>file &lt;span style="color:#719e07">=&lt;/span> file&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// When starting the subscription center,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// we need to read the local cache file for future Registry fault tolerance processing.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//加载本地磁盘文件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadProperties&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//变更推送
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> notify&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getBackupUrls&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1734-将服务提供者数据转换到本地内存的元数据信息中">17.3.4 将服务提供者数据转换到本地内存的元数据信息中&lt;/h3>
&lt;p>在前面我们看到了RegistryProtocol中调用register来注册服务提供者的数据到注册的中心，接下来详细看下实现原理：
下面参数为ServiceDiscoveryRegistry为情况下举例子：ServiceDiscoveryRegistry类型的register方法与ZookeeperRegister注册不一样传统的接口级注册在这个方法里面就将服务数据注册到注册中心了，服务发现的数据注册分为了两步，这里仅仅将数据封装到内存中如下：
url例子为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>dubbo:&lt;span style="color:#586e75">//192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;amp;application=dubbo-demo-api-provider&amp;amp;background=false&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=link.elastic.dubbo.entity.DemoService&amp;amp;methods=sayHello,sayHelloAsync&amp;amp;pid=19559&amp;amp;release=3.0.8&amp;amp;service-name-mapping=true&amp;amp;side=provider&amp;amp;timestamp=1654938441023
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>RegistryProtocol中的register方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">register&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Registry registry&lt;span style="color:#719e07">,&lt;/span> URL registeredProviderUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>registeredProviderUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面这个代码会优先走ListenerRegistryWrapper的一些逻辑来执行register方法来触发一些监听器的逻辑，我们直接跳到ServiceDiscoveryRegistry中的register方法来看&lt;/p>
&lt;p>ServiceDiscoveryRegistry的register方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">register&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//逻辑判断比如只有side为提供者时候才能注册
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>shouldRegister&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#586e75">// Should Not Register
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doRegister&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ServiceDiscoveryRegistry的doRegister方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doRegister&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// fixme, add registry-cluster is not necessary anymore
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> url &lt;span style="color:#719e07">=&lt;/span> addRegistryClusterKey&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceDiscovery&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>AbstractServiceDiscovery的register方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">register&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//metadaInfo类型为MetadataInfo类型，用来操作元数据的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> metadataInfo&lt;span style="color:#719e07">.&lt;/span>addService&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>MetadataInfo 类型的addService方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addService&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// fixme, pass in application mode context during initialization of MetadataInfo.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//元数据参数过滤器扩展获取:MetadataParamsFilter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>loader &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>loader &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getOrDefaultApplicationModel&lt;span style="color:#719e07">().&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>MetadataParamsFilter&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//元数据参数过滤器获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataParamsFilter&lt;span style="color:#719e07">&amp;gt;&lt;/span> filters &lt;span style="color:#719e07">=&lt;/span> loader&lt;span style="color:#719e07">.&lt;/span>getActivateExtension&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;params-filter&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// generate service level metadata
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//生成服务级别的元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceInfo serviceInfo &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceInfo&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> filters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>services&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>serviceInfo&lt;span style="color:#719e07">.&lt;/span>getMatchKey&lt;span style="color:#719e07">(),&lt;/span> serviceInfo&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// extract common instance level params
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> extractInstanceParams&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> filters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>exportedServiceURLs &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportedServiceURLs &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentSkipListMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> addURL&lt;span style="color:#719e07">(&lt;/span>exportedServiceURLs&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> updated &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1735-接口级服务提供者配置的注册">17.3.5 接口级服务提供者配置的注册&lt;/h3>
&lt;p>前面我们通过服务发现的的url进行了举例子，其实在RegistryProtocol协议的export方法中还会注册接口级信息：
例如如下关键代码：
当registryUrl参数不是服务发现协议service-discovery-registry配置而是zookeeper如下时候获取到的扩展类型将是与Zookeeper相关的扩展对象&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>zookeeper:&lt;span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=29386&amp;amp;release=3.0.8&amp;amp;timestamp=1655023329438
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>RegistryProtocol协议的export方法中接口级数据注册的核心代码如下：
如下代码的操作类型可以看注释&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// url to registry 这里registry对象的类型为ZookeeperRegistry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Registry registry &lt;span style="color:#719e07">=&lt;/span> getRegistry&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> URL registeredProviderUrl &lt;span style="color:#719e07">=&lt;/span> getUrlToRegistry&lt;span style="color:#719e07">(&lt;/span>providerUrl&lt;span style="color:#719e07">,&lt;/span> registryUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// decide if we need to delay publish (provider itself and registry should both need to register)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> register &lt;span style="color:#719e07">=&lt;/span> providerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registryUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这一个方法里面会将提供者的url配置写入Zookeeper的provider节点下面
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>register&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> register&lt;span style="color:#719e07">(&lt;/span>registry&lt;span style="color:#719e07">,&lt;/span> registeredProviderUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如上代码是获取Zookeeper操作对象和向Zookeeper中写入服务提供者信息的代码，关于与Zookeeper连接和注册数据本地缓存的代码可以看ZookeeperRegistry类型和它的几个父类型比如：CacheableFailbackRegistry类型，关于接口级数据的注册可以看register方法，这个就不详细说了，下面我贴一下接口级数据注册的Zookeeper信息可以了解下就行：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register4.png" alt="在这里插入图片描述">
接口信息如下，上面我们需要注意的是这个 url配置为临时节点，当与Zookeeper断开连接或者Session超时的时候这个信息会被移除：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">/&lt;/span>dubbo&lt;span style="color:#719e07">/&lt;/span>link&lt;span style="color:#719e07">.&lt;/span>elastic&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>entity&lt;span style="color:#719e07">.&lt;/span>DemoService&lt;span style="color:#719e07">/&lt;/span>providers&lt;span style="color:#719e07">/&lt;/span>dubbo&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3&lt;/span>A&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">2F&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">2F192.168.1.9&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3&lt;/span>A20880&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">2F&lt;/span>link&lt;span style="color:#719e07">.&lt;/span>elastic&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>entity&lt;span style="color:#719e07">.&lt;/span>DemoService&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3F&lt;/span>anyhost&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>application&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>dubbo&lt;span style="color:#719e07">-&lt;/span>demo&lt;span style="color:#719e07">-&lt;/span>api&lt;span style="color:#719e07">-&lt;/span>provider&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>background&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>deprecated&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>dubbo&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D2.0.2&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>dynamic&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>generic&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>interface&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>link&lt;span style="color:#719e07">.&lt;/span>elastic&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>entity&lt;span style="color:#719e07">.&lt;/span>DemoService&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>methods&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>sayHello&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">2&lt;/span>CsayHelloAsync&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>pid&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>&lt;span style="color:#2aa198">29386&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>release&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D3.0.8&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>service&lt;span style="color:#719e07">-&lt;/span>name&lt;span style="color:#719e07">-&lt;/span>mapping&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>side&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>provider&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">26&lt;/span>timestamp&lt;span style="color:#719e07">%&lt;/span>&lt;span style="color:#2aa198">3D&lt;/span>&lt;span style="color:#2aa198">1655023329514&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="174-应用级服务发现功能的实现servicediscovery">17.4 应用级服务发现功能的实现ServiceDiscovery&lt;/h2>
&lt;p>在说这个实现之前我们先看看相关类型，这个服务发现相关的类型与注册中心相关的类型有点类似：&lt;/p>
&lt;p>服务发现工厂类型：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register5.png" alt="在这里插入图片描述">
服务发现类型：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register6.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>刚刚在 ServiceDiscoveryRegistry中创建服务发现对象getServiceDiscovery方法看到了两个类型一个是服务发现工厂类型ServiceDiscoveryFactory，一个是服务发现类型ServiceDiscovery&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> ServiceDiscovery &lt;span style="color:#268bd2">getServiceDiscovery&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//服务发现工厂对象的获取这里是ServiceDiscoveryFactory类型，这里对应ZookeeperServiceDiscoveryFactory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceDiscoveryFactory factory &lt;span style="color:#719e07">=&lt;/span> getExtension&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务发现工厂对象获取服务发现对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> factory&lt;span style="color:#719e07">.&lt;/span>getServiceDiscovery&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>AbstractServiceDiscoveryFactory类型的getServiceDiscovery方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> ServiceDiscovery &lt;span style="color:#268bd2">getServiceDiscovery&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个key是 zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.client.ServiceDiscovery
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//一个地址需要创建一个服务发现对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> registryURL&lt;span style="color:#719e07">.&lt;/span>toServiceStringWithoutResolving&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> discoveries&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> k &lt;span style="color:#719e07">-&amp;gt;&lt;/span> createDiscovery&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>createDiscovery方法对应ZookeeperServiceDiscoveryFactory类型中的createDiscovery方法&lt;/p>
&lt;p>如下代码所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> ServiceDiscovery &lt;span style="color:#268bd2">createDiscovery&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ZookeeperServiceDiscovery&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1741-zookeeperservicediscovery">17.4.1 ZookeeperServiceDiscovery&lt;/h3>
&lt;p>ZookeeperServiceDiscovery的构造器&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ZookeeperServiceDiscovery&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">,&lt;/span> URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//先调用父类AbstractServiceDiscovery 模板类构造器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建 创建CuratorFramework 类型对象用于操作Zookeeper
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>curatorFramework &lt;span style="color:#719e07">=&lt;/span> buildCuratorFramework&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取应用级服务发现的根路径 值为/services 这个可以在Zookeeper上面看到
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>rootPath &lt;span style="color:#719e07">=&lt;/span> ROOT_PATH&lt;span style="color:#719e07">.&lt;/span>getParameterValue&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建服务发现对象 实现类型为ServiceDiscoveryImpl 这个实现来源于Curator框架中的discovery模块
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceDiscovery &lt;span style="color:#719e07">=&lt;/span> buildServiceDiscovery&lt;span style="color:#719e07">(&lt;/span>curatorFramework&lt;span style="color:#719e07">,&lt;/span> rootPath&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//启动服务发现
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceDiscovery&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Create zookeeper service discovery failed.&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个方法比较重要是应用级服务发现的实现，这里主要关注下serviceDiscovery类型的创建与启动，这个应用级服务发现的实现其实是Dubbo使用了Curator来做的，Dubbo只是在这里封装了一些方法来进行调用Curator的实现：
关于Curator的官方文档可以看&lt;a href="https://curator.apache.org/">curator官网&lt;/a>&lt;/p>
&lt;p>关于Zookeeper上面注册服务应用级服务注册信息可以看如下图所示(后面会具体讲到数据注册时的调用）：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register7.png" alt="在这里插入图片描述">
我这个服务提供者注册的应用数据如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;name&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;id&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;192.168.1.9:20880&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;address&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;192.168.1.9&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;port&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">20880&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;sslPort&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;payload&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;@class&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.registry.zookeeper.ZookeeperInstance&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;id&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;192.168.1.9:20880&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;name&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;metadata&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo.endpoints&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;[{\&amp;#34;port\&amp;#34;:20880,\&amp;#34;protocol\&amp;#34;:\&amp;#34;dubbo\&amp;#34;}]&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo.metadata-service.url-params&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;{\&amp;#34;connections\&amp;#34;:\&amp;#34;1\&amp;#34;,\&amp;#34;version\&amp;#34;:\&amp;#34;1.0.0\&amp;#34;,\&amp;#34;dubbo\&amp;#34;:\&amp;#34;2.0.2\&amp;#34;,\&amp;#34;release\&amp;#34;:\&amp;#34;3.0.8\&amp;#34;,\&amp;#34;side\&amp;#34;:\&amp;#34;provider\&amp;#34;,\&amp;#34;port\&amp;#34;:\&amp;#34;20880\&amp;#34;,\&amp;#34;protocol\&amp;#34;:\&amp;#34;dubbo\&amp;#34;}&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo.metadata.revision&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;a662fd2213a8a49dc6ff43a4c2ae7b9e&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo.metadata.storage-type&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;local&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;timestamp&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1654916298616&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">},&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;registrationTimeUTC&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">1654917265499&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;serviceType&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;DYNAMIC&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;uriSpec&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果感兴趣的话可以看更详细的curator服务发现文档&lt;a href="https://curator.apache.org/curator-x-discovery/index.html">curator-x-discovery&lt;/a>&lt;/p>
&lt;h3 id="1742-abstractservicediscovery的构造器">17.4.2 AbstractServiceDiscovery的构造器&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractServiceDiscovery&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">,&lt;/span> URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用重载的构造器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationName&lt;span style="color:#719e07">(),&lt;/span> registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetadataReportInstance metadataReportInstance &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>MetadataReportInstance&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataType &lt;span style="color:#719e07">=&lt;/span> metadataReportInstance&lt;span style="color:#719e07">.&lt;/span>getMetadataType&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataReport &lt;span style="color:#719e07">=&lt;/span> metadataReportInstance&lt;span style="color:#719e07">.&lt;/span>getMetadataReport&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_CLUSTER_KEY&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataReportInstance.getMetadataType())) {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// this.metadataReport = metadataReportInstance.getMetadataReport(registryURL.getParameter(REGISTRY_CLUSTER_KEY));
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// } else {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// this.metadataReport = metadataReportInstance.getNopMetadataReport();
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>重载的构造器&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractServiceDiscovery&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String serviceName&lt;span style="color:#719e07">,&lt;/span> URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModel &lt;span style="color:#719e07">=&lt;/span> ApplicationModel&lt;span style="color:#719e07">.&lt;/span>defaultModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个url参考：zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;interface=org.apache.dubbo.registry.client.ServiceDiscovery&amp;amp;pid=4570&amp;amp;release=3.0.8
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>registryURL &lt;span style="color:#719e07">=&lt;/span> registryURL&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个serviceName参考dubbo-demo-api-provider
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceName &lt;span style="color:#719e07">=&lt;/span> serviceName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//MetadataInfo 用来封装元数据信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataInfo &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MetadataInfo&lt;span style="color:#719e07">(&lt;/span>serviceName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个是元数据缓存信息管理的类型 缓存文件使用LRU策略 感兴趣的可以详细看看
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//对应缓存路径为：/Users/song/.dubbo/.metadata.zookeeper127.0.0.1%003a2181.dubbo.cache
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metaCacheManager &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MetaCacheManager&lt;span style="color:#719e07">(&lt;/span>getCacheNameSuffix&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getFrameworkModel&lt;span style="color:#719e07">().&lt;/span>getBeanFactory&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>FrameworkExecutorRepository&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getCacheRefreshingScheduledExecutor&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="175-服务映射类型abstractservicenamemapping">17.5 服务映射类型AbstractServiceNameMapping&lt;/h2>
&lt;p>服务映射主要是通过服务名字来反查应用信息的应用名字如下图所示
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register8.png" alt="在这里插入图片描述">
这里我们来看下服务映射相关的类型主要通过如下代码来获取扩展对象：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceNameMapping &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>AbstractServiceNameMapping&lt;span style="color:#719e07">)&lt;/span> ServiceNameMapping&lt;span style="color:#719e07">.&lt;/span>getDefaultExtension&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对应类型如下：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register9.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>最终获取的扩展实现类型为：MetadataServiceNameMapping
构造器如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">MetadataServiceNameMapping&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataReportInstance &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>MetadataReportInstance&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务映射元数据父类型AbstractServiceNameMapping如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractServiceNameMapping&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//LRU缓存保存服务映射数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>mappingCacheManager &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MappingCacheManager&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getFrameworkModel&lt;span style="color:#719e07">().&lt;/span>getBeanFactory&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>FrameworkExecutorRepository&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getCacheRefreshingScheduledExecutor&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="174-双注册元数据信息发布到注册中心">17.4 双注册元数据信息发布到注册中心&lt;/h2>
&lt;h3 id="1741-回顾简介">17.4.1 回顾简介&lt;/h3>
&lt;p>前面注册数据的时候并没有把服务配置的元数据直接注册在注册中心而是需要在导出服务之后在ServiceConfig中来发布元数据，这个就需要我们回到ServiceConfig的exportUrl方法来看了如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportUrl&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> registryURLs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String scope &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>SCOPE_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// don&amp;#39;t export when none is configured
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>省略到若干代码
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>SCOPE_LOCAL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>scope&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> exportRemote&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> registryURLs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isGeneric&lt;span style="color:#719e07">(&lt;/span>generic&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>getScopeModel&lt;span style="color:#719e07">().&lt;/span>isInternal&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetadataUtils&lt;span style="color:#719e07">.&lt;/span>publishServiceDefinition&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> providerModel&lt;span style="color:#719e07">.&lt;/span>getServiceModel&lt;span style="color:#719e07">(),&lt;/span> getApplicationModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>urls&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1742-元数据服务定义数据的发布">17.4.2 元数据服务定义数据的发布&lt;/h3>
&lt;p>在exportRemote之后单独调用发布元数据的方法来发布，通过调用元数据工具类来发布元数据信息接下来我们详细看下:
MetadataUtils类型的publishServiceDefinition方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">publishServiceDefinition&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> ServiceDescriptor serviceDescriptor&lt;span style="color:#719e07">,&lt;/span> ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//查询是否存在元数据存储对象 对应接口MetadataReport 这里对应实现类 ZookeeperMetadataReport
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>getMetadataReports&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">).&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String msg &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Remote Metadata Report Server is not provided or unavailable, will stop registering service definition to remote center!&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>msg&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String side &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getSide&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务提供者走这个逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>PROVIDER_SIDE&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>side&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String serviceKey &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getServiceKey&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取当前服务元数据信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> FullServiceDefinition serviceDefinition &lt;span style="color:#719e07">=&lt;/span> serviceDescriptor&lt;span style="color:#719e07">.&lt;/span>getFullServiceDefinition&lt;span style="color:#719e07">(&lt;/span>serviceKey&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>serviceKey&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> serviceDefinition &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceDefinition&lt;span style="color:#719e07">.&lt;/span>setParameters&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> MetadataReport&lt;span style="color:#719e07">&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> getMetadataReports&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">).&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetadataReport metadataReport &lt;span style="color:#719e07">=&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>metadataReport&lt;span style="color:#719e07">.&lt;/span>shouldReportDefinition&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Report of service definition is disabled for &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//存储服务提供者的元数据 metadataReport类型为ZookeeperMetadataReport 方法来源于父类模板方法： AbstractMetadataReport类型的storeProviderMetadata模板方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> metadataReport&lt;span style="color:#719e07">.&lt;/span>storeProviderMetadata&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> MetadataIdentifier&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#719e07">.&lt;/span>getServiceInterface&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#719e07">.&lt;/span>getVersion&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getVersion&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> PROVIDER_SIDE&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationName&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">,&lt;/span> serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务消费者走这个逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> MetadataReport&lt;span style="color:#719e07">&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> getMetadataReports&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">).&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetadataReport metadataReport &lt;span style="color:#719e07">=&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>metadataReport&lt;span style="color:#719e07">.&lt;/span>shouldReportDefinition&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Report of service definition is disabled for &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataReport&lt;span style="color:#719e07">.&lt;/span>storeConsumerMetadata&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> MetadataIdentifier&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#719e07">.&lt;/span>getServiceInterface&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#719e07">.&lt;/span>getVersion&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getVersion&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CONSUMER_SIDE&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationName&lt;span style="color:#719e07">()),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//ignore error
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;publish service definition metadata error.&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>AbstractMetadataReport的storeProviderMetadata方法如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">storeProviderMetadata&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataIdentifier providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> ServiceDefinition serviceDefinition&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否同步配置对应sync-report 默认为异步
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>syncReport&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storeProviderMetadataTask&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportCacheExecutor&lt;span style="color:#719e07">.&lt;/span>execute&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> storeProviderMetadataTask&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> serviceDefinition&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>AbstractMetadataReport的存储元数据方法storeProviderMetadataTask&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">storeProviderMetadataTask&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataIdentifier providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> ServiceDefinition serviceDefinition&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;store provider metadata. Identifier : &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> providerMetadataIdentifier &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;; definition: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> allMetadataReports&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failedReports&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Gson gson &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Gson&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String data &lt;span style="color:#719e07">=&lt;/span> gson&lt;span style="color:#719e07">.&lt;/span>toJson&lt;span style="color:#719e07">(&lt;/span>serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doStoreProviderMetadata&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> data&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> saveProperties&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> data&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">!&lt;/span>syncReport&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// retry again. If failed again, throw exception.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> failedReports&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataReportRetry&lt;span style="color:#719e07">.&lt;/span>startRetryTask&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to put provider metadata &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> providerMetadataIdentifier &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; in &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> serviceDefinition &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, cause: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/17-register10.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>元数据信息如下：可以分为两类 应用元数据，服务元数据&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameters&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;side&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;provider&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;interface&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;link.elastic.dubbo.entity.DemoService&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;pid&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;22099&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;application&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dubbo&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;2.0.2&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;release&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3.0.8&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;anyhost&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;bind.ip&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;192.168.1.9&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;methods&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHello,sayHelloAsync&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;background&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;deprecated&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dynamic&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;service-name-mapping&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;generic&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;bind.port&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;20880&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;timestamp&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;1654942353902&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;canonicalName&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;link.elastic.dubbo.entity.DemoService&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;codeSource&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;file:/Users/song/Desktop/dubbo-test/target/classes/&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;methods&amp;#34;&lt;/span>: [{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHelloAsync&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameterTypes&amp;#34;&lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;returnType&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;annotations&amp;#34;&lt;/span>: []
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }, {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameterTypes&amp;#34;&lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;returnType&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;annotations&amp;#34;&lt;/span>: []
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;types&amp;#34;&lt;/span>: [{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;result&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.Object&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;stack&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture.Completion&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }, {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.Object&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }, {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }, {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture.Completion&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;next&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture.Completion&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;status&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;int&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }, {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;int&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;annotations&amp;#34;&lt;/span>: []
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Zookeeper扩展类型ZookeeperMetadataReport实现的存储方法如下所示doStoreProviderMetadata：&lt;/p>
&lt;p>如果我们自己实现一套元数据就可以重写这个方法来进行元数据的额存储&lt;/p>
&lt;p>ZookeeperMetadataReport的doStoreProviderMetadata&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doStoreProviderMetadata&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataIdentifier providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> String serviceDefinitions&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storeMetadata&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> serviceDefinitions&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ZookeeperMetadataReport的storeMetadata&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">storeMetadata&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataIdentifier metadataIdentifier&lt;span style="color:#719e07">,&lt;/span> String v&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//参数false为非临时节点，这个元数据为持久节点，这个细节就暂时不看了就是将刚刚的json元数据存储到对应路径上面：路径为：/dubbo/metadata/link.elastic.dubbo.entity.DemoService/provider/dubbo-demo-api-provider
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> zkClient&lt;span style="color:#719e07">.&lt;/span>create&lt;span style="color:#719e07">(&lt;/span>getNodePath&lt;span style="color:#719e07">(&lt;/span>metadataIdentifier&lt;span style="color:#719e07">),&lt;/span> v&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文地址：&lt;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服务提供者的双注册原理&lt;/a>&lt;/p></description></item><item><title>Blog: 16-模块发布器发布服务全过程</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="16-模块发布器发布服务全过程">16-模块发布器发布服务全过程&lt;/h1>
&lt;h2 id="161-简介">16.1 简介&lt;/h2>
&lt;p>Dubbo做为服务治理框架,比较核心的就是服务相关的概念,这里我先贴个找到的关于Dubbo工作原理的架构图:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/16-deploy.png" alt="在这里插入图片描述">
如果按完整服务启动与订阅的顺序我们可以归结为以下6点:&lt;/p>
&lt;ul>
&lt;li>导出服务(提供者)
&lt;ul>
&lt;li>服务提供方通过指定端口对外暴露服务&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>注册服务(提供者)
&lt;ul>
&lt;li>提供方向注册中心注册自己的信息&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>(服务发现)-订阅服务(消费者)
&lt;ul>
&lt;li>服务调用方通过注册中心订阅自己感兴趣的服务&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>(服务发现)-服务推送(消费者)
&lt;ul>
&lt;li>注册中心向调用方推送地址列表&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>调用服务(消费者调用提供者)
&lt;ul>
&lt;li>调用方选择一个地址发起RPC调用&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>监控服务
&lt;ul>
&lt;li>服务提供方和调用方的统计数据由监控模块收集展示&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>上面的完整的服务启动订阅与调用流程不仅仅适用于Dubbo 同样也适用于其他服务治理与发现的模型, 一般服务发现与服务调用的思路就是这样的,我们将以上内容扩展,暴漏服务可以使用http,tcp,udp等各种协议,注册服务可以注册到Redis,Dns,Etcd,Zookeeper等注册中心中,订阅服务可以主动去注册中心查询服务列表,服务发现可以让注册中心将服务数据动态推送给消费者.Dubbo其实就是基于这种简单的服务模型来扩展出各种功能的支持,来满足服务治理的各种场景,了解了这里可能各位同学就想着自行开发一个简单的微服务框架了。&lt;/p>
&lt;p>回到主题,从以上的服务完整发布调用流程可以看到,所有的功能都是由导出服务(提供者)开始的,只有提供者先提供了服务才可以有真正的服务让消费者调用。&lt;/p>
&lt;p>之前的博客内容 链接:&lt;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/">&amp;laquo;12-全局视野来看Dubbo3.0.8的服务启动生命周期&amp;raquo;&lt;/a> 我们了解了 DefaultModuleDeployer模块器启动的流程,其中在start代码的模版方法中开始了导出服务的功能,这里我们来详细看下服务发布的全过程:&lt;/p>
&lt;p>入口代码: DefaultModuleDeployer的发布服务方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportServices&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ServiceConfigBase sc &lt;span style="color:#719e07">:&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getServices&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportServiceInternal&lt;span style="color:#719e07">(&lt;/span>sc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="162-导出服务的入口">16.2 导出服务的入口&lt;/h2>
&lt;p>入口代码: DefaultModuleDeployer的发布服务方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportServices&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ServiceConfigBase sc &lt;span style="color:#719e07">:&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getServices&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportServiceInternal&lt;span style="color:#719e07">(&lt;/span>sc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>主要流程为遍历初始化的服务配置列表然后逐个服务开始到处
内部导出服务代码:
exportServiceInternal方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportServiceInternal&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ServiceConfigBase sc&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> serviceConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ServiceConfig&lt;span style="color:#719e07">&amp;lt;?&amp;gt;)&lt;/span> sc&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务配置刷新 配置优先级覆盖
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>serviceConfig&lt;span style="color:#719e07">.&lt;/span>isRefreshed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务已经导出过了就直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>sc&lt;span style="color:#719e07">.&lt;/span>isExported&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否异步方式导出 全局配置或者服务级其中一个配置了异步则异步处理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>exportAsync &lt;span style="color:#719e07">||&lt;/span> sc&lt;span style="color:#719e07">.&lt;/span>shouldExportAsync&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//异步其实就是使用线程来导出服务serviceExportExecutor
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExecutorService executor &lt;span style="color:#719e07">=&lt;/span> executorRepository&lt;span style="color:#719e07">.&lt;/span>getServiceExportExecutor&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>Void&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>runAsync&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>sc&lt;span style="color:#719e07">.&lt;/span>isExported&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sc&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportedServices&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>sc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; export async catch error : &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">},&lt;/span> executor&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncExportingFutures&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>future&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//同步导出服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>sc&lt;span style="color:#719e07">.&lt;/span>isExported&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sc&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportedServices&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>sc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个逻辑里面做了一些基本的操作,可以直接看注释然后调用ServiceConfig的export的来导出服务,继续往后看服务配置的导出服务方法。&lt;/p>
&lt;h2 id="163-服务配置导出服务模板方法">16.3 服务配置导出服务模板方法&lt;/h2>
&lt;p>核心的服务导出代码是在服务配置中来做的ServiceConfig的 export() 方法
ServiceConfig的 export() 方法代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//已经导出过服务直接放那会
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>exported&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ensure start module, compatible with old api usage
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//确保模块启动了(基本的初始化操作执行了)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> getScopeModel&lt;span style="color:#719e07">().&lt;/span>getDeployer&lt;span style="color:#719e07">().&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//悲观锁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//双重校验
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>exported&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置是否刷新 前面初始化时候已经刷新过配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>isRefreshed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务导出配置配置为false则不导出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>shouldExport&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务发布前初始化一下元数据对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>init&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>shouldDelay&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置了服务的延迟发布配置则走延迟发布逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> doDelayExport&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//导出服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> doExport&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1631-服务配置导出服务前的初始化方法">16.3.1 服务配置导出服务前的初始化方法&lt;/h3>
&lt;p>ServiceConfig 导出服务之前的初始化方法init&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>initialized&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载服务监听器 这里暂时没有服务监听器扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// load ServiceListeners from extension
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ServiceListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> extensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ServiceListener&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceListeners&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>extensionLoader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务提供者配置传递给元数据配置对象 一个服务提供者配置会有一个元数据配置，服务配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initServiceMetadata&lt;span style="color:#719e07">(&lt;/span>provider&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>setServiceType&lt;span style="color:#719e07">(&lt;/span>getInterfaceClass&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>setTarget&lt;span style="color:#719e07">(&lt;/span>getRef&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//元数据的key格式为 group/服务接口:版本号
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>generateServiceKey&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="164-服务配置导出服务模板方法2">16.4 服务配置导出服务模板方法2&lt;/h2>
&lt;p>ServiceConfig 导出服务核心逻辑&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doExport&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//取消发布
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>unexported&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;The service &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> interfaceClass&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; has already unexported!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//已经发布
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>exported&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务路径 为空则设置为接口名，本例子中为link.elastic.dubbo.entity.DemoService
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path &lt;span style="color:#719e07">=&lt;/span> interfaceName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//导出URL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> doExportUrls&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> exported&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1641-导出服务的url配置逻辑">16.4.1 导出服务的URL配置逻辑&lt;/h3>
&lt;p>ServiceConfig 导出URL核心逻辑&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doExportUrls&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//模块服务存储库
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ModuleServiceRepository repository &lt;span style="color:#719e07">=&lt;/span> getScopeModel&lt;span style="color:#719e07">().&lt;/span>getServiceRepository&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceDescriptor serviceDescriptor&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//ref为服务实现类型 这里对应我们例子的DemoServiceImpl
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> serverService &lt;span style="color:#719e07">=&lt;/span> ref &lt;span style="color:#719e07">instanceof&lt;/span> ServerService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span>&lt;span style="color:#719e07">(&lt;/span>serverService&lt;span style="color:#719e07">){&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceDescriptor&lt;span style="color:#719e07">=((&lt;/span>ServerService&lt;span style="color:#719e07">)&lt;/span> ref&lt;span style="color:#719e07">).&lt;/span>getServiceDescriptor&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> repository&lt;span style="color:#719e07">.&lt;/span>registerService&lt;span style="color:#719e07">(&lt;/span>serviceDescriptor&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>&lt;span style="color:#719e07">else&lt;/span>&lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//我们代码走这个逻辑 注册服务 这个注册不是向注册中心注册 这个是解析服务接口将服务方法等描述信息存放在了服务存储ModuleServiceRepository类型对象的成员变量services中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceDescriptor &lt;span style="color:#719e07">=&lt;/span> repository&lt;span style="color:#719e07">.&lt;/span>registerService&lt;span style="color:#719e07">(&lt;/span>getInterfaceClass&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//提供者领域模型， 提供者领域模型 封装了一些提供者需要的就基本属性同时内部解析封装方法信息 ProviderMethodModel 列表 ， 服务标识符 格式group/服务接:版本号
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> providerModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ProviderModel&lt;span style="color:#719e07">(&lt;/span>getUniqueServiceName&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务实现类DemoServiceImpl
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ref&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务描述符 描述符里面包含了服务接口的方法信息，不过服务接口通过反射也可以拿到方法信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceDescriptor&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//当前所处模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> getScopeModel&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//当前服务接口的元数据对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//模块服务存储库存储提供者模型对象ModuleServiceRepository
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> repository&lt;span style="color:#719e07">.&lt;/span>registerProvider&lt;span style="color:#719e07">(&lt;/span>providerModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取配置的注册中心列表 ，同时将注册中心配置转URL （在Dubbo中URL就是配置信息的一种形式）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这里会获取到两个 由dubbo.application.register-mode 双注册配置决定
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//注册中心 registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=9008&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653703292768
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//service-discovery-registry://8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=10275&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653704425920
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//参数dubbo是dubbo协议的版本不是Dubbo版本 Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这里后面详细说下 服务双注册 dubbo.application.register-mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> registryURLs &lt;span style="color:#719e07">=&lt;/span> ConfigValidationUtils&lt;span style="color:#719e07">.&lt;/span>loadRegistries&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ProtocolConfig protocolConfig &lt;span style="color:#719e07">:&lt;/span> protocols&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String pathKey &lt;span style="color:#719e07">=&lt;/span> URL&lt;span style="color:#719e07">.&lt;/span>buildKey&lt;span style="color:#719e07">(&lt;/span>getContextPath&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>map&lt;span style="color:#719e07">(&lt;/span>p &lt;span style="color:#719e07">-&amp;gt;&lt;/span> p &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;/&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> path&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>orElse&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">),&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// stub service will use generated service name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span>&lt;span style="color:#719e07">(!&lt;/span>serverService&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// In case user specified path, register service one more time to map it to path.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//模块服务存储库ModuleServiceRepository存储服务接口信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> repository&lt;span style="color:#719e07">.&lt;/span>registerService&lt;span style="color:#719e07">(&lt;/span>pathKey&lt;span style="color:#719e07">,&lt;/span> interfaceClass&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//导出根据协议导出配置到注册中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> doExportUrlsFor1Protocol&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">,&lt;/span> registryURLs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1642-应用级和接口级服务注册地址获取">16.4.2 应用级和接口级服务注册地址获取&lt;/h3>
&lt;p>这里主要看下注册中心的获取，这里涉及到服务的双注册配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> registryURLs &lt;span style="color:#719e07">=&lt;/span> ConfigValidationUtils&lt;span style="color:#719e07">.&lt;/span>loadRegistries&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于loadRegistries方法的详情我们就不看了主要看loadRegistries方法中调用的genCompatibleRegistries添加服务发现注册中心&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param scopeModel 域模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param registryList 配置的注册中心列表 例如：registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=9008&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653703292768
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param provider 是否为服务提供者 这里Demo为true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">genCompatibleRegistries&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel scopeModel&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> registryList&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> provider&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>registryList&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历所有的注册中心 为每个注册中心增加兼容的服务发现注册中心地址配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registryList&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>registryURL &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否为提供者
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>provider&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// for registries enabled service discovery, automatically register interface compatible addresses.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String registerMode&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册协议配置了service-discovery-registry 走这个逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//前面这个逻辑是直接接给result结果中添加应用级注册，如果是all配置则增加接口级注册信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>SERVICE_REGISTRY_PROTOCOL&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registerMode &lt;span style="color:#719e07">=&lt;/span> registryURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_MODE_KEY&lt;span style="color:#719e07">,&lt;/span> ConfigurationUtils&lt;span style="color:#719e07">.&lt;/span>getCachedDynamicProperty&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">,&lt;/span> DUBBO_REGISTER_MODE_DEFAULT_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_REGISTER_MODE_INSTANCE&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isValidRegisterMode&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registerMode &lt;span style="color:#719e07">=&lt;/span> DEFAULT_REGISTER_MODE_INSTANCE&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里配置的就是应用级配置 则先添加应用级地址，再根据条件判断是否添加接口级注册中心地址
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> result&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>DEFAULT_REGISTER_MODE_ALL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registryNotExists&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">,&lt;/span> registryList&lt;span style="color:#719e07">,&lt;/span> REGISTRY_PROTOCOL&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL interfaceCompatibleRegistryURL &lt;span style="color:#719e07">=&lt;/span> URLBuilder&lt;span style="color:#719e07">.&lt;/span>from&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>REGISTRY_PROTOCOL&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>removeParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_TYPE_KEY&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>interfaceCompatibleRegistryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//正常情况下我们的配置会走这个逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 获取服务注册的注册模式 配置为dubbo.application.register-mode 默认值为all 既注册接口数据又注册应用级信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registerMode &lt;span style="color:#719e07">=&lt;/span> registryURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_MODE_KEY&lt;span style="color:#719e07">,&lt;/span> ConfigurationUtils&lt;span style="color:#719e07">.&lt;/span>getCachedDynamicProperty&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">,&lt;/span> DUBBO_REGISTER_MODE_DEFAULT_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_REGISTER_MODE_ALL&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isValidRegisterMode&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registerMode &lt;span style="color:#719e07">=&lt;/span> DEFAULT_REGISTER_MODE_INTERFACE&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据逻辑条件判断是否添加应用级注册中心地址
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">((&lt;/span>DEFAULT_REGISTER_MODE_INSTANCE&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> DEFAULT_REGISTER_MODE_ALL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registryNotExists&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">,&lt;/span> registryList&lt;span style="color:#719e07">,&lt;/span> SERVICE_REGISTRY_PROTOCOL&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL serviceDiscoveryRegistryURL &lt;span style="color:#719e07">=&lt;/span> URLBuilder&lt;span style="color:#719e07">.&lt;/span>from&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>SERVICE_REGISTRY_PROTOCOL&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>removeParameter&lt;span style="color:#719e07">(&lt;/span>REGISTRY_TYPE_KEY&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryRegistryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据逻辑条件判断是否添加接口级注册中心地址
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>DEFAULT_REGISTER_MODE_INTERFACE&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> DEFAULT_REGISTER_MODE_ALL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FrameworkStatusReportService reportService &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">).&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>FrameworkStatusReportService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportService&lt;span style="color:#719e07">.&lt;/span>reportRegistrationStatus&lt;span style="color:#719e07">(&lt;/span>reportService&lt;span style="color:#719e07">.&lt;/span>createRegistrationReport&lt;span style="color:#719e07">(&lt;/span>registerMode&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个方法是根据服务注册模式来判断使用接口级注册地址还是应用级注册地址分别如下所示：
配置信息：
dubbo.application.register-mode
配置值：&lt;/p>
&lt;ul>
&lt;li>interface
&lt;ul>
&lt;li>接口级注册&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>instance
&lt;ul>
&lt;li>应用级注册&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>all
&lt;ul>
&lt;li>接口级别和应用级都注册&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>接口级注册地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>registry:&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=9008&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653703292768
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>应用级注册地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">-&lt;/span>discovery&lt;span style="color:#719e07">-&lt;/span>registry&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=10275&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653704425920
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="165-导出服务配置到本地和注册中心">16.5 导出服务配置到本地和注册中心&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> doExportUrlsFor1Protocol&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">,&lt;/span> registryURLs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>protocolConfig为：dubbo协议的配置
&amp;lt;dubbo:protocol port=&amp;quot;-1&amp;quot; name=&amp;ldquo;dubbo&amp;rdquo; /&amp;gt;&lt;/p>
&lt;p>registryURLs目前有两个 应用级注册地址和接口级注册地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>registry:&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=9008&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653703292768
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">-&lt;/span>discovery&lt;span style="color:#719e07">-&lt;/span>registry&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=10275&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653704425920
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1651-导出服务配置的doexporturlsfor1protocol方法">16.5.1 导出服务配置的doExportUrlsFor1Protocol方法&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doExportUrlsFor1Protocol&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ProtocolConfig protocolConfig&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> registryURLs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//生成协议配置具体可见下图中的元数据配置中的attachments
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> map &lt;span style="color:#719e07">=&lt;/span> buildAttributes&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// remove null key and null value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//移除空值 简化配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> map&lt;span style="color:#719e07">.&lt;/span>keySet&lt;span style="color:#719e07">().&lt;/span>removeIf&lt;span style="color:#719e07">(&lt;/span>key &lt;span style="color:#719e07">-&amp;gt;&lt;/span> key &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> map&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init serviceMetadata attachments
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//协议配置放到元数据对象中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>getAttachments&lt;span style="color:#719e07">().&lt;/span>putAll&lt;span style="color:#719e07">(&lt;/span>map&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//协议配置 + 默认协议配置转URL类型的配置存储
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> URL url &lt;span style="color:#719e07">=&lt;/span> buildUrl&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">,&lt;/span> map&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//导出url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> exportUrl&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> registryURLs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/16-deploy2.png" alt="在这里插入图片描述">&lt;/p>
&lt;h3 id="1652-导出服务配置模板方法">16.5.2 导出服务配置模板方法&lt;/h3>
&lt;p>继续看导出服务的模板方法，分为本地导出和注册中心导出
//参数url为协议配置url可以参考：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>dubbo:&lt;span style="color:#586e75">//192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;amp;application=dubbo-demo-api-provider&amp;amp;background=false&amp;amp;bind.ip=192.168.1.9&amp;amp;bind.port=20880&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=link.elastic.dubbo.entity.DemoService&amp;amp;methods=sayHello,sayHelloAsync&amp;amp;pid=10953&amp;amp;release=3.0.8&amp;amp;side=provider&amp;amp;timestamp=1653705630518
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportUrl&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> registryURLs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String scope &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>SCOPE_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// don&amp;#39;t export when none is configured
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>SCOPE_NONE&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>scope&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// export to local if the config is not remote (export to remote only when config is remote)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//未明确指定远程导出 则开启本地导出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>SCOPE_REMOTE&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>scope&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportLocal&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//未明确指定本地导出 则开启远程导出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// export to remote if the config is not local (export to local only when config is local)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>SCOPE_LOCAL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>scope&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> exportRemote&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> registryURLs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isGeneric&lt;span style="color:#719e07">(&lt;/span>generic&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>getScopeModel&lt;span style="color:#719e07">().&lt;/span>isInternal&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetadataUtils&lt;span style="color:#719e07">.&lt;/span>publishServiceDefinition&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> providerModel&lt;span style="color:#719e07">.&lt;/span>getServiceModel&lt;span style="color:#719e07">(),&lt;/span> getApplicationModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>urls&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="166-导出服务到本地">16.6 导出服务到本地&lt;/h2>
&lt;p>本地调用使用了 injvm 协议，是一个伪协议，它不开启端口，不发起远程调用，只在 JVM 内直接关联，但执行 Dubbo 的 Filter 链。&lt;/p>
&lt;p>直接通过代码来看吧&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportLocal&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//协议转为injvm 代表本地导出 host为127.0.0.1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> URL local &lt;span style="color:#719e07">=&lt;/span> URLBuilder&lt;span style="color:#719e07">.&lt;/span>from&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>LOCAL_PROTOCOL&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setHost&lt;span style="color:#719e07">(&lt;/span>LOCALHOST_VALUE&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> local &lt;span style="color:#719e07">=&lt;/span> local&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>getScopeModel&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setServiceModel&lt;span style="color:#719e07">(&lt;/span>providerModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doExportUrl&lt;span style="color:#719e07">(&lt;/span>local&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Export dubbo service &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> interfaceClass&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to local registry url : &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> local&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1661-doexporturl方法">16.6.1 doExportUrl方法&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doExportUrl&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> withMetaData&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里是由adaptor扩展类型处理过的 我们直接看默认的类型javassist 对应JavassistProxyFactory代理工厂 获取调用对象 （
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker &lt;span style="color:#719e07">=&lt;/span> proxyFactory&lt;span style="color:#719e07">.&lt;/span>getInvoker&lt;span style="color:#719e07">(&lt;/span>ref&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">)&lt;/span> interfaceClass&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>withMetaData&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DelegateProviderMetaDataInvoker&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Exporter&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> exporter &lt;span style="color:#719e07">=&lt;/span> protocolSPI&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exporters&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>exporter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1662-javassistproxyfactory类型的getinvoker方法">16.6.2 JavassistProxyFactory类型的getInvoker方法&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>T proxy&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// TODO Wrapper cannot handle this scenario correctly: the classname contains &amp;#39;$&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 创建实际服务提供者的代理类型，代理类型后缀为DubboWrap在这里类型为 link.elastic.dubbo.entity.DemoServiceImplDubboWrap0
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Wrapper wrapper &lt;span style="color:#719e07">=&lt;/span> Wrapper&lt;span style="color:#719e07">.&lt;/span>getWrapper&lt;span style="color:#719e07">(&lt;/span>proxy&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">().&lt;/span>getName&lt;span style="color:#719e07">().&lt;/span>indexOf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#39;$&amp;#39;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span> &lt;span style="color:#719e07">?&lt;/span> proxy&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">:&lt;/span> type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个匿名内部类对象 继承自AbstractProxyInvoker的Invoker对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AbstractProxyInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>proxy&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> Object &lt;span style="color:#268bd2">doInvoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>T proxy&lt;span style="color:#719e07">,&lt;/span> String methodName&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]&lt;/span> parameterTypes&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object&lt;span style="color:#719e07">[]&lt;/span> arguments&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Throwable &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> wrapper&lt;span style="color:#719e07">.&lt;/span>invokeMethod&lt;span style="color:#719e07">(&lt;/span>proxy&lt;span style="color:#719e07">,&lt;/span> methodName&lt;span style="color:#719e07">,&lt;/span> parameterTypes&lt;span style="color:#719e07">,&lt;/span> arguments&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable fromJavassist&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// try fall back to JDK proxy factory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1663-使用协议导出调用对象-export">16.6.3 使用协议导出调用对象 export&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> Exporter&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> exporter &lt;span style="color:#719e07">=&lt;/span> protocolSPI&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个使用了Adaptor扩展和Wrapper机制Debug起来不太方便这里贴一下调用堆栈&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/16-deploy3.png" alt="在这里插入图片描述">&lt;/p>
&lt;h3 id="16631-协议序列化机制protocolserializationwrapper">16.6.3.1 协议序列化机制ProtocolSerializationWrapper&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里主要逻辑是将服务提供者url添加到服务存储仓库中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> getFrameworkModel&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getScopeModel&lt;span style="color:#719e07">()).&lt;/span>getServiceRepository&lt;span style="color:#719e07">().&lt;/span>registerProviderUrl&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="16632-协议过滤器wrapper-protocolfilterwrapper">16.6.3.2 协议过滤器Wrapper ProtocolFilterWrapper&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心的协议导出直接执行
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//过滤器调用链FilterChainBuilder的扩展对象查询
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> FilterChainBuilder builder &lt;span style="color:#719e07">=&lt;/span> getFilterChainBuilder&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里分为2步 生成过滤器调用链 然后使用链表中的节点调用 这里值查询provider类型的过滤器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>builder&lt;span style="color:#719e07">.&lt;/span>buildInvokerChain&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> SERVICE_FILTER_KEY&lt;span style="color:#719e07">,&lt;/span> CommonConstants&lt;span style="color:#719e07">.&lt;/span>PROVIDER&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>过滤器调用链的生成 对用DefaultFilterChainBuilder类型的buildInvokerChain方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">buildInvokerChain&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> originalInvoker&lt;span style="color:#719e07">,&lt;/span> String key&lt;span style="color:#719e07">,&lt;/span> String group&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//originalInvoker代表真正的服务调用器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> last &lt;span style="color:#719e07">=&lt;/span> originalInvoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL url &lt;span style="color:#719e07">=&lt;/span> originalInvoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ModuleModel&lt;span style="color:#719e07">&amp;gt;&lt;/span> moduleModels &lt;span style="color:#719e07">=&lt;/span> getModuleModelsFromUrl&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Filter&lt;span style="color:#719e07">&amp;gt;&lt;/span> filters&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>moduleModels &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> moduleModels&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//类型Filter key为service.filter 分组为provider 所有提供者过滤器拉取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> filters &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Filter&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> moduleModels&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)).&lt;/span>getActivateExtension&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> key&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>moduleModels &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> moduleModels&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filters &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ExtensionDirector&lt;span style="color:#719e07">&amp;gt;&lt;/span> directors &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ModuleModel moduleModel &lt;span style="color:#719e07">:&lt;/span> moduleModels&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Filter&lt;span style="color:#719e07">&amp;gt;&lt;/span> tempFilters &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Filter&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> moduleModel&lt;span style="color:#719e07">).&lt;/span>getActivateExtension&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> key&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filters&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>tempFilters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> directors&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">.&lt;/span>getExtensionDirector&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filters &lt;span style="color:#719e07">=&lt;/span> sortingAndDeduplication&lt;span style="color:#719e07">(&lt;/span>filters&lt;span style="color:#719e07">,&lt;/span> directors&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filters &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Filter&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">).&lt;/span>getActivateExtension&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> key&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//倒序拼接，将过滤器的调用对象添加到链表中 最后倒序遍历之后 last节点指向了调用链路链表头节点的对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>filters&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> filters&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;gt;=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">--)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> Filter filter &lt;span style="color:#719e07">=&lt;/span> filters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>i&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> next &lt;span style="color:#719e07">=&lt;/span> last&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//每个invoker对象中都有originalInvoker对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> last &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CopyOfFilterChainNode&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>originalInvoker&lt;span style="color:#719e07">,&lt;/span> next&lt;span style="color:#719e07">,&lt;/span> filter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CallbackRegistrationInvoker&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>last&lt;span style="color:#719e07">,&lt;/span> filters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> last&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/16-deploy4.png" alt="在这里插入图片描述">&lt;/p>
&lt;h3 id="16633-协议监听器wrapper-protocollistenerwrapper">16.6.3.3 协议监听器Wrapper ProtocolListenerWrapper&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心地址则直接导出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 先导出对象 再创建过滤器包装对象 执行监听器逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ListenerExporterWrapper&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Collections&lt;span style="color:#719e07">.&lt;/span>unmodifiableList&lt;span style="color:#719e07">(&lt;/span>ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ExporterListener&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getScopeModel&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getActivateExtension&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">(),&lt;/span> EXPORTER_LISTENER_KEY&lt;span style="color:#719e07">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="16634-qos的协议wrapper-qosprotocolwrapper">16.6.3.4 QOS的协议Wrapper QosProtocolWrapper&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心导出的时候开启QOS 默认端口22222
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startQosServer&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="16635-injvmprotocol-的导出方法">16.6.3.5 InjvmProtocol 的导出方法&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InjvmExporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getServiceKey&lt;span style="color:#719e07">(),&lt;/span> exporterMap&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="167-导出服务到注册中心">16.7 导出服务到注册中心&lt;/h2>
&lt;p>16.5.2 导出服务配置模板方法 中我们看到了服务导出会导出到本地和远程，接下来就看下导出到远程的方法exportRemote
参数url:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>dubbo:&lt;span style="color:#586e75">//192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;amp;application=dubbo-demo-api-provider&amp;amp;background=false&amp;amp;bind.ip=192.168.1.9&amp;amp;bind.port=20880&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=link.elastic.dubbo.entity.DemoService&amp;amp;methods=sayHello,sayHelloAsync&amp;amp;pid=12865&amp;amp;release=3.0.8&amp;amp;side=provider&amp;amp;timestamp=1653708351378
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>参数registryURLs目前有两个 应用级注册地址和接口级注册地址：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>registry:&lt;span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=9008&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653703292768
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">-&lt;/span>discovery&lt;span style="color:#719e07">-&lt;/span>registry&lt;span style="color:#719e07">:&lt;/span>&lt;span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=10275&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653704425920
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> URL &lt;span style="color:#268bd2">exportRemote&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> registryURLs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>registryURLs&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历所有注册地址与注册模式 逐个注册
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>URL registryURL &lt;span style="color:#719e07">:&lt;/span> registryURLs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为协议URL 添加应用级注册service-discovery-registry参数service-name-mapping为true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>SERVICE_REGISTRY_PROTOCOL&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>addParameterIfAbsent&lt;span style="color:#719e07">(&lt;/span>SERVICE_NAME_MAPPING_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//if protocol is only injvm ,not register
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>LOCAL_PROTOCOL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为协议url 添加动态配置dynamic
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> url &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>addParameterIfAbsent&lt;span style="color:#719e07">(&lt;/span>DYNAMIC_KEY&lt;span style="color:#719e07">,&lt;/span> registryURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>DYNAMIC_KEY&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//监控配置暂时为null
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> URL monitorUrl &lt;span style="color:#719e07">=&lt;/span> ConfigValidationUtils&lt;span style="color:#719e07">.&lt;/span>loadMonitor&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> registryURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>monitorUrl &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>putAttribute&lt;span style="color:#719e07">(&lt;/span>MONITOR_KEY&lt;span style="color:#719e07">,&lt;/span> monitorUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// For providers, this is used to enable custom proxy to generate invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String proxy &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROXY_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>proxy&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryURL &lt;span style="color:#719e07">=&lt;/span> registryURL&lt;span style="color:#719e07">.&lt;/span>addParameter&lt;span style="color:#719e07">(&lt;/span>PROXY_KEY&lt;span style="color:#719e07">,&lt;/span> proxy&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//开始注册服务了 打印个认知 提示下
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Register dubbo service &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> interfaceClass&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; url &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to registry &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> registryURL&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Export dubbo service &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> interfaceClass&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to url &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doExportUrl&lt;span style="color:#719e07">(&lt;/span>registryURL&lt;span style="color:#719e07">.&lt;/span>putAttribute&lt;span style="color:#719e07">(&lt;/span>EXPORT_KEY&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Export dubbo service &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> interfaceClass&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to url &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doExportUrl&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> url&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1671---doexporturl方法">16.7.1 doExportUrl方法&lt;/h3>
&lt;p>与 16.6.1 doExportUrl方法 导出本地协议是一样的逻辑 ，我们来看看点不同地方&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doExportUrl&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> withMetaData&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker &lt;span style="color:#719e07">=&lt;/span> proxyFactory&lt;span style="color:#719e07">.&lt;/span>getInvoker&lt;span style="color:#719e07">(&lt;/span>ref&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">)&lt;/span> interfaceClass&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>withMetaData&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//远程服务导出逐个值为true 元数据invoker包装一下
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invoker &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DelegateProviderMetaDataInvoker&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Exporter&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> exporter &lt;span style="color:#719e07">=&lt;/span> protocolSPI&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exporters&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>exporter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>与本地导出ProtocolFilterWrapper的不同之处
服务发现service-discovery-registry的导出UrlUtils.isRegistry(invoker.getUrl() 判断结果为true会走这个逻辑&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心的协议导出直接执行
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 服务发现service-discovery-registry的导出会走这个逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//过滤器调用链FilterChainBuilder的扩展对象查询
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> FilterChainBuilder builder &lt;span style="color:#719e07">=&lt;/span> getFilterChainBuilder&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里分为2步 生成过滤器调用链 然后使用链表中的节点调用 这里值查询provider类型的过滤器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>builder&lt;span style="color:#719e07">.&lt;/span>buildInvokerChain&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> SERVICE_FILTER_KEY&lt;span style="color:#719e07">,&lt;/span> CommonConstants&lt;span style="color:#719e07">.&lt;/span>PROVIDER&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>与 协议监听器Wrapper ProtocolListenerWrapper 的不同之处&lt;/p>
&lt;p>服务发现service-discovery-registry的导出UrlUtils.isRegistry(invoker.getUrl() 判断结果为true会走这个逻辑&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心地址则直接导出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 服务发现service-discovery-registry的导出会走这个逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 先导出对象 再创建过滤器包装对象 执行监听器逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ListenerExporterWrapper&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Collections&lt;span style="color:#719e07">.&lt;/span>unmodifiableList&lt;span style="color:#719e07">(&lt;/span>ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ExporterListener&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getScopeModel&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getActivateExtension&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">(),&lt;/span> EXPORTER_LISTENER_KEY&lt;span style="color:#719e07">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>与 16.6.3.4 QOS的协议Wrapper QosProtocolWrapper 不同之处&lt;/p>
&lt;p>服务发现service-discovery-registry的导出UrlUtils.isRegistry(invoker.getUrl() 判断结果为true会走这个逻辑&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心导出的时候开启QOS 默认端口22222
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startQosServer&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启动QOS服务startQosServer&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startQosServer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>hasStarted&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> qosEnable &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>QOS_ENABLE&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>qosEnable&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;qos won&amp;#39;t be started because it is disabled. &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;Please check dubbo.application.qos.enable is configured either in system property, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo.properties or XML/spring-boot configuration.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String host &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>QOS_HOST&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> port &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>QOS_PORT&lt;span style="color:#719e07">,&lt;/span> QosConstants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_PORT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> acceptForeignIp &lt;span style="color:#719e07">=&lt;/span> Boolean&lt;span style="color:#719e07">.&lt;/span>parseBoolean&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>ACCEPT_FOREIGN_IP&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Server server &lt;span style="color:#719e07">=&lt;/span> frameworkModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>Server&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> server&lt;span style="color:#719e07">.&lt;/span>setHost&lt;span style="color:#719e07">(&lt;/span>host&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> server&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>port&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> server&lt;span style="color:#719e07">.&lt;/span>setAcceptForeignIp&lt;span style="color:#719e07">(&lt;/span>acceptForeignIp&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> server&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable throwable&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Fail to start qos server: &amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> throwable&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>QOS的Server的启动方法start&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">start&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Throwable &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>started&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//1个主线程
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> boss &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> NioEventLoopGroup&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DefaultThreadFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;qos-boss&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//0个从线程
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> worker &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> NioEventLoopGroup&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DefaultThreadFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;qos-worker&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务端启动器，和参数设置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServerBootstrap serverBootstrap &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServerBootstrap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serverBootstrap&lt;span style="color:#719e07">.&lt;/span>group&lt;span style="color:#719e07">(&lt;/span>boss&lt;span style="color:#719e07">,&lt;/span> worker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serverBootstrap&lt;span style="color:#719e07">.&lt;/span>channel&lt;span style="color:#719e07">(&lt;/span>NioServerSocketChannel&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serverBootstrap&lt;span style="color:#719e07">.&lt;/span>option&lt;span style="color:#719e07">(&lt;/span>ChannelOption&lt;span style="color:#719e07">.&lt;/span>SO_REUSEADDR&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serverBootstrap&lt;span style="color:#719e07">.&lt;/span>childOption&lt;span style="color:#719e07">(&lt;/span>ChannelOption&lt;span style="color:#719e07">.&lt;/span>TCP_NODELAY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serverBootstrap&lt;span style="color:#719e07">.&lt;/span>childHandler&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ChannelInitializer&lt;span style="color:#719e07">&amp;lt;&lt;/span>Channel&lt;span style="color:#719e07">&amp;gt;()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initChannel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Channel ch&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ch&lt;span style="color:#719e07">.&lt;/span>pipeline&lt;span style="color:#719e07">().&lt;/span>addLast&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> QosProcessHandler&lt;span style="color:#719e07">(&lt;/span>frameworkModel&lt;span style="color:#719e07">,&lt;/span> welcome&lt;span style="color:#719e07">,&lt;/span> acceptForeignIp&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isBlank&lt;span style="color:#719e07">(&lt;/span>host&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serverBootstrap&lt;span style="color:#719e07">.&lt;/span>bind&lt;span style="color:#719e07">(&lt;/span>port&lt;span style="color:#719e07">).&lt;/span>sync&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serverBootstrap&lt;span style="color:#719e07">.&lt;/span>bind&lt;span style="color:#719e07">(&lt;/span>host&lt;span style="color:#719e07">,&lt;/span> port&lt;span style="color:#719e07">).&lt;/span>sync&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;qos-server bind localhost:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> port&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable throwable&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;qos-server can not bind localhost:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> port&lt;span style="color:#719e07">,&lt;/span> throwable&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> throwable&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>QOS处理器为QosProcessHandler关于QosProcessHandler的细节这里先不说&lt;/p>
&lt;p>最后一个不同的地方调用链路走的这个 RegistryProtocol&lt;/p>
&lt;h3 id="1672-通过注册协议导出服务与注册服务的流程">16.7.2 通过注册协议导出服务与注册服务的流程&lt;/h3>
&lt;p>RegistryProtocol的导出方法：
这个方法非常重要也是服务注册的核心代码，先概括下包含了哪些步骤&lt;/p>
&lt;ul>
&lt;li>覆盖配置&lt;/li>
&lt;li>导出协议端口开启TCP服务&lt;/li>
&lt;li>注册到注册中心&lt;/li>
&lt;li>通知服务启动了&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> originInvoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//service-discovery-registry://8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;amp;dubbo=2.0.2&amp;amp;pid=14256&amp;amp;registry=zookeeper&amp;amp;release=3.0.8&amp;amp;timestamp=1653710477057
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> URL registryUrl &lt;span style="color:#719e07">=&lt;/span> getRegistryUrl&lt;span style="color:#719e07">(&lt;/span>originInvoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// url to export locally
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;amp;application=dubbo-demo-api-provider&amp;amp;background=false&amp;amp;bind.ip=192.168.1.9&amp;amp;bind.port=20880&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=link.elastic.dubbo.entity.DemoService&amp;amp;methods=sayHello,sayHelloAsync&amp;amp;pid=14256&amp;amp;release=3.0.8&amp;amp;service-name-mapping=true&amp;amp;side=provider&amp;amp;timestamp=1653710479073
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> URL providerUrl &lt;span style="color:#719e07">=&lt;/span> getProviderUrl&lt;span style="color:#719e07">(&lt;/span>originInvoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Subscribe the override data
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// the same service. Because the subscribed is cached key with the name of the service, it causes the
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// subscription information to cover.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//provider://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;amp;application=dubbo-demo-api-provider&amp;amp;background=false&amp;amp;bind.ip=192.168.1.9&amp;amp;bind.port=20880&amp;amp;category=configurators&amp;amp;check=false&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=link.elastic.dubbo.entity.DemoService&amp;amp;methods=sayHello,sayHelloAsync&amp;amp;pid=14256&amp;amp;release=3.0.8&amp;amp;service-name-mapping=true&amp;amp;side=provider&amp;amp;timestamp=1653710479073
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> URL overrideSubscribeUrl &lt;span style="color:#719e07">=&lt;/span> getSubscribedOverrideUrl&lt;span style="color:#719e07">(&lt;/span>providerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//override配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> OverrideListener overrideSubscribeListener &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> OverrideListener&lt;span style="color:#719e07">(&lt;/span>overrideSubscribeUrl&lt;span style="color:#719e07">,&lt;/span> originInvoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">,&lt;/span> NotifyListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> overrideListeners &lt;span style="color:#719e07">=&lt;/span> getProviderConfigurationListener&lt;span style="color:#719e07">(&lt;/span>providerUrl&lt;span style="color:#719e07">).&lt;/span>getOverrideListeners&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> overrideListeners&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">,&lt;/span> overrideSubscribeListener&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> providerUrl &lt;span style="color:#719e07">=&lt;/span> overrideUrlWithConfig&lt;span style="color:#719e07">(&lt;/span>providerUrl&lt;span style="color:#719e07">,&lt;/span> overrideSubscribeListener&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//export invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> ExporterChangeableWrapper&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> exporter &lt;span style="color:#719e07">=&lt;/span> doLocalExport&lt;span style="color:#719e07">(&lt;/span>originInvoker&lt;span style="color:#719e07">,&lt;/span> providerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// url to registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//通过URL获取 注册中心Registry操作对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Registry registry &lt;span style="color:#719e07">=&lt;/span> getRegistry&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//需要向注册中心注册地址转换
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;amp;application=dubbo-demo-api-provider&amp;amp;background=false&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=link.elastic.dubbo.entity.DemoService&amp;amp;methods=sayHello,sayHelloAsync&amp;amp;pid=14656&amp;amp;release=3.0.8&amp;amp;service-name-mapping=true&amp;amp;side=provider&amp;amp;timestamp=1653711086189
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> URL registeredProviderUrl &lt;span style="color:#719e07">=&lt;/span> getUrlToRegistry&lt;span style="color:#719e07">(&lt;/span>providerUrl&lt;span style="color:#719e07">,&lt;/span> registryUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// decide if we need to delay publish (provider itself and registry should both need to register)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> register &lt;span style="color:#719e07">=&lt;/span> providerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registryUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REGISTER_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否向注册中心注册
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>register&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> register&lt;span style="color:#719e07">(&lt;/span>registry&lt;span style="color:#719e07">,&lt;/span> registeredProviderUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register stated url on provider model
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registerStatedUrl&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">,&lt;/span> registeredProviderUrl&lt;span style="color:#719e07">,&lt;/span> register&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exporter&lt;span style="color:#719e07">.&lt;/span>setRegisterUrl&lt;span style="color:#719e07">(&lt;/span>registeredProviderUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exporter&lt;span style="color:#719e07">.&lt;/span>setSubscribeUrl&lt;span style="color:#719e07">(&lt;/span>overrideSubscribeUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>registry&lt;span style="color:#719e07">.&lt;/span>isServiceDiscovery&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Deprecated! Subscribe to override rules in 2.6.x or before.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registry&lt;span style="color:#719e07">.&lt;/span>subscribe&lt;span style="color:#719e07">(&lt;/span>overrideSubscribeUrl&lt;span style="color:#719e07">,&lt;/span> overrideSubscribeListener&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//内置监听器通知 这个不是通知消费者的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> notifyExport&lt;span style="color:#719e07">(&lt;/span>exporter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//Ensure that a new exporter instance is returned every time export
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DestroyableExporter&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>exporter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="168-dolocalexport本地导出协议开启端口">16.8 doLocalExport本地导出协议开启端口&lt;/h2>
&lt;p>前面已经看过了本地协议JVM协议的服务导出和注册中心配置的导出，这里可以直接看一些关键代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> ExporterChangeableWrapper&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">doLocalExport&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> originInvoker&lt;span style="color:#719e07">,&lt;/span> URL providerUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String key &lt;span style="color:#719e07">=&lt;/span> getCacheKey&lt;span style="color:#719e07">(&lt;/span>originInvoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ExporterChangeableWrapper&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> bounds&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> s &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invokerDelegate &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InvokerDelegate&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>originInvoker&lt;span style="color:#719e07">,&lt;/span> providerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//代码中用的这个protoco对象是dubbo自动生成的适配器对象protocol$Adaptive 适配器对象会根据当前协议的参数来查询具体的协议扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ExporterChangeableWrapper&lt;span style="color:#719e07">&amp;lt;&amp;gt;((&lt;/span>Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>invokerDelegate&lt;span style="color:#719e07">),&lt;/span> originInvoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面这个protocol$Adaptive 协议的export导出方法与之前的一样也会经历下面几个过程，具体细节可以参考JVM协议的导出：&lt;/p>
&lt;ul>
&lt;li>ProtocolSerializationWrapper&lt;/li>
&lt;li>ProtocolFilterWrapper&lt;/li>
&lt;li>ProtocolListenerWrapper&lt;/li>
&lt;li>QosProtocolWrapper&lt;/li>
&lt;li>唯一不同的是我们这里对应的协议扩展类型为DubboProtocol、
接下来来看下DubboProtocol的导出服务export方法实现：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> checkDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务提供者的url参考例子dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;amp;application=dubbo-demo-api-provider&amp;amp;background=false&amp;amp;bind.ip=192.168.1.9&amp;amp;bind.port=20880&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=link.elastic.dubbo.entity.DemoService&amp;amp;methods=sayHello,sayHelloAsync&amp;amp;pid=6043&amp;amp;release=3.0.8&amp;amp;service-name-mapping=true&amp;amp;side=provider&amp;amp;timestamp=1654224285437
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> URL url &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// export service.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//生成服务的key参考：link.elastic.dubbo.entity.DemoService:20880
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> serviceKey&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建导出服务用的导出器DubboExporter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> DubboExporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> exporter &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DubboExporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> key&lt;span style="color:#719e07">,&lt;/span> exporterMap&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//export a stub service for dispatching event
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//stub配置校验
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Boolean isStubSupportEvent &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>STUB_EVENT_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_STUB_EVENT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Boolean isCallbackservice &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>IS_CALLBACK_SERVICE&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isStubSupportEvent &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>isCallbackservice&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String stubServiceMethods &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>STUB_EVENT_METHODS_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>stubServiceMethods &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> stubServiceMethods&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isWarnEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;consumer [&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>INTERFACE_KEY&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;], has set stubproxy support event ,but no stub methods founded.&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建服务开启服务端口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> openServer&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> optimizeSerialization&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> exporter&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="开启服务端口">开启服务端口&lt;/h3>
&lt;p>这里就到了RPC协议的TCP通信模块了，对应DubboProtocol 的 openServer(url);方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">openServer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> checkDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// find server. 地址作为key这里是192.168.1.9:20880
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// client can export a service which only for server to invoke
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//默认提供者开启服务，消费者是不能开启服务的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> isServer &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>IS_SERVER_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isServer&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//协议服务器 下面一个双重校验锁检查，如果为空则创建服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ProtocolServer server &lt;span style="color:#719e07">=&lt;/span> serverMap&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>server &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> server &lt;span style="color:#719e07">=&lt;/span> serverMap&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>server &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serverMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> createServer&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>&lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> server&lt;span style="color:#719e07">.&lt;/span>reset&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// server supports reset, use together with override
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> server&lt;span style="color:#719e07">.&lt;/span>reset&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>为当前地址创建协议服务对应方法如下：
DubboProtocol的createServer方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> ProtocolServer &lt;span style="color:#268bd2">createServer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//下面将url增加了心跳参数最终如下dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;amp;application=dubbo-demo-api-provider&amp;amp;background=false&amp;amp;bind.ip=192.168.1.9&amp;amp;bind.port=20880&amp;amp;channel.readonly.sent=true&amp;amp;codec=dubbo&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;heartbeat=60000&amp;amp;interface=link.elastic.dubbo.entity.DemoService&amp;amp;methods=sayHello,sayHelloAsync&amp;amp;pid=6700&amp;amp;release=3.0.8&amp;amp;service-name-mapping=true&amp;amp;side=provider&amp;amp;timestamp=1654225251112
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> url &lt;span style="color:#719e07">=&lt;/span> URLBuilder&lt;span style="color:#719e07">.&lt;/span>from&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// send readonly event when server closes, it&amp;#39;s enabled by default
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>addParameterIfAbsent&lt;span style="color:#719e07">(&lt;/span>CHANNEL_READONLYEVENT_SENT_KEY&lt;span style="color:#719e07">,&lt;/span> Boolean&lt;span style="color:#719e07">.&lt;/span>TRUE&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// enable heartbeat by default
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>addParameterIfAbsent&lt;span style="color:#719e07">(&lt;/span>HEARTBEAT_KEY&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">.&lt;/span>valueOf&lt;span style="color:#719e07">(&lt;/span>DEFAULT_HEARTBEAT&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>addParameter&lt;span style="color:#719e07">(&lt;/span>CODEC_KEY&lt;span style="color:#719e07">,&lt;/span> DubboCodec&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里服务端使用的网络库这里是默认值netty
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String str &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>SERVER_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_REMOTING_SERVER&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>str&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getOrDefaultFrameworkModel&lt;span style="color:#719e07">().&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Transporter&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>hasExtension&lt;span style="color:#719e07">(&lt;/span>str&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Unsupported server type: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> str &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, url: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//dubbo交换器层对象创建
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExchangeServer server&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个方法会绑定端口，关于交换器与传输网络层到后面统一说
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这里通过绑定url和请求处理器来创建交换器对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> server &lt;span style="color:#719e07">=&lt;/span> Exchangers&lt;span style="color:#719e07">.&lt;/span>bind&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> requestHandler&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RemotingException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Fail to start server(url: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> str &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>CLIENT_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>str&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> supportedTypes &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getOrDefaultFrameworkModel&lt;span style="color:#719e07">().&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Transporter&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSupportedExtensions&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>supportedTypes&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>str&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Unsupported client type: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> str&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboProtocolServer protocolServer &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DubboProtocolServer&lt;span style="color:#719e07">(&lt;/span>server&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//关闭等待时长默认为10秒
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadServerProperties&lt;span style="color:#719e07">(&lt;/span>protocolServer&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocolServer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="169-向注册中心注册服务register">16.9 向注册中心注册服务register&lt;/h2>
&lt;p>这个细节在下个博客中说涉及到Dubbo3的双注册&lt;/p>
&lt;p>原文地址：&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/16-mo-kuai-fa-bu-qi-fa-bu-fu-wu-quan-guo-cheng/">16-模块发布器发布服务全过程&lt;/a>&lt;/p></description></item><item><title>Blog: 15-Dubbo的三大中心之元数据中心源码解析</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="15-dubbo的三大中心之元数据中心源码解析">15-Dubbo的三大中心之元数据中心源码解析&lt;/h1>
&lt;h2 id="151-简介">15.1 简介&lt;/h2>
&lt;p>关于元数据中心的概念对于大部分用户来说是比较陌生的,配置中心的话我们还好理解,对于元数据中心是什么,我们来看下我从官网拷贝过来的一段文字:&lt;/p>
&lt;p>元数据中心在2.7.x版本开始支持，随着应用级别的服务注册和服务发现在Dubbo中落地，&lt;strong>元数据中心也变的越来越重要&lt;/strong>。在以下几种情况下会需要部署元数据中心：&lt;/p>
&lt;ul>
&lt;li>对于一个原先采用老版本Dubbo搭建的应用服务，在迁移到Dubbo 3时，Dubbo 3 会需要一个&lt;strong>元数据中心来维护RPC服务与应用的映射关系（即接口与应用的映射关系）&lt;/strong>，因为如果采用了&lt;strong>应用级别的服务发现和服务注册&lt;/strong>，在注册中心中将&lt;strong>采用“应用 —— 实例列表”结构&lt;/strong>的数据组织形式，&lt;strong>不再是以往的“接口 —— 实例列表”结构的数据组织形式&lt;/strong>，而以往用接口级别的服务注册和服务发现的应用服务在&lt;strong>迁移到应用级别&lt;/strong>时，&lt;strong>得不到接口与应用之间的对应关系&lt;/strong>，从而无法从注册中心得到实例列表信息，所以&lt;strong>Dubbo为了兼容这种场景，在Provider端启动时，会往元数据中心存储接口与应用的映射关系&lt;/strong>。&lt;/li>
&lt;li>为了让&lt;strong>注册中心更加聚焦与地址的发现和推送能力&lt;/strong>，&lt;strong>减轻注册中心的负担&lt;/strong>，元数据中心承载了所有的服务元数据、大量接口/方法级别配置信息等，无论是接口粒度还是应用粒度的服务发现和注册，元数据中心都起到了重要的作用。&lt;/li>
&lt;li>&lt;/li>
&lt;/ul>
&lt;p>如果有以上两种需求，都可以选择部署元数据中心，并通过Dubbo的配置来集成该元数据中心。&lt;/p>
&lt;p>&lt;strong>元数据中心并不依赖于注册中心和配置中心&lt;/strong>，用户可以自由选择是否集成和部署元数据中心，如下图所示：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/v3/concepts/centers-metadata.png" alt="/imgs/v3/concepts/centers-metadata.png">&lt;/p>
&lt;p>该图中不配备配置中心，意味着可以不需要全局管理配置的能力。该图中不配备注册中心，意味着可能采用了Dubbo mesh的方案，也可能不需要进行服务注册，仅仅接收直连模式的服务调用。
官网参考文章地址:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://dubbo.apache.org/zh/docs/v3.0/concepts/registry-configcenter-metadata/">部署架构（注册中心 配置中心 元数据中心&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://dubbo.apache.org/zh/docs/references/metadata/">元数据参考手册&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>综上所述可以用几句话概括下:&lt;/p>
&lt;ul>
&lt;li>元数据中心来维护RPC服务与应用的映射关系（即接口与应用的映射关系）来兼容接口与应用之间的对应关系&lt;/li>
&lt;li>让注册中心更加聚焦与地址的发现和推送能力&lt;/li>
&lt;/ul>
&lt;p>注册中心的启动是在DefaultApplicationDeployer中的初始化方法 initialize() 中:如下所示&lt;/p>
&lt;p>这里只看下 startMetadataCenter();方法即可&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>startLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register shutdown hook
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registerShutdownHook&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startConfigCenter&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadApplicationConfigs&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initModuleDeployers&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// @since 2.7.8
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> startMetadataCenter&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialized &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; has been initialized!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="152-深入探究元数据中心的启动过程">15.2 深入探究元数据中心的启动过程&lt;/h2>
&lt;h3 id="1521-启动元数据中心的代码全貌">15.2.1 启动元数据中心的代码全貌&lt;/h3>
&lt;p>关于元数据中心我们看下 startMetadataCenter()方法来大致了解下整个流程&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startMetadataCenter&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> useRegistryAsMetadataCenterIfNecessary&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取应用的配置信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> getApplication&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//元数据配置类型 元数据类型， local 或 remote,，如果选择远程，则需要进一步指定元数据中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String metadataType &lt;span style="color:#719e07">=&lt;/span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>getMetadataType&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// FIXME, multiple metadata config support.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//查询元数据中心的地址等配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Collection&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> metadataReportConfigs &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getMetadataConfigs&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>metadataReportConfigs&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个就是判断 如果选择远程，则需要进一步指定元数据中心 否则就抛出来异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>REMOTE_METADATA_STORAGE_TYPE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>metadataType&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;No MetadataConfig found, Metadata Center address is required when &amp;#39;metadata=remote&amp;#39; is enabled.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//MetadataReport实例的存储库对象获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MetadataReportInstance metadataReportInstance &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>MetadataReportInstance&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> validMetadataReportConfigs &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>metadataReportConfigs&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>MetadataReportConfig metadataReportConfig &lt;span style="color:#719e07">:&lt;/span> metadataReportConfigs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConfigValidationUtils&lt;span style="color:#719e07">.&lt;/span>validateMetadataConfig&lt;span style="color:#719e07">(&lt;/span>metadataReportConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> validMetadataReportConfigs&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>metadataReportConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> metadataReportInstance&lt;span style="color:#719e07">.&lt;/span>init&lt;span style="color:#719e07">(&lt;/span>validMetadataReportConfigs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//MetadataReport实例的存储库对象初始化失败则抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>metadataReportInstance&lt;span style="color:#719e07">.&lt;/span>inited&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;%s MetadataConfigs found, but none of them is valid.&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> metadataReportConfigs&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1522-元数据中心未配置则使用注册中心配置">15.2.2 元数据中心未配置则使用注册中心配置&lt;/h3>
&lt;p>前面在说配置中心的时候有说过配置中心如果未配置会使用注册中心的地址等信息作为默认配置,这里元数据做了类似的操作:如代码:
DefaultApplicationDeployer类型的 useRegistryAsMetadataCenterIfNecessary()方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">useRegistryAsMetadataCenterIfNecessary&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置缓存中查询元数据配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Collection&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> metadataConfigs &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getMetadataConfigs&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置存在则直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>metadataConfigs&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">////查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> defaultRegistries &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getDefaultRegistries&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>defaultRegistries&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//多注册中心遍历
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> defaultRegistries
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>stream&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>filter&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>isUsedRegistryAsMetadataCenter&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心配置映射为元数据中心 映射就是获取需要的配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>map&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>registryAsMetadataCenter&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将元数据中心配置存储在配置缓存中方便后续使用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>metadataReportConfig &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metadataReportConfig&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Collection&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> metadataReportConfigs &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getMetadataConfigs&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>metadataReportConfigs&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>MetadataReportConfig existedConfig &lt;span style="color:#719e07">:&lt;/span> metadataReportConfigs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>existedConfig&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> existedConfig&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">().&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>metadataReportConfig&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configManager&lt;span style="color:#719e07">.&lt;/span>addMetadataReport&lt;span style="color:#719e07">(&lt;/span>metadataReportConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Optional&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> configOptional &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getConfig&lt;span style="color:#719e07">(&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> metadataReportConfig&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>configOptional&lt;span style="color:#719e07">.&lt;/span>isPresent&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configManager&lt;span style="color:#719e07">.&lt;/span>addMetadataReport&lt;span style="color:#719e07">(&lt;/span>metadataReportConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;use registry as metadata-center: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> metadataReportConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个代码有些细节就不细说了 我们概括下顺序梳理下思路:&lt;/p>
&lt;ul>
&lt;li>配置缓存中查询元数据配置,配置存在则直接返回&lt;/li>
&lt;li>查询所有可用的默认注册中心列表
&lt;ul>
&lt;li>多注册中心遍历&lt;/li>
&lt;li>选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)&lt;/li>
&lt;li>注册中心配置RegistryConfig映射转换为元数据中心配置类型MetadataReportConfig 映射就是获取需要的配置&lt;/li>
&lt;li>将元数据中心配置存储在配置缓存中方便后续使用&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>元数据的配置可以参考官网:&lt;a href="https://dubbo.apache.org/zh/docs/v3.0/references/metadata/">元数据参考手册&lt;/a>&lt;/p>
&lt;p>这里主要看下可配置项有哪些 对应类型为MetadataReportConfig 在官网暂时未找到合适的文档,这里整理下属性列表方便后续配置说明查看:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>配置变量&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>id&lt;/td>
&lt;td>String&lt;/td>
&lt;td>配置id&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>protocol&lt;/td>
&lt;td>String&lt;/td>
&lt;td>元数据协议&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>address&lt;/td>
&lt;td>String&lt;/td>
&lt;td>元数据中心地址&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>port&lt;/td>
&lt;td>Integer&lt;/td>
&lt;td>元数据中心端口&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>username&lt;/td>
&lt;td>String&lt;/td>
&lt;td>元数据中心认证用户名&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>password&lt;/td>
&lt;td>String&lt;/td>
&lt;td>元数据中心认证密码&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>Integer&lt;/td>
&lt;td>元数据中心的请求超时（毫秒）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>group&lt;/td>
&lt;td>String&lt;/td>
&lt;td>该组将元数据保存在中。它与注册表相同&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>parameters&lt;/td>
&lt;td>Map&amp;lt;String, String&amp;gt;&lt;/td>
&lt;td>自定义参数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>retryTimes&lt;/td>
&lt;td>Integer&lt;/td>
&lt;td>重试次数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>retryPeriod&lt;/td>
&lt;td>Integer&lt;/td>
&lt;td>重试间隔&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cycleReport&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>默认情况下， 是否每天重复存储完整的元数据&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>syncReport&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>Sync or Async report.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cluster&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>需要群集支持，默认为false&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>registry&lt;/td>
&lt;td>String&lt;/td>
&lt;td>注册表配置id&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>file&lt;/td>
&lt;td>String&lt;/td>
&lt;td>元数据报告文件存储位置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>check&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>连接到元数据中心时要应用的失败策略&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="1523-元数据中心的初始化逻辑">15.2.3 元数据中心的初始化逻辑&lt;/h3>
&lt;h4 id="15231-元数据中心的初始化调用逻辑">15.2.3.1 元数据中心的初始化调用逻辑&lt;/h4>
&lt;p>主要看这一行比较重要的逻辑:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> metadataReportInstance&lt;span style="color:#719e07">.&lt;/span>init&lt;span style="color:#719e07">(&lt;/span>validMetadataReportConfigs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在了解这一行逻辑之前我们先来看下元数据相关联的类型:&lt;/p>
&lt;p>MetadataReportInstance中的初始化方法init&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>&lt;span style="color:#719e07">(&lt;/span>List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> metadataReportConfigs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//CAS判断是否有初始化过
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>init&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//元数据类型配置如果未配置则默认为local
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataType &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">().&lt;/span>getApplicationOrElseThrow&lt;span style="color:#719e07">().&lt;/span>getMetadataType&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metadataType &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataType &lt;span style="color:#719e07">=&lt;/span> DEFAULT_METADATA_STORAGE_TYPE&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取MetadataReportFactory 工厂类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MetadataReportFactory metadataReportFactory &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>MetadataReportFactory&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//多元数据中心初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>MetadataReportConfig metadataReportConfig &lt;span style="color:#719e07">:&lt;/span> metadataReportConfigs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> init&lt;span style="color:#719e07">(&lt;/span>metadataReportConfig&lt;span style="color:#719e07">,&lt;/span> metadataReportFactory&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataReportConfig config&lt;span style="color:#719e07">,&lt;/span> MetadataReportFactory metadataReportFactory&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置转url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> URL url &lt;span style="color:#719e07">=&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>toUrl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>METADATA_REPORT_KEY&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String protocol &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>METADATA_REPORT_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_DIRECTORY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> URLBuilder&lt;span style="color:#719e07">.&lt;/span>from&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>protocol&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>removeParameter&lt;span style="color:#719e07">(&lt;/span>METADATA_REPORT_KEY&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>addParameterIfAbsent&lt;span style="color:#719e07">(&lt;/span>APPLICATION_KEY&lt;span style="color:#719e07">,&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getCurrentConfig&lt;span style="color:#719e07">().&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String relatedRegistryId &lt;span style="color:#719e07">=&lt;/span> isEmpty&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getRegistry&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">?&lt;/span> DEFAULT_KEY &lt;span style="color:#719e07">:&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">:&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>getRegistry&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从元数据工厂中获取元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MetadataReport metadataReport &lt;span style="color:#719e07">=&lt;/span> metadataReportFactory&lt;span style="color:#719e07">.&lt;/span>getMetadataReport&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存元数据到内存
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metadataReport &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataReports&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>relatedRegistryId&lt;span style="color:#719e07">,&lt;/span> metadataReport&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于元数据的初始化我们主要看两个位置:&lt;/p>
&lt;ul>
&lt;li>一个是元数据工厂对象的创建与初始化MetadataReportFactory&lt;/li>
&lt;li>一个是元数据对象的创建与初始化MetadataReport&lt;/li>
&lt;/ul>
&lt;h4 id="15232-元数据工厂对象metadatareportfactory">15.2.3.2 元数据工厂对象MetadataReportFactory&lt;/h4>
&lt;p>关于元数据工厂类型MetadataReportFactory,元数据工厂 用于&lt;strong>创建与管理元数据对象&lt;/strong>, 相关类型如下:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/15-config.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>我们这里主要以为Zookeeper扩展的元数据工厂ZookeeperMetadataReportFactory类型为例子:
实现类型逻辑不复杂,这里就直接贴代码看看:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ZookeeperMetadataReportFactory&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> AbstractMetadataReportFactory &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//与Zookeeper交互的传输器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">private&lt;/span> ZookeeperTransporter zookeeperTransporter&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//应用程序模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">private&lt;/span> ApplicationModel applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ZookeeperMetadataReportFactory&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>zookeeperTransporter &lt;span style="color:#719e07">=&lt;/span> ZookeeperTransporter&lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@DisableInject&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setZookeeperTransporter&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ZookeeperTransporter zookeeperTransporter&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>zookeeperTransporter &lt;span style="color:#719e07">=&lt;/span> zookeeperTransporter&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> MetadataReport &lt;span style="color:#268bd2">createMetadataReport&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ZookeeperMetadataReport&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> zookeeperTransporter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>元数据工厂的实现比较简单&lt;/p>
&lt;ul>
&lt;li>继承抽象的元数据工厂AbstractMetadataReportFactory&lt;/li>
&lt;li>实现工厂方法createMetadataReport来创建一个元数据操作类型&lt;/li>
&lt;/ul>
&lt;p>如果我们想要实现一个元数据工厂扩展可以参考Zookeeper的这个方式&lt;/p>
&lt;h4 id="15233-元数据操作对象metadatareport的创建与初始化">15.2.3.3 元数据操作对象MetadataReport的创建与初始化&lt;/h4>
&lt;p>前面的从元数据工厂中获取元数据操作对象的逻辑处理代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//从元数据工厂中获取元数据 ,url对象可以理解为配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MetadataReport metadataReport &lt;span style="color:#719e07">=&lt;/span> metadataReportFactory&lt;span style="color:#719e07">.&lt;/span>getMetadataReport&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于元数据对象,用于元数据信息的增删改查等逻辑的操作与元数据信息的缓存&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/15-config2.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>我们这里还是以Zookeeper的实现ZookeeperMetadataReportFactory类型做为参考:&lt;/p>
&lt;p>我们先来看这个逻辑&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//从元数据工厂中获取元数据 ,url对象可以理解为配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MetadataReport metadataReport &lt;span style="color:#719e07">=&lt;/span> metadataReportFactory&lt;span style="color:#719e07">.&lt;/span>getMetadataReport&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ZookeeperMetadataReportFactory的父类型AbstractMetadataReportFactory中的getMetadataReport方法如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> MetadataReport &lt;span style="color:#268bd2">getMetadataReport&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//url值参考例子zookeeper://127.0.0.1:2181?application=dubbo-demo-api-provider&amp;amp;client=&amp;amp;port=2181&amp;amp;protocol=zookeeper
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果存在export则移除
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> url &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>setPath&lt;span style="color:#719e07">(&lt;/span>MetadataReport&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>removeParameters&lt;span style="color:#719e07">(&lt;/span>EXPORT_KEY&lt;span style="color:#719e07">,&lt;/span> REFER_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//生成元数据缓存key 元数据维度 地址+名字
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如: zookeeper://127.0.0.1:2181/org.apache.dubbo.metadata.report.MetadataReport
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>toServiceString&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存中查询 查到则直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MetadataReport metadataReport &lt;span style="color:#719e07">=&lt;/span> serviceStoreMap&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metadataReport &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> metadataReport&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Lock the metadata access process to ensure a single instance of the metadata instance
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//存在写操作 加个锁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> lock&lt;span style="color:#719e07">.&lt;/span>lock&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//双重校验锁在查一下
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> metadataReport &lt;span style="color:#719e07">=&lt;/span> serviceStoreMap&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metadataReport &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> metadataReport&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//check参数 查元数据报错是否抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> check &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>CHECK_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getPort&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//关键模版方法 调用扩展实现的具体业务(创建元数据操作对象)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> metadataReport &lt;span style="color:#719e07">=&lt;/span> createMetadataReport&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>check&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;The metadata reporter failed to initialize&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> e&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//check逻辑检查
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>check &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> metadataReport &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Can not create metadata Report &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metadataReport &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceStoreMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> metadataReport&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> metadataReport&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Release the lock
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> lock&lt;span style="color:#719e07">.&lt;/span>unlock&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面这个抽象类AbstractMetadataReportFactory中的获取元数据操作对象的模版方法getMetadataReport(URL url), 用了双重校验锁的逻辑来创建对象缓存对象,又用了模版方法设计模式,来让抽象类做通用的逻辑,让实现类型去做扩展, 虽然代码写的太长了些整体还是用了不少的设计思想.&lt;/p>
&lt;p>我们直接看这个代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>metadataReport &lt;span style="color:#719e07">=&lt;/span> createMetadataReport&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个创建元数据操作对象的代码实际上走的是实现类型的逻辑:&lt;/p>
&lt;p>来自工厂Bean ZookeeperMetadataReportFactory的工厂方法如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> MetadataReport &lt;span style="color:#268bd2">createMetadataReport&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ZookeeperMetadataReport&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> zookeeperTransporter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>创建了元数据操作对象,这里我们继续看下元数据操作对象ZookeeperMetadataReport创建做了哪些逻辑:
来自ZookeeperMetadataReport的构造器:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ZookeeperMetadataReport&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> ZookeeperTransporter zookeeperTransporter&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//url即配置 配置传递给抽象类 做一些公共的逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//url参考:zookeeper://127.0.0.1:2181/org.apache.dubbo.metadata.report.MetadataReport?application=dubbo-demo-api-provider&amp;amp;client=&amp;amp;port=2181&amp;amp;protocol=zookeeper
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>isAnyHost&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;registry address == null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String group &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(&lt;/span>DEFAULT_ROOT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>group&lt;span style="color:#719e07">.&lt;/span>startsWith&lt;span style="color:#719e07">(&lt;/span>PATH_SEPARATOR&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> group &lt;span style="color:#719e07">=&lt;/span> PATH_SEPARATOR &lt;span style="color:#719e07">+&lt;/span> group&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>root &lt;span style="color:#719e07">=&lt;/span> group&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//连接Zookeeper
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> zkClient &lt;span style="color:#719e07">=&lt;/span> zookeeperTransporter&lt;span style="color:#719e07">.&lt;/span>connect&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>核心的公共的操作逻辑封装在父类AbstractMetadataReport里面
我们来看前面super调用的构造器逻辑:
如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractMetadataReport&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL reportServerURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//设置url 如:zookeeper://127.0.0.1:2181/org.apache.dubbo.metadata.report.MetadataReport?application=dubbo-demo-api-provider&amp;amp;client=&amp;amp;port=2181&amp;amp;protocol=zookeeper
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> setUrl&lt;span style="color:#719e07">(&lt;/span>reportServerURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Start file save timer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//缓存的文件名字
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//格式为: 用户目录+/.dubbo/dubbo-metadata- + 应用程序名字application + url地址(IP+端口) + 后缀.cache 如下所示
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">///Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String defaultFilename &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>USER_HOME&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span> DUBBO_METADATA &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportServerURL&lt;span style="color:#719e07">.&lt;/span>getApplication&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;-&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> replace&lt;span style="color:#719e07">(&lt;/span>reportServerURL&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#2aa198">&amp;#34;:&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;-&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span> CACHE&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果用户配置了缓存文件名字则以用户配置为准file
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String filename &lt;span style="color:#719e07">=&lt;/span> reportServerURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>FILE_KEY&lt;span style="color:#719e07">,&lt;/span> defaultFilename&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> File file &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//文件名字不为空
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>filename&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> file &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> File&lt;span style="color:#719e07">(&lt;/span>filename&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//文件和父目录不存在则创建文件目录
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>file&lt;span style="color:#719e07">.&lt;/span>exists&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> file&lt;span style="color:#719e07">.&lt;/span>getParentFile&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>file&lt;span style="color:#719e07">.&lt;/span>getParentFile&lt;span style="color:#719e07">().&lt;/span>exists&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>file&lt;span style="color:#719e07">.&lt;/span>getParentFile&lt;span style="color:#719e07">().&lt;/span>mkdirs&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Invalid service store file &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> file &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, cause: Failed to create directory &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> file&lt;span style="color:#719e07">.&lt;/span>getParentFile&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if this file exists, firstly delete it.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//还未初始化则已存在的历史文件删除掉
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>initialized&lt;span style="color:#719e07">.&lt;/span>getAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> file&lt;span style="color:#719e07">.&lt;/span>exists&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> file&lt;span style="color:#719e07">.&lt;/span>delete&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//赋值给成员变量后续继续可以用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>file &lt;span style="color:#719e07">=&lt;/span> file&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//文件存在则直接加载文件中的内容
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadProperties&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//sync-report配置的值为同步配置还异步配置,true是同步配置,默认为false为异步配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> syncReport &lt;span style="color:#719e07">=&lt;/span> reportServerURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>SYNC_REPORT_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//重试属性与逻辑也封装了一个类型 创建对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//retry-times重试次数配置 默认为100次
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//retry-period 重试间隔配置 默认为3000
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> metadataReportRetry &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MetadataReportRetry&lt;span style="color:#719e07">(&lt;/span>reportServerURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>RETRY_TIMES_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_METADATA_REPORT_RETRY_TIMES&lt;span style="color:#719e07">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportServerURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>RETRY_PERIOD_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_METADATA_REPORT_RETRY_PERIOD&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// cycle report the data switch
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//是否定期从元数据中心同步配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//cycle-report配置默认为true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>reportServerURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>CYCLE_REPORT_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_METADATA_REPORT_CYCLE_REPORT&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//开启重试定时器 24个小时间隔从元数据中心同步一次
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> reportTimerScheduler &lt;span style="color:#719e07">=&lt;/span> Executors&lt;span style="color:#719e07">.&lt;/span>newSingleThreadScheduledExecutor&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> NamedThreadFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;DubboMetadataReportTimer&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportTimerScheduler&lt;span style="color:#719e07">.&lt;/span>scheduleAtFixedRate&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>publishAll&lt;span style="color:#719e07">,&lt;/span> calculateStartTime&lt;span style="color:#719e07">(),&lt;/span> ONE_DAY_IN_MILLISECONDS&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>reportMetadata &lt;span style="color:#719e07">=&lt;/span> reportServerURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REPORT_METADATA_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>reportDefinition &lt;span style="color:#719e07">=&lt;/span> reportServerURL&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>REPORT_DEFINITION_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="15234-内存中元数据自动同步到zookeeper和本地文件">15.2.3.4 内存中元数据自动同步到Zookeeper和本地文件&lt;/h4>
&lt;p>这里来总结下元数据操作的初始化逻辑:&lt;/p>
&lt;ul>
&lt;li>首次初始化清理历史元数据文件如:
Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache&lt;/li>
&lt;li>如果非首次进来则直接加载缓存在本地的缓存文件,赋值给properties成员变量&lt;/li>
&lt;li>初始化同步配置是否异步(默认为false), sync-report配置的值为同步配置还异步配置,true是同步配置,默认为false为异步配置&lt;/li>
&lt;li>初始化重试属性&lt;/li>
&lt;li>是否定期从元数据中心同步配置初始化 默认为true 24小时自动同步一次&lt;/li>
&lt;/ul>
&lt;p>关于元数据同步可以看AbstractMetadataReport类型的publishAll方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> reportTimerScheduler &lt;span style="color:#719e07">=&lt;/span> Executors&lt;span style="color:#719e07">.&lt;/span>newSingleThreadScheduledExecutor&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> NamedThreadFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;DubboMetadataReportTimer&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportTimerScheduler&lt;span style="color:#719e07">.&lt;/span>scheduleAtFixedRate&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>publishAll&lt;span style="color:#719e07">,&lt;/span> calculateStartTime&lt;span style="color:#719e07">(),&lt;/span> ONE_DAY_IN_MILLISECONDS&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里有个方法叫做calculateStartTime 这个代码是随机时间的between 2:00 am to 6:00 am, the time is random. 2点到6点之间启动, 低峰期启动自动同步
返回值:&lt;/p>
&lt;p>AbstractMetadataReport类型的&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">publishAll&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;start to publish all metadata.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>doHandleMetadataCollection&lt;span style="color:#719e07">(&lt;/span>allMetadataReports&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>AbstractMetadataReport类型的doHandleMetadataCollection&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">doHandleMetadataCollection&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> metadataMap&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metadataMap&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Iterator&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> iterable &lt;span style="color:#719e07">=&lt;/span> metadataMap&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">().&lt;/span>iterator&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">(&lt;/span>iterable&lt;span style="color:#719e07">.&lt;/span>hasNext&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> item &lt;span style="color:#719e07">=&lt;/span> iterable&lt;span style="color:#719e07">.&lt;/span>next&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>PROVIDER_SIDE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>item&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">().&lt;/span>getSide&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//提供端的元数据则存储提供端元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>storeProviderMetadata&lt;span style="color:#719e07">(&lt;/span>item&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#719e07">(&lt;/span>FullServiceDefinition&lt;span style="color:#719e07">)&lt;/span> item&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CONSUMER_SIDE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>item&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">().&lt;/span>getSide&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//消费端的元数据则存储提供端元数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>storeConsumerMetadata&lt;span style="color:#719e07">(&lt;/span>item&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">)&lt;/span> item&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>提供端元数据的存储:
AbstractMetadataReport类型的storeProviderMetadata&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">storeProviderMetadata&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataIdentifier providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> ServiceDefinition serviceDefinition&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>syncReport&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storeProviderMetadataTask&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportCacheExecutor&lt;span style="color:#719e07">.&lt;/span>execute&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> storeProviderMetadataTask&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> serviceDefinition&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>AbstractMetadataReport类型的storeProviderMetadataTask
具体同步代码:storeProviderMetadataTask&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">storeProviderMetadataTask&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataIdentifier providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> ServiceDefinition serviceDefinition&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;store provider metadata. Identifier : &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> providerMetadataIdentifier &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;; definition: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> allMetadataReports&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failedReports&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Gson gson &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Gson&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String data &lt;span style="color:#719e07">=&lt;/span> gson&lt;span style="color:#719e07">.&lt;/span>toJson&lt;span style="color:#719e07">(&lt;/span>serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//内存中的元数据同步到元数据中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> doStoreProviderMetadata&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> data&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//内存中的元数据同步到本地文件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> saveProperties&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> data&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">!&lt;/span>syncReport&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// retry again. If failed again, throw exception.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> failedReports&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> serviceDefinition&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadataReportRetry&lt;span style="color:#719e07">.&lt;/span>startRetryTask&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to put provider metadata &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> providerMetadataIdentifier &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; in &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> serviceDefinition &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, cause: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面代码我们主要看本地内存中的元数据同步到元数据中心和存本地的两个点:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//内存中的元数据同步到元数据中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>doStoreProviderMetadata&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> data&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//内存中的元数据同步到本地文件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>saveProperties&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> data&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>//内存中的元数据同步到元数据中心&lt;/p>
&lt;p>这个方法会调用当前子类重写的具体存储逻辑:这里我们以
ZookeeperMetadataReport的doStoreProviderMetadata举例:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">storeMetadata&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataIdentifier metadataIdentifier&lt;span style="color:#719e07">,&lt;/span> String v&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用zkClient创建一个节点数据为参数V v是前面说的服务定义数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> zkClient&lt;span style="color:#719e07">.&lt;/span>create&lt;span style="color:#719e07">(&lt;/span>getNodePath&lt;span style="color:#719e07">(&lt;/span>metadataIdentifier&lt;span style="color:#719e07">),&lt;/span> v&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里参数我们举个例子: 提供者的元数据内容如下:
节点路径为:&lt;/p>
&lt;ul>
&lt;li>/dubbo/metadata/link.elastic.dubbo.entity.DemoService/provider/dubbo-demo-api-provider&lt;/li>
&lt;/ul>
&lt;p>格式:&lt;/p>
&lt;ul>
&lt;li>/dubbo/metadata前缀&lt;/li>
&lt;li>服务提供者接口&lt;/li>
&lt;li>提供者类型provider&lt;/li>
&lt;li>应用名&lt;/li>
&lt;/ul>
&lt;p>具体的元数据内容如下:
比较详细的记录了应用信息,服务接口信息和服务接口对应的方法信息&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameters&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;side&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;provider&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;interface&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;link.elastic.dubbo.entity.DemoService&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;pid&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;38680&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;application&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dubbo&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;2.0.2&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;release&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3.0.8&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;anyhost&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;bind.ip&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;192.168.1.9&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;methods&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHello,sayHelloAsync&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;background&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;deprecated&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dynamic&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;service-name-mapping&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;generic&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;bind.port&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;20880&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;timestamp&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;1653097653865&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;canonicalName&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;link.elastic.dubbo.entity.DemoService&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;codeSource&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;file:/Users/song/Desktop/Computer/A/code/gitee/weaving-a-net/weaving-test/dubbo-test/target/classes/&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;methods&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameterTypes&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;returnType&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;annotations&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHelloAsync&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameterTypes&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;returnType&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;annotations&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;types&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;result&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.Object&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;stack&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture.Completion&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.Object&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture.Completion&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;next&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture.Completion&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;status&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;int&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;int&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;annotations&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>本地缓存文件的写入 可以看下如下代码
AbstractMetadataReport类型的saveProperties方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">saveProperties&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataIdentifier metadataIdentifier&lt;span style="color:#719e07">,&lt;/span> String value&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> add&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> sync&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>file &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>add&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> properties&lt;span style="color:#719e07">.&lt;/span>setProperty&lt;span style="color:#719e07">(&lt;/span>metadataIdentifier&lt;span style="color:#719e07">.&lt;/span>getUniqueKey&lt;span style="color:#719e07">(&lt;/span>KeyTypeEnum&lt;span style="color:#719e07">.&lt;/span>UNIQUE_KEY&lt;span style="color:#719e07">),&lt;/span> value&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> properties&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>metadataIdentifier&lt;span style="color:#719e07">.&lt;/span>getUniqueKey&lt;span style="color:#719e07">(&lt;/span>KeyTypeEnum&lt;span style="color:#719e07">.&lt;/span>UNIQUE_KEY&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">long&lt;/span> version &lt;span style="color:#719e07">=&lt;/span> lastCacheChanged&lt;span style="color:#719e07">.&lt;/span>incrementAndGet&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>sync&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取最新修改版本持久化到磁盘
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">new&lt;/span> SaveProperties&lt;span style="color:#719e07">(&lt;/span>version&lt;span style="color:#719e07">).&lt;/span>run&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportCacheExecutor&lt;span style="color:#719e07">.&lt;/span>execute&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> SaveProperties&lt;span style="color:#719e07">(&lt;/span>version&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>t&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>主要看如下代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> SaveProperties&lt;span style="color:#719e07">(&lt;/span>version&lt;span style="color:#719e07">).&lt;/span>run&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>SaveProperties类型代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">SaveProperties&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Runnable &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">long&lt;/span> version&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">SaveProperties&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">long&lt;/span> version&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>version &lt;span style="color:#719e07">=&lt;/span> version&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">run&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doSaveProperties&lt;span style="color:#719e07">(&lt;/span>version&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续看doSaveProperties方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doSaveProperties&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">long&lt;/span> version&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//不是最新的就不要持久化了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">&amp;lt;&lt;/span> lastCacheChanged&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>file &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Save
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建本地文件锁:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//路径为:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">///Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache.lock
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> File lockfile &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> File&lt;span style="color:#719e07">(&lt;/span>file&lt;span style="color:#719e07">.&lt;/span>getAbsolutePath&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;.lock&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//锁文件不存在则创建锁文件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>lockfile&lt;span style="color:#719e07">.&lt;/span>exists&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> lockfile&lt;span style="color:#719e07">.&lt;/span>createNewFile&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//随机访问文件工具类对象创建 读写权限
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RandomAccessFile raf &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RandomAccessFile&lt;span style="color:#719e07">(&lt;/span>lockfile&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;rw&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//文件文件Channel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//返回与此文件关联的唯一FileChannel对象。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> FileChannel channel &lt;span style="color:#719e07">=&lt;/span> raf&lt;span style="color:#719e07">.&lt;/span>getChannel&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//FileChannel中的lock()与tryLock()方法都是尝试去获取在某一文件上的独有锁（以下简称独有锁），可以实现进程间操作的互斥。区别在于lock()会阻塞（blocking）方法的执行，tryLock()则不会。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> FileLock lock &lt;span style="color:#719e07">=&lt;/span> channel&lt;span style="color:#719e07">.&lt;/span>tryLock&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果多个线程同时进来未获取锁的则抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>lock &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IOException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Can not lock the metadataReport cache file &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> file&lt;span style="color:#719e07">.&lt;/span>getAbsolutePath&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, ignore and retry later, maybe multi java process use the file, please config: dubbo.metadata.file=xxx.properties&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Save
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//文件不存在则创建本地元数据缓存文件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">///Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>file&lt;span style="color:#719e07">.&lt;/span>exists&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> file&lt;span style="color:#719e07">.&lt;/span>createNewFile&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Properties tmpProperties&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>syncReport&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// When syncReport = false, properties.setProperty and properties.store are called from the same
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// thread(reportCacheExecutor), so deep copy is not required
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> tmpProperties &lt;span style="color:#719e07">=&lt;/span> properties&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Using store method and setProperty method of the this.properties will cause lock contention
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// under multi-threading, so deep copy a new container
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//异步存储会导致锁争用 使用此的store方法和setProperty方法。属性将导致多线程下的锁争用，因此深度复制新容器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> tmpProperties &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Properties&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> entries &lt;span style="color:#719e07">=&lt;/span> properties&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> entries&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tmpProperties&lt;span style="color:#719e07">.&lt;/span>setProperty&lt;span style="color:#719e07">((&lt;/span>String&lt;span style="color:#719e07">)&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">)&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">(&lt;/span>FileOutputStream outputFile &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> FileOutputStream&lt;span style="color:#719e07">(&lt;/span>file&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//Properties类型自带的方法:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//将此属性表中的属性列表（键和元素对）以适合使用load（Reader）方法的格式写入输出字符流。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> tmpProperties&lt;span style="color:#719e07">.&lt;/span>store&lt;span style="color:#719e07">(&lt;/span>outputFile&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Dubbo metadataReport Cache&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> lock&lt;span style="color:#719e07">.&lt;/span>release&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">&amp;lt;&lt;/span> lastCacheChanged&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportCacheExecutor&lt;span style="color:#719e07">.&lt;/span>execute&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> SaveProperties&lt;span style="color:#719e07">(&lt;/span>lastCacheChanged&lt;span style="color:#719e07">.&lt;/span>incrementAndGet&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个代码太诡异了如果是lock失败也会打印异常给人非常疑惑的感觉 后续会修复
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to save service store file, cause: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>写入文件的内容大致如下&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>link.elastic.dubbo.entity.DemoService:::provider:dubbo-demo-api-provider -&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameters&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;side&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;provider&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;interface&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;link.elastic.dubbo.entity.DemoService&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;pid&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;41457&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;application&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dubbo&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;2.0.2&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;release&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3.0.8&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;anyhost&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;bind.ip&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;192.168.1.9&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;methods&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHello,sayHelloAsync&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;background&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;deprecated&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dynamic&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;service-name-mapping&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;generic&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;bind.port&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;20880&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;timestamp&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;1653100253548&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;canonicalName&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;link.elastic.dubbo.entity.DemoService&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;codeSource&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;file:/Users/song/Desktop/Computer/A/code/gitee/weaving-a-net/weaving-test/dubbo-test/target/classes/&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;methods&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHelloAsync&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameterTypes&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;returnType&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;annotations&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameterTypes&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;returnType&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;annotations&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;types&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;result&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.Object&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;stack&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture.Completion&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.Object&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture.Completion&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;next&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.util.concurrent.CompletableFuture.Completion&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;status&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;int&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;int&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;annotations&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文地址：&lt;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的三大中心之元数据中心源码解析&lt;/a>&lt;/p></description></item><item><title>Blog: 14-Dubbo配置加载全解析</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="14-dubbo配置加载全解析">14-Dubbo配置加载全解析&lt;/h1>
&lt;h2 id="141-回到启动器的初始化过程">14.1 回到启动器的初始化过程&lt;/h2>
&lt;p>在应用程序启动的时候会调用发布器的启动方法 ,然后调用初始化方法,在发布器DefaultApplicationDeployer中的初始化方法initialize() 如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>startLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register shutdown hook
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registerShutdownHook&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startConfigCenter&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadApplicationConfigs&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initModuleDeployers&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// @since 2.7.8
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> startMetadataCenter&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialized &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; has been initialized!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>初始化过程中会先启动配置中心配置信息处理,然后 调用加载初始化应用程序配置方法loadApplicationConfigs();进行配置加载
关于配置的官方文档链接为 &lt;a href="https://dubbo.apache.org/zh/docs/references/configuration/overview/">配置概述&lt;/a>&lt;/p>
&lt;p>Dubbo框架的配置项比较繁多，为了更好地管理各种配置，将其按照用途划分为不同的组件，最终所有配置项都会汇聚到URL中，传递给后续处理模块。&lt;/p>
&lt;p>&lt;strong>常用配置组件如下&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>application: Dubbo应用配置&lt;/li>
&lt;li>registry: 注册中心&lt;/li>
&lt;li>protocol: 服务提供者RPC协议&lt;/li>
&lt;li>config-center: 配置中心&lt;/li>
&lt;li>metadata-report: 元数据中心&lt;/li>
&lt;li>service: 服务提供者配置&lt;/li>
&lt;li>reference: 远程服务引用配置&lt;/li>
&lt;li>provider: service的默认配置或分组配置&lt;/li>
&lt;li>consumer: reference的默认配置或分组配置&lt;/li>
&lt;li>module: 模块配置&lt;/li>
&lt;li>monitor: 监控配置&lt;/li>
&lt;li>metrics: 指标配置&lt;/li>
&lt;li>ssl: SSL/TLS配置&lt;/li>
&lt;/ul>
&lt;p>配置还有几个比较重要的点:&lt;/p>
&lt;p>&lt;strong>配置来源&lt;/strong>
从Dubbo支持的配置来源说起，默认有6种配置来源：&lt;/p>
&lt;ul>
&lt;li>JVM System Properties，JVM -D 参数&lt;/li>
&lt;li>System environment，JVM进程的环境变量&lt;/li>
&lt;li>Externalized Configuration，外部化配置，从配置中心读取&lt;/li>
&lt;li>Application Configuration，应用的属性配置，从Spring应用的Environment中提取&amp;quot;dubbo&amp;quot;打头的属性集&lt;/li>
&lt;li>API / XML /注解等编程接口采集的配置可以被理解成配置来源的一种，是直接面向用户编程的配置采集方式&lt;/li>
&lt;li>从classpath读取配置文件 dubbo.properties&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>覆盖关系&lt;/strong>
下图展示了配置覆盖关系的优先级，从上到下优先级依次降低： &lt;img src="https://cn.dubbo.apache.org/imgs/blog/configuration.jpg" alt="在这里插入图片描述">&lt;/p>
&lt;p>&lt;strong>配置方式&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Java API配置&lt;/li>
&lt;li>XML配置&lt;/li>
&lt;li>Annotation配置&lt;/li>
&lt;li>属性配置&lt;/li>
&lt;/ul>
&lt;p>配置虽然非常多,但是我们掌握一下配置加载的原理,再了解下官网的文档说明路径应该基础的配置搞定是没问题的,更深入的配置很多参数还是需要了解下源码的.&lt;/p>
&lt;h2 id="142-配置信息的初始化回顾">14.2 配置信息的初始化回顾&lt;/h2>
&lt;p>前面我们在讲ModuleModel对象的创建的时候ModuleModel模型中包含了一个成员变量为ModuleEnvironment 代表当前的模块环境和ModuleConfigManager配置管理器
而ModuleModel模型对象的父模型对象ApplicationModel中包含了一个成员变量Environment环境和ConfigManager配置管理器.&lt;/p>
&lt;p>在回顾调用过程之前我们先看下模型,配置管理器和环境与配置之间的关系如下图:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/14-config.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>在ModuleConfigManager对象初始化方法initialize()中创建了模块配置管理器:ModuleConfigManager
如下代码所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceRepository &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ModuleServiceRepository&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleConfigManager &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ModuleConfigManager&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleConfigManager&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ModuleEnvironment环境信息对象也会在配置管理器创建的时候被调用到:
如下代码所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> ModuleEnvironment &lt;span style="color:#268bd2">getModelEnvironment&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>moduleEnvironment &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> moduleEnvironment &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ModuleEnvironment&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ModuleExt&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>ModuleEnvironment&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> moduleEnvironment&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在扩展对象ExtensionLoader进行对象ModuleEnvironment创建之后会对对象进行初始化调用 initExtension(instance)方法 初始化的时候调用如下代码:
ExtensionLoader中的初始化方法如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>T instance&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">instanceof&lt;/span> Lifecycle&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Lifecycle lifecycle &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Lifecycle&lt;span style="color:#719e07">)&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> lifecycle&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="143-属性加载">14.3 属性加载&lt;/h2>
&lt;h3 id="1431-environment中属性的初始化方法">14.3.1 Environment中属性的初始化方法&lt;/h3>
&lt;p>这个初始化方法对应ModuleEnvironment的父类型Environment中的初始化方法如下:initialize()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载在JVM或者环境变量指定的dubbo.properties配置文件 配置的key为dubbo.properties.file ,如果未指定则查找类路径下的dubbo.properties
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>propertiesConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> PropertiesConfiguration&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//系统JVM参数的配置无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getProperty(key)来调用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>systemConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> SystemConfiguration&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//系统环境变量的配置,无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getenv(key)来获取就可以
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>environmentConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> EnvironmentConfiguration&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从远程配置中心的全局配置获取对应配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>externalConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InmemoryConfiguration&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;ExternalConfig&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从远程配置中心的应用配置获取对应配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>appExternalConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InmemoryConfiguration&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;AppExternalConfig&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//应用内的配置比如: Spring Environment/PropertySources/application.properties
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>appConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InmemoryConfiguration&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;AppConfig&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载迁移配置,用户在JVM参数或者环境变量中指定的dubbo.migration.file,如果用户未指定测尝试加载类路径下的dubbo-migration.yaml
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadMigrationRule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1442--属性变量说明">14.4.2 属性变量说明&lt;/h3>
&lt;p>前面我们已经基本上介绍了各个属性的含义下面用一个表格列举一下方便查看:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性变量名&lt;/th>
&lt;th>属性类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>propertiesConfiguration&lt;/td>
&lt;td>PropertiesConfiguration&lt;/td>
&lt;td>dubbo.properties文件中的属性&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>systemConfiguration&lt;/td>
&lt;td>SystemConfiguration&lt;/td>
&lt;td>JVM参数 启动进程时指定的 (-D)配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>environmentConfiguration&lt;/td>
&lt;td>EnvironmentConfiguration&lt;/td>
&lt;td>环境变量中的配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>externalConfiguration&lt;/td>
&lt;td>InmemoryConfiguration&lt;/td>
&lt;td>外部配置全局配置 例如配置中心中 config-center global/default config&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>appExternalConfiguration&lt;/td>
&lt;td>InmemoryConfiguration&lt;/td>
&lt;td>外部的应用配置 例如配置中心中执行的当前应用的配置 config-center app config&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>appConfiguration&lt;/td>
&lt;td>InmemoryConfiguration&lt;/td>
&lt;td>来自应用中的配置例如:Spring Environment/PropertySources/application.properties&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>globalConfiguration&lt;/td>
&lt;td>CompositeConfiguration&lt;/td>
&lt;td>前面6个配置属性放到一起就是这个&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>globalConfigurationMaps&lt;/td>
&lt;td>List&amp;lt;Map&amp;lt;String, String&amp;raquo;&lt;/td>
&lt;td>最前面的6个属性转换为map放到一起就是这个可以理解为将全局配置globalConfiguration转换成了列表 这个列表顺序在这里是:SystemConfiguration -&amp;gt; EnvironmentConfiguration -&amp;gt; AppExternalConfiguration -&amp;gt; ExternalConfiguration -&amp;gt; AppConfiguration -&amp;gt; AbstractConfig -&amp;gt; PropertiesConfiguration&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>defaultDynamicGlobalConfiguration&lt;/td>
&lt;td>CompositeConfiguration&lt;/td>
&lt;td>这个也是一个组合配置将defaultDynamicConfiguration动态配置(来自配置中心的配置)和全局配置添加到了自己的配置列表中 列表顺序为defaultDynamicConfiguration -&amp;gt; globalConfiguration&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>localMigrationRule&lt;/td>
&lt;td>String&lt;/td>
&lt;td>,用户在JVM参数或者环境变量中指定的dubbo.migration.file,如果用户未指定测尝试加载类路径下的dubbo-migration.yaml&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>关于每个配置信息这里还是来了解下细节,方便大家了解原理.&lt;/p>
&lt;h3 id="1433-dubboproperties配置文件加载解析原理">14.3.3 dubbo.properties配置文件加载解析原理&lt;/h3>
&lt;p>如前面所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//加载在JVM或者环境变量指定的dubbo.properties配置文件 配置的key为dubbo.properties.file ,如果未指定则查找类路径下的dubbo.properties
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>propertiesConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> PropertiesConfiguration&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下面就直接提构造器的PropertiesConfiguration代码了:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">PropertiesConfiguration&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>scopeModel &lt;span style="color:#719e07">=&lt;/span> scopeModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refresh&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置获取的过程是借助工具类ConfigUtils来获取的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> properties &lt;span style="color:#719e07">=&lt;/span> ConfigUtils&lt;span style="color:#719e07">.&lt;/span>getProperties&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">.&lt;/span>getClassLoaders&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续看ConfigUtils的getProperties方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> Properties &lt;span style="color:#268bd2">getProperties&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader&lt;span style="color:#719e07">&amp;gt;&lt;/span> classLoaders&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个配置的KEY是dubbo.properties.file System.getProperty是从JVM参数中获取配置的 一般情况下我们在启动Java进程的时候会指定Dubbo配置文件 如配置:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//-Ddubbo.properties.file=/dubbo.properties
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String path &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_PROPERTIES_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//优先级最高的JVM参数拿不到数据则从 环境变量中获取,这个配置key也是dubbo.properties.file System.getenv是从环境变量中获取数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//例如我们在环境变量中配置 dubbo.properties.file=/dubbo.properties
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> path &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getenv&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_PROPERTIES_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果在JVM参数和环境变量都拿不到这个配置文件的路径我们就用默认的吧
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//默认的路径是类路径下的资源文件 这个路径是: dubbo.properties
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> path &lt;span style="color:#719e07">=&lt;/span> CommonConstants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_DUBBO_PROPERTIES&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ConfigUtils&lt;span style="color:#719e07">.&lt;/span>loadProperties&lt;span style="color:#719e07">(&lt;/span>classLoaders&lt;span style="color:#719e07">,&lt;/span> path&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>路径获取之后加载详细的配置内容:&lt;/p>
&lt;p>ConfigUtils的loadProperties代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ConfigUtils&lt;span style="color:#719e07">.&lt;/span>loadProperties&lt;span style="color:#719e07">(&lt;/span>classLoaders&lt;span style="color:#719e07">,&lt;/span> path&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> Properties &lt;span style="color:#268bd2">loadProperties&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader&lt;span style="color:#719e07">&amp;gt;&lt;/span> classLoaders&lt;span style="color:#719e07">,&lt;/span> String fileName&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> allowMultiFile&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> optional&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Properties properties &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Properties&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// add scene judgement in windows environment Fix 2557
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//检查文件是否存在 直接加载配置文件如果加载到了配置文件则直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>checkFileNameExist&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FileInputStream input &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> FileInputStream&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> properties&lt;span style="color:#719e07">.&lt;/span>load&lt;span style="color:#719e07">(&lt;/span>input&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> input&lt;span style="color:#719e07">.&lt;/span>close&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to load &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; file from &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;(ignore this file): &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> properties&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为什么会有下面的逻辑呢,如果仅仅使用上面的加载方式只能加载到本系统下的配置文件,无法加载封装在jar中的根路径的配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>java&lt;span style="color:#719e07">.&lt;/span>net&lt;span style="color:#719e07">.&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> set &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader&lt;span style="color:#719e07">&amp;gt;&lt;/span> classLoadersToLoad &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> LinkedList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classLoadersToLoad&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>ClassUtils&lt;span style="color:#719e07">.&lt;/span>getClassLoader&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classLoadersToLoad&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>classLoaders&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个方法loadResources在扩展加载的时候说过
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> set &lt;span style="color:#719e07">=&lt;/span> ClassLoaderResourceLoader&lt;span style="color:#719e07">.&lt;/span>loadResources&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">,&lt;/span> classLoadersToLoad&lt;span style="color:#719e07">).&lt;/span>values&lt;span style="color:#719e07">().&lt;/span>stream&lt;span style="color:#719e07">().&lt;/span>reduce&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> LinkedHashSet&lt;span style="color:#719e07">&amp;lt;&amp;gt;(),&lt;/span> &lt;span style="color:#719e07">(&lt;/span>a&lt;span style="color:#719e07">,&lt;/span> i&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> a&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>i&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> a&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Fail to load &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; file: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>set&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>optional&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;No &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; found on the class path.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> properties&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>allowMultiFile&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>set&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String errMsg &lt;span style="color:#719e07">=&lt;/span> String&lt;span style="color:#719e07">.&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;only 1 %s file is expected, but %d dubbo.properties files found on class path: %s&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fileName&lt;span style="color:#719e07">,&lt;/span> set&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">(),&lt;/span> set&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>errMsg&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// fall back to use method getResourceAsStream
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> properties&lt;span style="color:#719e07">.&lt;/span>load&lt;span style="color:#719e07">(&lt;/span>ClassUtils&lt;span style="color:#719e07">.&lt;/span>getClassLoader&lt;span style="color:#719e07">().&lt;/span>getResourceAsStream&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to load &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; file from &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;(ignore this file): &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> properties&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;load &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; properties file from &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> set&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>java&lt;span style="color:#719e07">.&lt;/span>net&lt;span style="color:#719e07">.&lt;/span>URL url &lt;span style="color:#719e07">:&lt;/span> set&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Properties p &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Properties&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> InputStream input &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>openStream&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>input &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> p&lt;span style="color:#719e07">.&lt;/span>load&lt;span style="color:#719e07">(&lt;/span>input&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> properties&lt;span style="color:#719e07">.&lt;/span>putAll&lt;span style="color:#719e07">(&lt;/span>p&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> input&lt;span style="color:#719e07">.&lt;/span>close&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Fail to load &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; file from &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;(ignore this file): &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> properties&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>完整的配置加载流程这里用简单的话描述下:&lt;/p>
&lt;ul>
&lt;li>项目内配置查询
&lt;ul>
&lt;li>路径查询
&lt;ul>
&lt;li>从JVM参数中获取配置的 dubbo.properties.file配置文件路径&lt;/li>
&lt;li>如果前面未获取到路径则从环境变量参数中获取配置的dubbo.properties.file配置文件路径&lt;/li>
&lt;li>如果前面未获取到路径则使用默认路径dubbo.propertie&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>配置加载
&lt;ul>
&lt;li>将路径转为FileInputStream 然后使用Properties加载&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>依赖中的配置扫描查询
&lt;ul>
&lt;li>使用类加载器扫描所有资源URL&lt;/li>
&lt;li>url转InputStream 如 url.openStream() 然后使用Properties加载&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="1434-加载jvm参数的配置">14.3.4 加载JVM参数的配置&lt;/h3>
&lt;p>这里我们继续看SystemConfiguration配置的加载
这个直接看下代码就可以了:&lt;/p>
&lt;p>这个类型仅仅是使用System.getProperty来获取JVM配置即可&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">SystemConfiguration&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Configuration &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Object &lt;span style="color:#268bd2">getInternalProperty&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String key&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getProperties&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">)&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getProperties&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1435-加载环境变量参数的配置">14.3.5 加载环境变量参数的配置&lt;/h3>
&lt;p>这里我们来看EnvironmentConfiguration,这里我们直接来看代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">EnvironmentConfiguration&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Configuration &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Object &lt;span style="color:#268bd2">getInternalProperty&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String key&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String value &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getenv&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> value &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getenv&lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>toOSStyleKey&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> value&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getProperties&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getenv&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1436-内存配置的封装inmemoryconfiguration">14.3.6 内存配置的封装:InmemoryConfiguration&lt;/h3>
&lt;p>这里我们看下InmemoryConfiguration的设计,这个直接看代码吧内部使用了一个LinkedHashMap来存储配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">InmemoryConfiguration&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Configuration &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String name&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// stores the configuration key-value pairs
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">private&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> store &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> LinkedHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">InmemoryConfiguration&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">InmemoryConfiguration&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>name &lt;span style="color:#719e07">=&lt;/span> name&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">InmemoryConfiguration&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> properties&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>setProperties&lt;span style="color:#719e07">(&lt;/span>properties&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Object &lt;span style="color:#268bd2">getInternalProperty&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String key&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> store&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Add one property into the store, the previous value will be replaced if the key exists
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addProperty&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String key&lt;span style="color:#719e07">,&lt;/span> String value&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> store&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> value&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Add a set of properties into the store
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addProperties&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> properties&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>properties &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>store&lt;span style="color:#719e07">.&lt;/span>putAll&lt;span style="color:#719e07">(&lt;/span>properties&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * set store
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setProperties&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> properties&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>properties &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>store &lt;span style="color:#719e07">=&lt;/span> properties&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getProperties&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> store&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1437-dubbo迁移新版本的配置文件加载dubbo-migrationyaml">14.3.7 Dubbo迁移新版本的配置文件加载dubbo-migration.yaml&lt;/h3>
&lt;p>关于配置迁移文件的用法可以看下这个Dubbo官方的&lt;a href="https://dubbo.apache.org/zh/docs/v3.0/advanced/migration-invoker/">地址迁移规则说明&lt;/a>&lt;/p>
&lt;p>这个配置文件的文件名字为:dubbo-migration.yaml&lt;/p>
&lt;p>这个和14.3.4加载JVM参数配置的过程是相似的细节可以看14.3.4节&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">loadMigrationRule&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//JVM参数的dubbo.migration.file配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String path &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_MIGRATION_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//环境变量的dubbo.migration.file配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> path &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getenv&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_MIGRATION_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//默认的迁移配置文件 dubbo-migration.yaml
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> path &lt;span style="color:#719e07">=&lt;/span> CommonConstants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_DUBBO_MIGRATION_FILE&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>localMigrationRule &lt;span style="color:#719e07">=&lt;/span> ConfigUtils&lt;span style="color:#719e07">.&lt;/span>loadMigrationRule&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">.&lt;/span>getClassLoaders&lt;span style="color:#719e07">(),&lt;/span> path&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="144-初始化加载应用配置">14.4 初始化加载应用配置&lt;/h2>
&lt;p>加载配置涉及到了配置优先级的处理,&lt;/p>
&lt;p>下面来看加载配置代码 loadApplicationConfigs()方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">loadApplicationConfigs&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//发布器还是不处理配置加载的逻辑还是交给配置管理器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>loadConfigs&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置管理器加载配置:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">loadConfigs&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// application config has load before starting config center
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// load dubbo.applications.xxx
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//加载应用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load dubbo.monitors.xxx
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//加载监控配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>MonitorConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load dubbo.metrics.xxx
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//加载指标监控配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>MetricsConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load multiple config types:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// load dubbo.protocols.xxx
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//加载协议配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>ProtocolConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load dubbo.registries.xxx
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>RegistryConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load dubbo.metadata-report.xxx
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//加载元数据配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// config centers has bean loaded before starting config center
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//loadConfigsOfTypeFromProps(ConfigCenterConfig.class);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> refreshAll&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkConfigs&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// set model name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isBlank&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>getModelName&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationModel&lt;span style="color:#719e07">.&lt;/span>setModelName&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文地址：&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/14-dubbo-pei-zhi-jia-zai-quan-jie-xi/">Dubbo配置加载全解析&lt;/a>&lt;/p></description></item><item><title>Blog: 13-Dubbo的三大中心之配置中心</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="13-dubbo的三大中心之配置中心">13-Dubbo的三大中心之配置中心&lt;/h1>
&lt;h2 id="131-配置中心简介">13.1 配置中心简介&lt;/h2>
&lt;p>百度了一段不错的文字来介绍配置中心，我看了下肯定比我写的好多了，那我就直接拷贝过来一起看：&lt;/p>
&lt;p>&lt;em>对于传统的单体应用而言，常使用配置文件来管理所有配置，比如SpringBoot的application.yml文件，但是在微服务架构中全部手动修改的话很麻烦而且不易维护。微服务的配置管理一般有以下需求：&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>&lt;strong>集中配置管理&lt;/strong>，一个微服务架构中可能有成百上千个微服务，所以集中配置管理是很重要的。&lt;/em>&lt;/li>
&lt;li>&lt;em>&lt;strong>不同环境不同配置&lt;/strong>，比如数据源配置在不同环境（开发，生产，测试）中是不同的。&lt;/em>&lt;/li>
&lt;li>&lt;em>&lt;strong>运行期间可动态调整&lt;/strong>。例如，可根据各个微服务的负载情况，动态调整数据源连接池大小等。&lt;/em>&lt;/li>
&lt;li>&lt;em>&lt;strong>配置修改后可自动更新&lt;/strong>。如配置内容发生变化，微服务可以自动更新配置。&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>综上所述对于微服务架构而言，一套统一的，通用的管理配置机制是不可缺少的主要组成部分。常见的做法就是通过配置服务器进行管理。&lt;/p>
&lt;p>不过对于来看这个文章的小伙伴应该大部分对配置中心都会比较了解，分布式配置中心实现简单一点就是借助Zookeeper来协助存储，变更推送，不过为了实现各种不同的业务需求，市面上已经有很多很可靠的配置中心可用了，比如我从其他地方拷贝过来的图（虽然不是最新的但是可以供大家参考下）：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/register.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>每个配置中心都有自己的实现，如果对配置中心感兴趣的小伙伴可以自行去对应开源项目官网查看，我们这里来看Dubbo对配置中心的支持&lt;/p>
&lt;p>&lt;em>&lt;strong>多配置中心：&lt;/strong> Dubbo支持多配置中心，来 &lt;strong>保证其中一个配置中心集群出现不可用时能够切换到另一个配置中心集群&lt;/strong> ，保证能够正常从配置中心获取全局的配置、路由规则等信息。这也能够满足配置中心在部署上适应各类高可用的部署架构模式。-来自官网&lt;/em>&lt;/p>
&lt;p>做中间件可能考虑更多的的不仅仅是性能，还要过多的考虑高可用，高可用怎么做呢，其实就是失效转移，主备切换，降级，降级再降级这些理论的运用，多多考虑某一个服务挂了怎么办，Dubbo的多配置中心支持增加了复杂性，不过降低了服务不可用的风险，有一定的人手的公司还是值得做的。&lt;/p>
&lt;p>关于Dubbo的配置中心这里我来贴个官网的图:
&lt;img src="https://cn.dubbo.apache.org/imgs/v3/concepts/centers-config.png" alt="在这里插入图片描述">
关于官网的介绍可以自行去官网看详细内容: &lt;a href="https://dubbo.apache.org/zh/docs/concepts/registry-configcenter-metadata/">部署架构(注册中心、配置中心、元数据中心&lt;/a>&lt;/p>
&lt;h2 id="132-启动配置中心">13.2 启动配置中心&lt;/h2>
&lt;p>在上一个博客中说到了&lt;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的服务启动生命周期》&lt;/a>Dubbo应用的启动过程DefaultApplicationDeployer的initialize()方法的全生命周期，在初始化方法中通过调用startConfigCenter();方法来启动配置中心的加载。后面就来详细看下：&lt;/p>
&lt;p>DefaultApplicationDeployer类型的startConfigCenter()代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startConfigCenter&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load application config
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//加载应用程序配置 （配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置，也可能是多应用，多注册中心这样的配置）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// try set model name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isBlank&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>getModelName&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//设置一下模块名字和模块描述（我们再Debug里面经常会看到这个描述信息 toString直接返回了Dubbo为我们改造的对象信息）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>setModelName&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>tryGetApplicationName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load config centers
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//加载配置中心配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置，也可能是多应用，多注册中心这样的配置）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>ConfigCenterConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//出于兼容性目的，如果没有明确指定配置中心，并且registryConfig的UseAConfigCenter为null或true，请使用registry作为默认配置中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> useRegistryAsConfigCenterIfNecessary&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// check Config Center
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//配置管理器中获取配置中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Collection&lt;span style="color:#719e07">&amp;lt;&lt;/span>ConfigCenterConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> configCenters &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getConfigCenters&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置中心配置不为空则刷新配置中心配置将其放入配置管理器中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//下面开始刷新配置中心配置,如果配置中心配置为空则执行空刷新
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>configCenters&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置中心不存在的配置刷新
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ConfigCenterConfig configCenterConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConfigCenterConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configCenterConfig&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configCenterConfig&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//验证配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ConfigValidationUtils&lt;span style="color:#719e07">.&lt;/span>validateConfigCenterConfig&lt;span style="color:#719e07">(&lt;/span>configCenterConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>configCenterConfig&lt;span style="color:#719e07">.&lt;/span>isValid&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置合法则将配置放入配置管理器中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>addConfigCenter&lt;span style="color:#719e07">(&lt;/span>configCenterConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configCenters &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getConfigCenters&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//一个或者多个配置中心配置存在的情况下的配置刷新
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigCenterConfig configCenterConfig &lt;span style="color:#719e07">:&lt;/span> configCenters&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configCenterConfig&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//验证配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ConfigValidationUtils&lt;span style="color:#719e07">.&lt;/span>validateConfigCenterConfig&lt;span style="color:#719e07">(&lt;/span>configCenterConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置中心配置不为空则将配置中心配置添加到environment中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>configCenters&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//多配置中心本地动态配置对象创建CompositeDynamicConfiguration
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> CompositeDynamicConfiguration compositeDynamicConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CompositeDynamicConfiguration&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取配置中心的相关配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigCenterConfig configCenter &lt;span style="color:#719e07">:&lt;/span> configCenters&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Pass config from ConfigCenterBean to environment
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//将配置中心的外部化配置,更新到环境里面
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> environment&lt;span style="color:#719e07">.&lt;/span>updateExternalConfigMap&lt;span style="color:#719e07">(&lt;/span>configCenter&lt;span style="color:#719e07">.&lt;/span>getExternalConfiguration&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将配置中心的应用配置,添加到环境里面
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> environment&lt;span style="color:#719e07">.&lt;/span>updateAppExternalConfigMap&lt;span style="color:#719e07">(&lt;/span>configCenter&lt;span style="color:#719e07">.&lt;/span>getAppExternalConfiguration&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Fetch config from remote config center
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//从配置中心拉取配置添加到组合配置中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> compositeDynamicConfiguration&lt;span style="color:#719e07">.&lt;/span>addConfiguration&lt;span style="color:#719e07">(&lt;/span>prepareEnvironment&lt;span style="color:#719e07">(&lt;/span>configCenter&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将配置中心中的动态配置信息 设置到environment的动态配置属性中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> environment&lt;span style="color:#719e07">.&lt;/span>setDynamicConfiguration&lt;span style="color:#719e07">(&lt;/span>compositeDynamicConfiguration&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1321-配置管理器加载配置">13.2.1 配置管理器加载配置&lt;/h3>
&lt;p>前面我们看到了配置管理器会从系统属性中加载配置这里我们来详细看下，配置往往是我们使用者比较关注的内容，&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>configManager&lt;span style="color:#719e07">.&lt;/span>loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置管理器加载配置代码:
来自ConfigManager的父类型AbstractConfigManager中&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T &lt;span style="color:#268bd2">extends&lt;/span> AbstractConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">loadConfigsOfTypeFromProps&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> cls&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> tmpConfigs &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取属性配置 dubbo properties in classpath
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这个配置信息回头说
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> PropertiesConfiguration properties &lt;span style="color:#719e07">=&lt;/span> environment&lt;span style="color:#719e07">.&lt;/span>getPropertiesConfiguration&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load multiple configs with id
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//多注册中心配置id查询
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> 搜索属性并提取指定类型的配置ID。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> 例如如下配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> # 配置信息 properties
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> dubbo.registries.registry1.address=xxx
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> dubbo.registries.registry2.port=xxx
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> # 提取配置的id extract
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> Set configIds = getConfigIds(RegistryConfig.class)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> # 提取的配置id结果 result
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> configIds: [&amp;#34;registry1&amp;#34;, &amp;#34;registry2&amp;#34;]
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> configIds &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getConfigIdsFromProps&lt;span style="color:#719e07">(&lt;/span>cls&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configIds&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>id &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历这些配置id 判断配置缓存(configsCache成员变量)中是否已经存在当前配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getConfig&lt;span style="color:#719e07">(&lt;/span>cls&lt;span style="color:#719e07">,&lt;/span> id&lt;span style="color:#719e07">).&lt;/span>isPresent&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> T config&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建配置对象 为配置对象初始化配置id
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> config &lt;span style="color:#719e07">=&lt;/span> createConfig&lt;span style="color:#719e07">(&lt;/span>cls&lt;span style="color:#719e07">,&lt;/span> scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config&lt;span style="color:#719e07">.&lt;/span>setId&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;create config instance failed, id: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> id &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, type:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> cls&lt;span style="color:#719e07">.&lt;/span>getSimpleName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String key &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> addDefaultNameConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// add default name config (same as id), e.g. dubbo.protocols.rest.port=1234
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> key &lt;span style="color:#719e07">=&lt;/span> DUBBO &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;.&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> AbstractConfig&lt;span style="color:#719e07">.&lt;/span>getPluralTagName&lt;span style="color:#719e07">(&lt;/span>cls&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;.&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> id &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;.name&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>properties&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> properties&lt;span style="color:#719e07">.&lt;/span>setProperty&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> id&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> addDefaultNameConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新配置信息 好理解点就是Dubbo配置属性重写
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将当前配置信息添加到配置缓存中configsCache成员变量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>addConfig&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tmpConfigs&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;load config failed, id: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> id &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, type:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> cls&lt;span style="color:#719e07">.&lt;/span>getSimpleName&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;load config failed, id: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> id &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, type:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> cls&lt;span style="color:#719e07">.&lt;/span>getSimpleName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>addDefaultNameConfig &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> key &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> properties&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// If none config of the type, try load single config
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果没有该类型的配置，请尝试加载单个配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getConfigs&lt;span style="color:#719e07">(&lt;/span>cls&lt;span style="color:#719e07">).&lt;/span>isEmpty&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load single config
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> configurationMaps &lt;span style="color:#719e07">=&lt;/span> environment&lt;span style="color:#719e07">.&lt;/span>getConfigurationMaps&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigurationUtils&lt;span style="color:#719e07">.&lt;/span>hasSubProperties&lt;span style="color:#719e07">(&lt;/span>configurationMaps&lt;span style="color:#719e07">,&lt;/span> AbstractConfig&lt;span style="color:#719e07">.&lt;/span>getTypePrefix&lt;span style="color:#719e07">(&lt;/span>cls&lt;span style="color:#719e07">)))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> T config&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config &lt;span style="color:#719e07">=&lt;/span> createConfig&lt;span style="color:#719e07">(&lt;/span>cls&lt;span style="color:#719e07">,&lt;/span> scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;create default config instance failed, type:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> cls&lt;span style="color:#719e07">.&lt;/span>getSimpleName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>addConfig&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tmpConfigs&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> tmpConfigs&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="1322--默认使用注册中心地址为配置中心">13.2.2 默认使用注册中心地址为配置中心&lt;/h2>
&lt;p>出于兼容性目的，如果没有明确指定配置中心，并且registryConfig的UseAConfigCenter为null或true，请使用registry作为默认配置中心
调用方法useRegistryAsConfigCenterIfNecessary()来处理逻辑
我们来看下代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">useRegistryAsConfigCenterIfNecessary&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//我们使用DynamicConfiguration的加载状态来决定是否已启动ConfigCenter。配置中心配置加载完成之后会初始化动态配置defaultDynamicConfiguration
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>environment&lt;span style="color:#719e07">.&lt;/span>getDynamicConfiguration&lt;span style="color:#719e07">().&lt;/span>isPresent&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从配置缓存中查询是否存在config-center相关配置 ,如果已经存在配置了就无需使用注册中心的配置地址直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>configManager&lt;span style="color:#719e07">.&lt;/span>getConfigCenters&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//加载注册中心相关配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>loadConfigsOfTypeFromProps&lt;span style="color:#719e07">(&lt;/span>RegistryConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> defaultRegistries &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getDefaultRegistries&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//存在注册中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>defaultRegistries&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> defaultRegistries
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>stream&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//判断当前注册中心是否可以作为配置中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>filter&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>isUsedRegistryAsConfigCenter&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将注册中心配置映射转换为配置中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>map&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>registryAsConfigCenter&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历配置中心流
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>configCenter &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>configManager&lt;span style="color:#719e07">.&lt;/span>getConfigCenter&lt;span style="color:#719e07">(&lt;/span>configCenter&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">()).&lt;/span>isPresent&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置管理器中添加配置中心,方便后去读取配置中心的配置信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>addConfigCenter&lt;span style="color:#719e07">(&lt;/span>configCenter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;use registry as config-center: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> configCenter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="13221-如何判断当前注册中心是否可以为配置中心">13.2.2.1 如何判断当前注册中心是否可以为配置中心&lt;/h4>
&lt;p>isUsedRegistryAsConfigCenter&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isUsedRegistryAsCenter&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RegistryConfig registryConfig&lt;span style="color:#719e07">,&lt;/span> Supplier&lt;span style="color:#719e07">&amp;lt;&lt;/span>Boolean&lt;span style="color:#719e07">&amp;gt;&lt;/span> usedRegistryAsCenter&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String centerType&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> extensionClass&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> supported&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个useAsConfigCenter参数是来自注册中心的配置 如果配置了这个值则以这个值为准,如果配置了false则这个注册中心不能做为配置中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Boolean configuredValue &lt;span style="color:#719e07">=&lt;/span> usedRegistryAsCenter&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>configuredValue &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#586e75">// If configured, take its value.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> supported &lt;span style="color:#719e07">=&lt;/span> configuredValue&lt;span style="color:#719e07">.&lt;/span>booleanValue&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#586e75">// Or check the extension existence
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这个逻辑的话是判断下注册中心的协议是否满足要求,我们例子代码中使用的是zookeeper
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String protocol &lt;span style="color:#719e07">=&lt;/span> registryConfig&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个扩展是否支持的逻辑判断是这样的扫描扩展类 看一下当前扩展类型是否有对应协议的扩展 比如在扩展文件里面这样配置过后是支持的 protocol=xxxImpl
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//动态配置的扩展类型为:interface org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//zookeeper协议肯定是支持的因为zookeeper协议实现了这个动态配置工厂 ,这个扩展类型为ZookeeperDynamicConfigurationFactory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//代码位置在dubbo-configcenter-zookeeper包中的org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory扩展配置中内容为zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> supported &lt;span style="color:#719e07">=&lt;/span> supportsExtension&lt;span style="color:#719e07">(&lt;/span>extensionClass&lt;span style="color:#719e07">,&lt;/span> protocol&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置中心走注册中心会打印一条日志
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;No value is configured in the registry, the %s extension[name : %s] %s as the %s center&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">,&lt;/span> extensionClass&lt;span style="color:#719e07">.&lt;/span>getSimpleName&lt;span style="color:#719e07">(),&lt;/span> protocol&lt;span style="color:#719e07">,&lt;/span> supported &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#2aa198">&amp;#34;supports&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;does not support&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> centerType&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置中心走注册中心会打印一条日志
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;The registry[%s] will be %s as the %s center&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> registryConfig&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> supported &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#2aa198">&amp;#34;used&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;not used&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> centerType&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> supported&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个扩展是否支持的逻辑判断是这样的扫描扩展类 看一下当前扩展类型是否有对应协议的扩展 比如在扩展文件里面这样配置过后是支持的 protocol=xxxImpl
配置中心的动态配置的扩展类型为 org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory&lt;/p>
&lt;p>zookeeper协议肯定是支持的因为zookeeper协议实现了这个动态配置工厂 ,这个扩展类型为ZookeeperDynamicConfigurationFactory代码位置在dubbo-configcenter-zookeeper包中的org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory扩展配置中内容为&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="13222-注册中心配置转配置中心配置">13.2.2.2 注册中心配置转配置中心配置&lt;/h4>
&lt;p>这个逻辑是registryAsConfigCenter方法,我来贴一下代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> ConfigCenterConfig &lt;span style="color:#268bd2">registryAsConfigCenter&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RegistryConfig registryConfig&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心协议获取这里例子中的是zookeeper协议
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String protocol &lt;span style="color:#719e07">=&lt;/span> registryConfig&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心端口 2181
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Integer port &lt;span style="color:#719e07">=&lt;/span> registryConfig&lt;span style="color:#719e07">.&lt;/span>getPort&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//在Dubbo中配置信息 很多情况下都以URL形式表示,这里转换后的地址为zookeeper://127.0.0.1:2181
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> URL url &lt;span style="color:#719e07">=&lt;/span> URL&lt;span style="color:#719e07">.&lt;/span>valueOf&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">(),&lt;/span> registryConfig&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//生成当前配置中心的id 封装之后的内容为:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//config-center-zookeeper-127.0.0.1-2181
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String id &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;config-center-&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> protocol &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;-&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getHost&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;-&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> port&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置中心配置对象创建
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ConfigCenterConfig cc &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConfigCenterConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//config-center-zookeeper-127.0.0.1-2181
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cc&lt;span style="color:#719e07">.&lt;/span>setId&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cc&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cc&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cc&lt;span style="color:#719e07">.&lt;/span>setParameters&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmptyMap&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cc&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">().&lt;/span>putAll&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">());&lt;/span> &lt;span style="color:#586e75">// copy the parameters
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cc&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">().&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>CLIENT_KEY&lt;span style="color:#719e07">,&lt;/span> registryConfig&lt;span style="color:#719e07">.&lt;/span>getClient&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//zookeeper
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cc&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>protocol&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//2181
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cc&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>port&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cc&lt;span style="color:#719e07">.&lt;/span>setGroup&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个方法转换地址是修复bug用的可以看bug https://github.com/apache/dubbo/issues/6476
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cc&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>getRegistryCompatibleAddress&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心分组做为配置中心命名空间 这里为null
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cc&lt;span style="color:#719e07">.&lt;/span>setNamespace&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//zk认证信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cc&lt;span style="color:#719e07">.&lt;/span>setUsername&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getUsername&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//zk认证信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cc&lt;span style="color:#719e07">.&lt;/span>setPassword&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getPassword&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getTimeout&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cc&lt;span style="color:#719e07">.&lt;/span>setTimeout&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getTimeout&lt;span style="color:#719e07">().&lt;/span>longValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个属性注释中已经建议了已经弃用了默认就是false了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果配置中心被赋予最高优先级，它将覆盖所有其他配置，
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cc&lt;span style="color:#719e07">.&lt;/span>setHighestPriority&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> cc&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="133-配置刷新逻辑">13.3 配置刷新逻辑&lt;/h2>
&lt;p>来自AbstractConfig类型的refresh()方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refresh&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> refreshed&lt;span style="color:#719e07">.&lt;/span>set&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// check and init before do refresh
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//刷新之前执行的逻辑 这里并做什么逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> preProcessRefresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取当前域模型的环境信息对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Environment environment &lt;span style="color:#719e07">=&lt;/span> getScopeModel&lt;span style="color:#719e07">().&lt;/span>getModelEnvironment&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> configurationMaps &lt;span style="color:#719e07">=&lt;/span> environment&lt;span style="color:#719e07">.&lt;/span>getConfigurationMaps&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Search props starts with PREFIX in order
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String preferredPrefix &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String prefix &lt;span style="color:#719e07">:&lt;/span> getPrefixes&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigurationUtils&lt;span style="color:#719e07">.&lt;/span>hasSubProperties&lt;span style="color:#719e07">(&lt;/span>configurationMaps&lt;span style="color:#719e07">,&lt;/span> prefix&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> preferredPrefix &lt;span style="color:#719e07">=&lt;/span> prefix&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>preferredPrefix &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> preferredPrefix &lt;span style="color:#719e07">=&lt;/span> getPrefixes&lt;span style="color:#719e07">().&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Extract sub props (which key was starts with preferredPrefix)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Collection&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> instanceConfigMaps &lt;span style="color:#719e07">=&lt;/span> environment&lt;span style="color:#719e07">.&lt;/span>getConfigurationMaps&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> preferredPrefix&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> subProperties &lt;span style="color:#719e07">=&lt;/span> ConfigurationUtils&lt;span style="color:#719e07">.&lt;/span>getSubProperties&lt;span style="color:#719e07">(&lt;/span>instanceConfigMaps&lt;span style="color:#719e07">,&lt;/span> preferredPrefix&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> InmemoryConfiguration subPropsConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InmemoryConfiguration&lt;span style="color:#719e07">(&lt;/span>subProperties&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isDebugEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String idOrName &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>hasText&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> idOrName &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;[id=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;]&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String name &lt;span style="color:#719e07">=&lt;/span> ReflectUtils&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;getName&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>hasText&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> idOrName &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;[name=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;]&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>debug&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Refreshing &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">().&lt;/span>getSimpleName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> idOrName &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34; with prefix [&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> preferredPrefix &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;], extracted props: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> subProperties&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> assignProperties&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> environment&lt;span style="color:#719e07">,&lt;/span> subProperties&lt;span style="color:#719e07">,&lt;/span> subPropsConfiguration&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// process extra refresh of subclass, e.g. refresh method configs
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> processExtraRefresh&lt;span style="color:#719e07">(&lt;/span>preferredPrefix&lt;span style="color:#719e07">,&lt;/span> subPropsConfiguration&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to override field value of config bean: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to override field value of config bean: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> postProcessRefresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/13-config-1.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/13-config2.png" alt="在这里插入图片描述">&lt;/p>
&lt;h2 id="134-配置中心配置大全">13.4 配置中心配置大全&lt;/h2>
&lt;p>ConfigCenterConfig类型
下面配置信息来自官网
dubbo:config-center 配置&lt;/p>
&lt;p>配置中心。对应的配置类：&lt;code>org.apache.dubbo.config.ConfigCenterConfig&lt;/code>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性&lt;/th>
&lt;th>对应URL参数&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>是否必填&lt;/th>
&lt;th>缺省值&lt;/th>
&lt;th>描述&lt;/th>
&lt;th>兼容性&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>protocol&lt;/td>
&lt;td>config.protocol&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>zookeeper&lt;/td>
&lt;td>使用哪个配置中心：apollo、zookeeper、nacos等。 以zookeeper为例 1. 指定protocol，则address可以简化为&lt;code>127.0.0.1:2181&lt;/code>； 2. 不指定protocol，则address取值为&lt;code>zookeeper://127.0.0.1:2181&lt;/code>&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>address&lt;/td>
&lt;td>config.address&lt;/td>
&lt;td>string&lt;/td>
&lt;td>必填&lt;/td>
&lt;td>&lt;/td>
&lt;td>配置中心地址。 取值参见protocol说明&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>highest-priority&lt;/td>
&lt;td>config.highestPriority&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>来自配置中心的配置项具有最高优先级，即会覆盖本地配置项。&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>namespace&lt;/td>
&lt;td>config.namespace&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo&lt;/td>
&lt;td>通常用于多租户隔离，实际含义视具体配置中心而不同。 如： zookeeper - 环境隔离，默认值&lt;code>dubbo&lt;/code>； apollo - 区分不同领域的配置集合，默认使用&lt;code>dubbo&lt;/code>和&lt;code>application&lt;/code>&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cluster&lt;/td>
&lt;td>config.cluster&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>含义视所选定的配置中心而不同。 如Apollo中用来区分不同的配置集群&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>group&lt;/td>
&lt;td>config.group&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo&lt;/td>
&lt;td>含义视所选定的配置中心而不同。 nacos - 隔离不同配置集 zookeeper - 隔离不同配置集&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>check&lt;/td>
&lt;td>config.check&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>当配置中心连接失败时，是否终止应用启动。&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>config-file&lt;/td>
&lt;td>config.configFile&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo.properties&lt;/td>
&lt;td>全局级配置文件所映射到的key zookeeper - 默认路径/dubbo/config/dubbo/dubbo.properties apollo - dubbo namespace中的dubbo.properties键&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>config.timeout&lt;/td>
&lt;td>integer&lt;/td>
&lt;td>&lt;/td>
&lt;td>3000ms&lt;/td>
&lt;td>获取配置的超时时间&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>username&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>如果配置中心需要做校验，用户名 Apollo暂未启用&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>password&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>如果配置中心需要做校验，密码 Apollo暂未启用&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>parameters&lt;/td>
&lt;td>&lt;/td>
&lt;td>Map&amp;lt;string, string&amp;gt;&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>扩展参数，用来支持不同配置中心的定制化配置参数&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>include-spring-env&lt;/td>
&lt;td>&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>使用Spring框架时支持，为true时，会自动从Spring Environment中读取配置。 默认依次读取 key为dubbo.properties的配置 key为dubbo.properties的PropertySource&lt;/td>
&lt;td>2.7.0+&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>原文：&lt;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的三大中心之配置中心&lt;/a>&lt;/p></description></item><item><title>Blog: 12 全局视野来看Dubbo3的服务启动生命周期</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="12-全局视野来看dubbo3的服务启动生命周期">12 全局视野来看Dubbo3的服务启动生命周期&lt;/h1>
&lt;h2 id="121-启动方法简介">12.1 启动方法简介&lt;/h2>
&lt;p>在说启动方法之前先把视野拉回第一章&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">《1-从一个服务提供者的Demo说起》&lt;/a>我们的Demo代码,下面只贴一下核心代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">Application&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startWithBootstrap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startWithBootstrap&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> DemoServiceImpl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这一个篇章主要说这里:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化应用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化注册中心配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化协议配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化服务配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>service&lt;span style="color:#719e07">(&lt;/span>service&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//启动
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面我们介绍了Dubbo启动器DubboBootstrap类型对象的创建,又介绍了为DubboBootstrap启动器初始化各种配置信息,这一个博客就开始到了分析启动方法的位置了,Dubbo启动器借助Deployer发布器来启动和发布服务,发布器的启动过程包含了启动配置中心,加载配置,启动元数据中心,启动服务等操作都是比较重要又比较复杂的过程,这里我们先来看下启动过程的生命周期来为后面的内容做好铺垫。&lt;/p>
&lt;h2 id="122-启动器启动方法的调用逻辑start">12.2 启动器启动方法的调用逻辑start()&lt;/h2>
&lt;p>这里我们就直接来看DubboBootstrap的start()方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">start&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用重载的方法进行启动参数代表是否等待启动结束
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们再来看重载的start方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">start&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">boolean&lt;/span> wait&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个发布器是在ApplicationModel对象创建之后初始化的时候进行初始化的具体类型为DefaultApplicationDeployer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Future future &lt;span style="color:#719e07">=&lt;/span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>wait&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//等待异步启动的结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> future&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//启动失败则抛出一个异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;await dubbo application start finish failure&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="123-应用程序发布器defaultapplicationdeployer的启动方法">12.3 应用程序发布器DefaultApplicationDeployer的启动方法&lt;/h2>
&lt;p>发布器是帮助我们发布服务和引用服务的,在Dubbo3中不论是服务提供者还是服务消费者如果想要启动服务都需要走这个启动方法的逻辑,所以务必重视&lt;/p>
&lt;p>我们直接来看DefaultApplicationDeployer的start()代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Future &lt;span style="color:#268bd2">start&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//启动锁，防止重复启动
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>startLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//发布器,状态已经设置为停止或者失败了就直接抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isStopping&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">||&lt;/span> isStopped&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">||&lt;/span> isFailed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; is stopping or stopped, can not start again&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// maybe call start again after add new module, check if any new module
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//可能在添加新模块后再次调用start，检查是否有任何新模块
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这里遍历当前应用程序下的所有模块如果某个模块是PENDING状态则这里hasPendingModule的值为true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> hasPendingModule &lt;span style="color:#719e07">=&lt;/span> hasPendingModule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//发布器状态正在启动中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isStarting&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// currently, is starting, maybe both start by module and application
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// if it has new modules, start them
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//存在挂器的模块
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>hasPendingModule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//启动模块
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> startModules&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if it is starting, reuse previous startFuture
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//模块异步启动中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> startFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if is started and no new module, just return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果已启动且没有新模块，直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isStarted&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>hasPendingModule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>completedFuture&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// pending -&amp;gt; starting : first start app
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// started -&amp;gt; starting : re-start app
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//启动状态切换，将启动状态切换到STARTING（pending和started状态无需切换）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> onStarting&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//核心初始化逻辑，这里主要做一些应用级别启动比如配置中心，元数据中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//启动模块（我们的服务提供和服务引用是在这个模块级别的）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> doStart&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onFailed&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; start failure&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> e&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> startFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个启动方法逻辑不多 主要三个方法我们重点来看：&lt;/p>
&lt;ul>
&lt;li>onStarting() 这个是启动之前的状态切换&lt;/li>
&lt;li>initialize() 应用的初始化逻辑 比如配置中心，元数据中心的初始化&lt;/li>
&lt;li>doStart() 启动模块比如启动我们的服务提供和服务引用的）&lt;/li>
&lt;/ul>
&lt;p>继续看后面的细节吧，代码胜千言。&lt;/p>
&lt;h2 id="124-应用程序发布器对应用级别的初始化逻辑">12.4 应用程序发布器对应用级别的初始化逻辑&lt;/h2>
&lt;p>这个我们先来看DefaultApplicationDeployer的初始化方法initialize()：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//状态判断 如果已经初始化过了就直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//启动锁，确保在并发调用时完成初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>startLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//双重校验锁 如果已经初始化过了就直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register shutdown hook
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//注册关闭钩子，这个逻辑基本每个中间件应用都必须要要做的事情了，正常关闭应用回收资源，一般没这个逻辑情况下容易出现一些异常，让我们开发人员很疑惑，而这个逻辑往往并不好处理的干净。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registerShutdownHook&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//启动配置中心，感觉Dubbo3耦合了这个玩意
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> startConfigCenter&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载配置，一般配置信息当前机器的来源：环境变量，JVM启动参数，配置文字
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadApplicationConfigs&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化模块发布器 （发布服务提供和服务引用使用）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initModuleDeployers&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// @since 2.7.8
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//启动元数据中心
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> startMetadataCenter&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化完成
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initialized &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; has been initialized!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个是个生命周期整体概览的方法，将具体逻辑拆分到各个子方法中，是代码重构的一种策略，上面注释也很清楚了就不细说了，上面每个方法在后面会有单独的博客来分析。&lt;/p>
&lt;h2 id="125-应用下模块的启动服务的发布与引用">12.5 应用下模块的启动（服务的发布与引用）&lt;/h2>
&lt;p>我们回过头来详细看DefaultApplicationDeployer的doStart()代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doStart&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 启动模块
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> startModules&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DefaultApplicationDeployer的 startModules()方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startModules&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ensure init and start internal module first
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//确保初始化并首先启动内部模块,Dubbo3中将模块分为内部和外部，内部是核心代码已经提供的一些服务比如元数据服务，外部是我们自己写的服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> prepareInternalModule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// filter and start pending modules, ignore new module during starting, throw exception of module start
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//启动所有的模块 （启动所有的服务）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ModuleModel moduleModel &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>getModuleModels&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个状态默认就是PENDING的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">.&lt;/span>getDeployer&lt;span style="color:#719e07">().&lt;/span>isPending&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//模块启动器，发布服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getDeployer&lt;span style="color:#719e07">().&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个模块的启动其实就是用来启动服务的 先启动内部服务，再启动外部服务
内部服务有个元数据服务Dubbo3中每个服务都可以对外提供服务的元数据信息，来简化服务配置，不论是内部服务还是外部服务调用的代码逻辑都是模块发布器ModuleDeployer的start()方法，接下来我们详细看下模块发布器的生命周期函数。&lt;/p>
&lt;h2 id="126-模块发布器发布服务的过程">12.6 模块发布器发布服务的过程&lt;/h2>
&lt;p>前面我们说到了所有的服务都是经过模块发布器，ModuleDeployer的start()方法来启动的，那我们接下来就来看看这个模块发布器的启动方法。&lt;/p>
&lt;p>ModuleDeployer的start()方法代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> Future &lt;span style="color:#268bd2">start&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//模块发布器已经停止或者启动失败则直接抛出异常返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isStopping&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">||&lt;/span> isStopped&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">||&lt;/span> isFailed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; is stopping or stopped, can not start again&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//启动中或者已经启动了则直接返回一个Future对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isStarting&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">||&lt;/span> isStarted&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> startFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//切换模块启动状态为STARTING
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> onModuleStarting&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// initialize
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果应用未初始化则初始化（非正常逻辑）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//模块发布器进行初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// export services
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//暴露服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> exportServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// prepare application instance
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// exclude internal module to avoid wait itself
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>moduleModel &lt;span style="color:#719e07">!=&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">().&lt;/span>getInternalModule&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>prepareInternalModule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// refer services
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//引用服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if no async export/refer services, just set started
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//非异步启动则直接切换状态为STARTED
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>asyncExportingFutures&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> asyncReferringFutures&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onModuleStarted&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果是异步的则等待服务发布和服务引用异步回调
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> frameworkExecutorRepository&lt;span style="color:#719e07">.&lt;/span>getSharedExecutor&lt;span style="color:#719e07">().&lt;/span>submit&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// wait for export finish
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> waitExportFinish&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// wait for refer finish
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> waitReferFinish&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;wait for export/refer services occurred an exception&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//异步回调完成 所有服务都启动了，再切换状态
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> onModuleStarted&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onModuleFailed&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; start failed: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> e&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> startFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>好了整体的服务启动生命周期就如上代码，后续我们再详细来看每个细节。&lt;/p>
&lt;h2 id="127-发布器简介">12.7 发布器简介&lt;/h2>
&lt;p>前面主要说了应用和模块的发布器的启动和初始化，下面简单了解下它们的关系，如下所示
&lt;img src="https://img-blog.csdnimg.cn/37e7c05796ab4b38aa7658377e16c0aa.png" alt="在这里插入图片描述">
可以发布器主要包含&lt;/p>
&lt;ul>
&lt;li>应用的发布器ApplicationDeployer用于初始化并启动应用程序实例&lt;/li>
&lt;li>模块发布器ModuleDeployer 模块（服务）的导出/引用服务&lt;/li>
&lt;/ul>
&lt;p>两种发布器有各自的接口，他们都继承了抽象的发布器AbstractDeployer 封装了一些公共的操作比如状态切换，状态查询的逻辑。&lt;/p>
&lt;p>另外我们再来看下发布过程的状态枚举DeployState如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">enum&lt;/span> DeployState &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Unknown state
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UNKNOWN&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Pending, wait for start
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> PENDING&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Starting
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> STARTING&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Started
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> STARTED&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Stopping
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> STOPPING&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Stopped
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> STOPPED&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Failed
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FAILED
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo这一块后续可以优化以下，这里的状态切换全部耦合在一起了，可以考虑使用状态机将状态与行为解耦。&lt;/p>
&lt;p>原文：&lt;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&lt;/a>&lt;/p></description></item><item><title>Blog: 11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="11-dubbo启动器dubbobootstrap添加协议配置信息protocolconfig">11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig&lt;/h1>
&lt;h2 id="111-简介">11.1 简介&lt;/h2>
&lt;p>先贴个代码用来参考:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>service&lt;span style="color:#719e07">(&lt;/span>service&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上个博客我们说了 RegistryConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码协议配置信息:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="112--协议的配置相关">11.2 协议的配置相关&lt;/h2>
&lt;p>下面的配置来源于官网&lt;/p>
&lt;p>服务提供者协议配置。对应的配置类： org.apache.dubbo.config.ProtocolConfig。同时，如果需要支持多协议，可以声明多个 &lt;a href="dubbo:protocol">dubbo:protocol&lt;/a> 标签，并在 &lt;a href="dubbo:service">dubbo:service&lt;/a> 中通过 protocol 属性指定使用的协议。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性&lt;/th>
&lt;th>对应URL参数&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>是否必填&lt;/th>
&lt;th>缺省值&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>描述&lt;/th>
&lt;th>兼容性&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>id&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo&lt;/td>
&lt;td>配置关联&lt;/td>
&lt;td>协议BeanId，可以在&amp;lt;dubbo:service protocol=&amp;quot;&amp;quot;&amp;gt;中引用此ID，如果ID不填，缺省和name属性值一样，重复则在name后加序号。&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>name&lt;/td>
&lt;td>&lt;protocol>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>&lt;strong>必填&lt;/strong>&lt;/td>
&lt;td>dubbo&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>协议名称&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>port&lt;/td>
&lt;td>&lt;port>&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo协议缺省端口为20880，rmi协议缺省端口为1099，http和hessian协议缺省端口为80；如果&lt;strong>没有&lt;/strong>配置port，则自动采用默认端口，如果配置为**-1**，则会分配一个没有被占用的端口。Dubbo 2.4.0+，分配的端口在协议缺省端口的基础上增长，确保端口段可控。&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务端口&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>host&lt;/td>
&lt;td>&lt;host>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>自动查找本机IP&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>-服务主机名，多网卡选择或指定VIP及域名时使用，为空则自动查找本机IP，-建议不要配置，让Dubbo自动获取本机IP&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>threadpool&lt;/td>
&lt;td>threadpool&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>fixed&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>线程池类型，可选：fixed/cached&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>threads&lt;/td>
&lt;td>threads&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>200&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务线程池大小(固定大小)&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>iothreads&lt;/td>
&lt;td>threads&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>cpu个数+1&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>io线程池大小(固定大小)&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>accepts&lt;/td>
&lt;td>accepts&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>0&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务提供方最大可接受连接数&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>payload&lt;/td>
&lt;td>payload&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>8388608(=8M)&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>请求及响应数据包大小限制，单位：字节&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>codec&lt;/td>
&lt;td>codec&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>协议编码方式&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>serialization&lt;/td>
&lt;td>serialization&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo协议缺省为hessian2，rmi协议缺省为java，http协议缺省为json&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>协议序列化方式，当协议支持多种序列化方式时使用，比如：dubbo协议的dubbo,hessian2,java,compactedjava，以及http协议的json等&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>accesslog&lt;/td>
&lt;td>accesslog&lt;/td>
&lt;td>string/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>设为true，将向logger中输出访问日志，也可填写访问日志文件路径，直接把访问日志输出到指定文件&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>path&lt;/td>
&lt;td>&lt;path>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>提供者上下文路径，为服务path的前缀&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>transporter&lt;/td>
&lt;td>transporter&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo协议缺省为netty&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>协议的服务端和客户端实现类型，比如：dubbo协议的mina,netty等，可以分拆为server和client配置&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>server&lt;/td>
&lt;td>server&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo协议缺省为netty，http协议缺省为servlet&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>协议的服务器端实现类型，比如：dubbo协议的mina,netty等，http协议的jetty,servlet等&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>client&lt;/td>
&lt;td>client&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo协议缺省为netty&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>协议的客户端实现类型，比如：dubbo协议的mina,netty等&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dispatcher&lt;/td>
&lt;td>dispatcher&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo协议缺省为all&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>协议的消息派发方式，用于指定线程模型，比如：dubbo协议的all, direct, message, execution, connection等&lt;/td>
&lt;td>2.1.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>queues&lt;/td>
&lt;td>queues&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>0&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>线程池队列大小，当线程池满时，排队等待执行的队列大小，建议不要设置，当线程池满时应立即失败，重试其它服务提供机器，而不是排队，除非有特殊需求。&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>charset&lt;/td>
&lt;td>charset&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>UTF-8&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>序列化编码&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>buffer&lt;/td>
&lt;td>buffer&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>8192&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>网络读写缓冲区大小&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>heartbeat&lt;/td>
&lt;td>heartbeat&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>0&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>心跳间隔，对于长连接，当物理层断开时，比如拔网线，TCP的FIN消息来不及发送，对方收不到断开事件，此时需要心跳来帮助检查连接是否已断开&lt;/td>
&lt;td>2.0.10以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>telnet&lt;/td>
&lt;td>telnet&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>所支持的telnet命令，多个命令用逗号分隔&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>register&lt;/td>
&lt;td>register&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>该协议的服务是否注册到注册中心&lt;/td>
&lt;td>2.0.8以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>contextpath&lt;/td>
&lt;td>contextpath&lt;/td>
&lt;td>String&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省为空串&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>&lt;/td>
&lt;td>2.0.6以上版本&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>变量&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>threadname&lt;/td>
&lt;td>String&lt;/td>
&lt;td>线程池名称&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>corethreads&lt;/td>
&lt;td>Integer&lt;/td>
&lt;td>线程池核心线程大小&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>alive&lt;/td>
&lt;td>Integer&lt;/td>
&lt;td>线程池keepAliveTime，默认单位时间单位。毫秒&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>exchanger&lt;/td>
&lt;td>String&lt;/td>
&lt;td>交换器配置信息如何交换&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>prompt&lt;/td>
&lt;td>String&lt;/td>
&lt;td>命令行提示符&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>status&lt;/td>
&lt;td>String&lt;/td>
&lt;td>状态检查&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>sslEnabled&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>ssl是否启用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="113-协议配置对象创建与添加">11.3 协议配置对象创建与添加&lt;/h2>
&lt;p>前面例子中调用的代码&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里我们配置了协议类型为dubbo 端口为-1则会分配一个没有被占用的端口&lt;/p>
&lt;p>继续看下DubboBootstrap的protocol方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">protocol&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ProtocolConfig protocolConfig&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置信息转List
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> protocols&lt;span style="color:#719e07">(&lt;/span>singletonList&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续看protocols方法 ,这个代码与前面两个博客中看到的向配置管理器添加配置对象的逻辑是一样的
这里就不说了可以看前面的博客&lt;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》&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">protocols&lt;/span>&lt;span style="color:#719e07">(&lt;/span>List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ProtocolConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> protocolConfigs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>protocolConfigs&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ProtocolConfig protocolConfig &lt;span style="color:#719e07">:&lt;/span> protocolConfigs&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configManager&lt;span style="color:#719e07">.&lt;/span>addProtocol&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文：&lt;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&lt;/a>&lt;/p></description></item><item><title>Blog: 10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="10-dubbo启动器dubbobootstrap添加注册中心配置信息registryconfig">10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig&lt;/h1>
&lt;h2 id="101-简介">10.1 简介&lt;/h2>
&lt;p>先贴个代码用来参考:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>service&lt;span style="color:#719e07">(&lt;/span>service&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上个博客我们说了启动器ApplicationConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码注册中心配置信息:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="102--注册中心的配置相关">10.2 注册中心的配置相关&lt;/h2>
&lt;p>下面的配置来源于官网&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性&lt;/th>
&lt;th>对应URL参数&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>是否必填&lt;/th>
&lt;th>缺省值&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>描述&lt;/th>
&lt;th>兼容性&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>id&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>配置关联&lt;/td>
&lt;td>注册中心引用BeanId，可以在&amp;lt;dubbo:service registry=&amp;quot;&amp;quot;&amp;gt;或&amp;lt;dubbo:reference registry=&amp;quot;&amp;quot;&amp;gt;中引用此ID&lt;/td>
&lt;td>1.0.16以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>address&lt;/td>
&lt;td>&lt;a href="host:port">host:port&lt;/a>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>&lt;strong>必填&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>注册中心服务器地址，如果地址没有端口缺省为9090，同一集群内的多个地址用逗号分隔，如：ip:port,ip:port，不同集群的注册中心，请配置多个&lt;a href="dubbo:registry">dubbo:registry&lt;/a>标签&lt;/td>
&lt;td>1.0.16以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>protocol&lt;/td>
&lt;td>&lt;protocol>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>注册中心地址协议，支持&lt;code>dubbo&lt;/code>, &lt;code>multicast&lt;/code>, &lt;code>zookeeper&lt;/code>, &lt;code>redis&lt;/code>, &lt;code>consul(2.7.1)&lt;/code>, &lt;code>sofa(2.7.2)&lt;/code>, &lt;code>etcd(2.7.2)&lt;/code>, &lt;code>nacos(2.7.2)&lt;/code>等协议&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>port&lt;/td>
&lt;td>&lt;port>&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>9090&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>注册中心缺省端口，当address没有带端口时使用此端口做为缺省值&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>username&lt;/td>
&lt;td>&lt;username>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>登录注册中心用户名，如果注册中心不需要验证可不填&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>password&lt;/td>
&lt;td>&lt;password>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>登录注册中心密码，如果注册中心不需要验证可不填&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>transport&lt;/td>
&lt;td>registry.transporter&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>netty&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>网络传输方式，可选mina,netty&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>registry.timeout&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>5000&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>注册中心请求超时时间(毫秒)&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>session&lt;/td>
&lt;td>registry.session&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>60000&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>注册中心会话超时时间(毫秒)，用于检测提供者非正常断线后的脏数据，比如用心跳检测的实现，此时间就是心跳间隔，不同注册中心实现不一样。&lt;/td>
&lt;td>2.1.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>file&lt;/td>
&lt;td>registry.file&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>使用文件缓存注册中心地址列表及服务提供者列表，应用重启时将基于此文件恢复，注意：两个注册中心不能使用同一文件存储&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>wait&lt;/td>
&lt;td>registry.wait&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>0&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>停止时等待通知完成时间(毫秒)&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>check&lt;/td>
&lt;td>check&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>注册中心不存在时，是否报错&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>register&lt;/td>
&lt;td>register&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>是否向此注册中心注册服务，如果设为false，将只订阅，不注册&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>subscribe&lt;/td>
&lt;td>subscribe&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>是否向此注册中心订阅服务，如果设为false，将只注册，不订阅&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dynamic&lt;/td>
&lt;td>dynamic&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务是否动态注册，如果设为false，注册后将显示为disable状态，需人工启用，并且服务提供者停止时，也不会自动取消注册，需人工禁用。&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>group&lt;/td>
&lt;td>group&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务注册分组，跨组的服务不会相互影响，也无法相互调用，适用于环境隔离。&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>simplified&lt;/td>
&lt;td>simplified&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>注册到注册中心的URL是否采用精简模式的（与低版本兼容）&lt;/td>
&lt;td>2.7.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>extra-keys&lt;/td>
&lt;td>extraKeys&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>在simplified=true时，extraKeys允许你在默认参数外将额外的key放到URL中，格式：“interface,key1,key2”。&lt;/td>
&lt;td>2.7.0以上版本&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>变量&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>server&lt;/td>
&lt;td>String&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>client&lt;/td>
&lt;td>String&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cluster&lt;/td>
&lt;td>String&lt;/td>
&lt;td>影响流量在注册中心之间的分布，在订阅多个注册中心时很有用，可用选项：1。区域感知，特定类型的流量总是根据流量的来源进入一个注册表。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>zone&lt;/td>
&lt;td>String&lt;/td>
&lt;td>注册表所属的区域，通常用于隔离流量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>parameters&lt;/td>
&lt;td>Map&amp;lt;String, String&amp;gt;&lt;/td>
&lt;td>自定义参数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>useAsConfigCenter&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>该地址是否用作配置中心&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>useAsMetadataCenter&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>该地址是否用作远程元数据中心&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>accepts&lt;/td>
&lt;td>String&lt;/td>
&lt;td>此注册表接受的rpc协议列表，例如“dubbo，rest”&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>preferred&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>如果设置为true，则始终首先使用此注册表，这在订阅多个注册表时非常有用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>weight&lt;/td>
&lt;td>Integer&lt;/td>
&lt;td>影响注册中心之间的流量分布，当订阅多个注册中心仅在未指定首选注册中心时才生效时，此功能非常有用。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>registerMode&lt;/td>
&lt;td>String&lt;/td>
&lt;td>注册模式:实例级,接口级,所有&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>enableEmptyProtection&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>收到的空url地址列表和空保护被禁用，将清除当前可用地址&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="103-注册中心配置对象创建与添加">10.3 注册中心配置对象创建与添加&lt;/h2>
&lt;p>前面例子中调用的代码&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>首先我们要来看的是RegistryConfig类型的构造器&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">RegistryConfig&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String address&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setAddress&lt;span style="color:#719e07">(&lt;/span>address&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续看setAddress方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setAddress&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String address&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//保存地址
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>address &lt;span style="color:#719e07">=&lt;/span> address&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//下面是支持将参数在url地址后面 比如用户名,密码,协议,端口,这几个参数提前做解析放入成员变量中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>address &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//地址转Dubbo的URL对象 这个URL是Dubbo自行实现的URL封装信息的类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> URL url &lt;span style="color:#719e07">=&lt;/span> URL&lt;span style="color:#719e07">.&lt;/span>valueOf&lt;span style="color:#719e07">(&lt;/span>address&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Refactor since 2.7.8
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//值不存在时候更新属性,非常巧妙的代码 重构了多个if判断
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//第一个参数值不存在则调用第二个方法,第二个方法的参数为第三方方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> updatePropertyIfAbsent&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>getUsername&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>setUsername&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getUsername&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> updatePropertyIfAbsent&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>getPassword&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>setPassword&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getPassword&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> updatePropertyIfAbsent&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>getProtocol&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>setProtocol&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> updatePropertyIfAbsent&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>getPort&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>setPort&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getPort&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//移除掉url中的backup自定义参数 (备份的注册中心地址)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> params &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmptyMap&lt;span style="color:#719e07">(&lt;/span>params&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> params&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>BACKUP_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将自定义参数存储到成员变量中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> updateParameters&lt;span style="color:#719e07">(&lt;/span>params&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception ignored&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后再回过头来看DubboBootstrap的registry方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">registry&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RegistryConfig registryConfig&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将applicationModel对象设置给注册中心配置对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将注册中心配置对象添加到配置管理器中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>addRegistry&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>直接来看配置管理器configManager的添加注册中心配置addRegistry方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addRegistry&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RegistryConfig registryConfig&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> addConfig&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>configManager 的addConfig方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T &lt;span style="color:#268bd2">extends&lt;/span> AbstractConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> T &lt;span style="color:#268bd2">addConfig&lt;/span>&lt;span style="color:#719e07">(&lt;/span>AbstractConfig config&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>config &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore MethodConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//检查当前配置管理器支持管理的配置对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isSupportConfigType&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Unsupported config type: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存中是否存在
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> AbstractConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> configsMap &lt;span style="color:#719e07">=&lt;/span> configsCache&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>getTagName&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">()),&lt;/span> type &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//不是服务级接口配置则直接从缓存中读取到配置之后直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// fast check duplicated equivalent config before write lock
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!(&lt;/span>config &lt;span style="color:#719e07">instanceof&lt;/span> ReferenceConfigBase &lt;span style="color:#719e07">||&lt;/span> config &lt;span style="color:#719e07">instanceof&lt;/span> ServiceConfigBase&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>AbstractConfig value &lt;span style="color:#719e07">:&lt;/span> configsMap&lt;span style="color:#719e07">.&lt;/span>values&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> value&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// lock by config type
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//添加配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>configsMap&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> addIfAbsent&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">,&lt;/span> configsMap&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ConfigManager配置管理器的addIfAbsent方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>C &lt;span style="color:#268bd2">extends&lt;/span> AbstractConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> C &lt;span style="color:#268bd2">addIfAbsent&lt;/span>&lt;span style="color:#719e07">(&lt;/span>C config&lt;span style="color:#719e07">,&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> C&lt;span style="color:#719e07">&amp;gt;&lt;/span> configsMap&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置信息为空直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>config &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> configsMap &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> config&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// find by value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//根据配置规则判断,配置存在则返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Optional&lt;span style="color:#719e07">&amp;lt;&lt;/span>C&lt;span style="color:#719e07">&amp;gt;&lt;/span> prevConfig &lt;span style="color:#719e07">=&lt;/span> findDuplicatedConfig&lt;span style="color:#719e07">(&lt;/span>configsMap&lt;span style="color:#719e07">,&lt;/span> config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>prevConfig&lt;span style="color:#719e07">.&lt;/span>isPresent&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> prevConfig&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//生成配置key
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>key &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">do&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// generate key if id is not set
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> key &lt;span style="color:#719e07">=&lt;/span> generateConfigId&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">(&lt;/span>configsMap&lt;span style="color:#719e07">.&lt;/span>containsKey&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//不相同的配置key重复则抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> C existedConfig &lt;span style="color:#719e07">=&lt;/span> configsMap&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>existedConfig &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>isEquals&lt;span style="color:#719e07">(&lt;/span>existedConfig&lt;span style="color:#719e07">,&lt;/span> config&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String type &lt;span style="color:#719e07">=&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">().&lt;/span>getSimpleName&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Duplicate %s found, there already has one default %s or more than two %ss have the same id, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;you can try to give each %s a different id, override previous config with later config. id: %s, prev: %s, later: %s&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> key&lt;span style="color:#719e07">,&lt;/span> existedConfig&lt;span style="color:#719e07">,&lt;/span> config&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// override existed config if any
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//将配置对象存入configsMap对象中,configsMap来源于configsCache
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configsMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> config&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文： &lt;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//">&amp;laquo;Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig&amp;raquo;&lt;/a>&lt;/p></description></item><item><title>Blog: 9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="9-dubbo启动器dubbobootstrap添加应用程序的配置信息applicationconfig">9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&lt;/h1>
&lt;h2 id="91-简介">9.1 简介&lt;/h2>
&lt;p>先贴个代码用来参考:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>service&lt;span style="color:#719e07">(&lt;/span>service&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上个博客我们说了启动器对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="92-应用程序applicationconfig的配置信息">9.2 应用程序ApplicationConfig的配置信息&lt;/h2>
&lt;p>ApplicationConfig的构造器比较简单就是为他的成员变量name赋值来标识这个应用程序的名字
下面我们直接参考下官网的配置表格:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性&lt;/th>
&lt;th>对应URL参数&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>是否必填&lt;/th>
&lt;th>缺省值&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>描述&lt;/th>
&lt;th>兼容性&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>name&lt;/td>
&lt;td>application&lt;/td>
&lt;td>string&lt;/td>
&lt;td>&lt;strong>必填&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>当前应用名称，用于注册中心计算应用间依赖关系，注意：消费者和提供者应用名不要一样，此参数不是匹配条件，你当前项目叫什么名字就填什么，和提供者消费者角色无关，比如：kylin应用调用了morgan应用的服务，则kylin项目配成kylin，morgan项目配成morgan，可能kylin也提供其它服务给别人使用，但kylin项目永远配成kylin，这样注册中心将显示kylin依赖于morgan&lt;/td>
&lt;td>1.0.16以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>version&lt;/td>
&lt;td>application.version&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>当前应用的版本&lt;/td>
&lt;td>2.2.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>owner&lt;/td>
&lt;td>owner&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>应用负责人，用于服务治理，请填写负责人公司邮箱前缀&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>organization&lt;/td>
&lt;td>organization&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>组织名称(BU或部门)，用于注册中心区分服务来源，此配置项建议不要使用autoconfig，直接写死在配置中，比如china,intl,itu,crm,asc,dw,aliexpress等&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>architecture&lt;/td>
&lt;td>architecture&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>用于服务分层对应的架构。如，intl、china。不同的架构使用不同的分层。&lt;/td>
&lt;td>2.0.7以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>environment&lt;/td>
&lt;td>environment&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>应用环境，如：develop/test/product，不同环境使用不同的缺省值，以及作为只用于开发测试功能的限制条件&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>compiler&lt;/td>
&lt;td>compiler&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>javassist&lt;/td>
&lt;td>性能优化&lt;/td>
&lt;td>Java字节码编译器，用于动态类的生成，可选：jdk或javassist&lt;/td>
&lt;td>2.1.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>logger&lt;/td>
&lt;td>logger&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>slf4j&lt;/td>
&lt;td>性能优化&lt;/td>
&lt;td>日志输出方式，可选：slf4j,jcl,log4j,log4j2,jdk&lt;/td>
&lt;td>2.2.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>metadata-type&lt;/td>
&lt;td>metadata-type&lt;/td>
&lt;td>String&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>local&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>metadata 传递方式，是以 Provider 视角而言的，Consumer 侧配置无效，可选值有： remote - Provider 把 metadata 放到远端注册中心，Consumer 从注册中心获取 local - Provider 把 metadata 放在本地，Consumer 从 Provider 处直接获取&lt;/td>
&lt;td>2.7.6以上版本&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>官网的配置很详细了上面有一些属性是值得注意的比如这个name,compiler,logger,metadata-type 我们可能要多看下默认值是什么,方便我们在使用过程中遇到问题的排查&lt;/p>
&lt;p>常用的属性参考官网的表格已经足够了,不过上面的属性不是列举了所有的属性,后续应该官方文档回更新:
我这里把缺失的一些属性列举出来:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>变量&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>registries&lt;/td>
&lt;td>List&lt;RegistryConfig>&lt;/td>
&lt;td>应用级注册中心列表&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>registryIds&lt;/td>
&lt;td>String&lt;/td>
&lt;td>注册中心id列表&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>monitor&lt;/td>
&lt;td>MonitorConfig&lt;/td>
&lt;td>应用级监控配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dumpDirectory&lt;/td>
&lt;td>String&lt;/td>
&lt;td>保存线程转储的目录&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qosEnable&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>是否启用qos&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qosHost&lt;/td>
&lt;td>String&lt;/td>
&lt;td>要侦听的qos主机地址&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qosPort&lt;/td>
&lt;td>Integer&lt;/td>
&lt;td>要侦听的qos端口&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qosAcceptForeignIp&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>qos是否接收外部IP&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>parameters&lt;/td>
&lt;td>Map&amp;lt;String, String&amp;gt;&lt;/td>
&lt;td>自定义参数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>shutwait&lt;/td>
&lt;td>String&lt;/td>
&lt;td>应用程序关闭时间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>hostname&lt;/td>
&lt;td>String&lt;/td>
&lt;td>主机名&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>registerConsumer&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>用于控制是否将实例注册到注册表。仅当实例是纯消费者时才设置为“false”。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>repository&lt;/td>
&lt;td>String&lt;/td>
&lt;td>没找到哪里用了&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>enableFileCache&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>是否开启本地文件缓存&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>protocol&lt;/td>
&lt;td>String&lt;/td>
&lt;td>此应用程序的首选协议（名称）适用于难以确定哪个是首选协议的地方&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>metadataServiceProtocol&lt;/td>
&lt;td>String&lt;/td>
&lt;td>用于点对点的元数据传输的协议&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>metadataServicePort&lt;/td>
&lt;td>Integer&lt;/td>
&lt;td>元数据服务端口号，用于服务发现&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>livenessProbe&lt;/td>
&lt;td>String&lt;/td>
&lt;td>Liveness 存活探针 用于设置qos中探测器的扩展&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>readinessProbe&lt;/td>
&lt;td>String&lt;/td>
&lt;td>Readiness 就绪探针&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>startupProbe&lt;/td>
&lt;td>String&lt;/td>
&lt;td>Startup 启动探针&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>registerMode&lt;/td>
&lt;td>String&lt;/td>
&lt;td>注册模式,实例级,接口集,所有&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>enableEmptyProtection&lt;/td>
&lt;td>Boolean&lt;/td>
&lt;td>接收到的空url地址列表和空保护被禁用，将清除当前可用地址&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这里我们先来简单了解下这个实体类型的基本配置,直接看配置可能不太好理解,后面我们讲到每个配置的时候可以回来参考一下&lt;/p>
&lt;h2 id="应用程序配置对象添加到启动器中的配置管理器中">应用程序配置对象添加到启动器中的配置管理器中&lt;/h2>
&lt;p>了解了配置信息再回过头来看下这个配置信息如何存放到启动器里面的:&lt;/p>
&lt;p>我们的Demo调用代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DubboBootstrap的application方法设置一个应用程序配置ApplicationConfig对象&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">application&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationConfig applicationConfig&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将启动器构造器中初始化的默认应用程序模型对象传递给配置对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将配置信息添加到配置管理器中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>applicationConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ConfigManager配置管理器的setApplication方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@DisableInject&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setApplication&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationConfig application&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> addConfig&lt;span style="color:#719e07">(&lt;/span>application&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ConfigManager配置管理器的addConfig方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T &lt;span style="color:#268bd2">extends&lt;/span> AbstractConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> T &lt;span style="color:#268bd2">addConfig&lt;/span>&lt;span style="color:#719e07">(&lt;/span>AbstractConfig config&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>config &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore MethodConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//检查当前配置管理器支持管理的配置对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isSupportConfigType&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Unsupported config type: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存中是否存在
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> AbstractConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> configsMap &lt;span style="color:#719e07">=&lt;/span> configsCache&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>getTagName&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">()),&lt;/span> type &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// fast check duplicated equivalent config before write lock
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//不是服务级配置则直接从缓存中读取到配置之后直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!(&lt;/span>config &lt;span style="color:#719e07">instanceof&lt;/span> ReferenceConfigBase &lt;span style="color:#719e07">||&lt;/span> config &lt;span style="color:#719e07">instanceof&lt;/span> ServiceConfigBase&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>AbstractConfig value &lt;span style="color:#719e07">:&lt;/span> configsMap&lt;span style="color:#719e07">.&lt;/span>values&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> value&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// lock by config type
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//添加配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>configsMap&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> addIfAbsent&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">,&lt;/span> configsMap&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ConfigManager配置管理器的addIfAbsent方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>C &lt;span style="color:#268bd2">extends&lt;/span> AbstractConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> C &lt;span style="color:#268bd2">addIfAbsent&lt;/span>&lt;span style="color:#719e07">(&lt;/span>C config&lt;span style="color:#719e07">,&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> C&lt;span style="color:#719e07">&amp;gt;&lt;/span> configsMap&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置信息为空直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>config &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> configsMap &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> config&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// find by value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//根据配置规则判断,配置存在则返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Optional&lt;span style="color:#719e07">&amp;lt;&lt;/span>C&lt;span style="color:#719e07">&amp;gt;&lt;/span> prevConfig &lt;span style="color:#719e07">=&lt;/span> findDuplicatedConfig&lt;span style="color:#719e07">(&lt;/span>configsMap&lt;span style="color:#719e07">,&lt;/span> config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>prevConfig&lt;span style="color:#719e07">.&lt;/span>isPresent&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> prevConfig&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//生成配置key
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>key &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">do&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// generate key if id is not set
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> key &lt;span style="color:#719e07">=&lt;/span> generateConfigId&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">(&lt;/span>configsMap&lt;span style="color:#719e07">.&lt;/span>containsKey&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//不相同的配置key重复则抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> C existedConfig &lt;span style="color:#719e07">=&lt;/span> configsMap&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>existedConfig &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>isEquals&lt;span style="color:#719e07">(&lt;/span>existedConfig&lt;span style="color:#719e07">,&lt;/span> config&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String type &lt;span style="color:#719e07">=&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">().&lt;/span>getSimpleName&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Duplicate %s found, there already has one default %s or more than two %ss have the same id, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;you can try to give each %s a different id, override previous config with later config. id: %s, prev: %s, later: %s&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> key&lt;span style="color:#719e07">,&lt;/span> existedConfig&lt;span style="color:#719e07">,&lt;/span> config&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// override existed config if any
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//将配置对象存入configsMap对象中,configsMap来源于configsCache
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configsMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> config&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文： &lt;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/">&amp;laquo;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&amp;raquo;&lt;/a>&lt;/p></description></item><item><title>Blog: 8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="8-dubbo启动器dubbobootstrap借助双重校验锁的单例模式进行对象的初始化">8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化&lt;/h1>
&lt;h2 id="81-启动器简介">8.1 启动器简介&lt;/h2>
&lt;p>在说启动器之前先把视野拉回第一章&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">《1-从一个服务提供者的Demo说起》&lt;/a>我们的Demo代码,下面只贴一下核心代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">Application&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startWithBootstrap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startWithBootstrap&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> DemoServiceImpl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这一个篇章主要说这里:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>service&lt;span style="color:#719e07">(&lt;/span>service&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo3 往云原生的方向走自然要针对云原生应用的应用启动,应用运行,应用发布等信息做一些建模,这个DubboBootstrap就是用来启动Dubbo服务的.类似于Netty的Bootstrap类型和ServerBootstrap启动器&lt;/p>
&lt;h2 id="82-双重校验锁的单例模式创建启动器对象的">8.2 双重校验锁的单例模式创建启动器对象的&lt;/h2>
&lt;p>Dubbo的bootstrap类为啥要用单例模式:&lt;/p>
&lt;p>通过调用静态方法getInstance()获取单例实例。之所以设计为单例，是因为Dubbo中的一些类（如ExtensionLoader）只为每个进程设计一个实例。&lt;/p>
&lt;p>下面就来直接看代码吧,代码胜千言:
对象的调用代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DubboBootstrap获取对象的getInstance()方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">getInstance&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//双重校验锁第一次判断空
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为空都进行排队
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//双重校验锁第二次判断空 上面为空的都排队了这里得判断下
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用重载方法获取对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">(&lt;/span>ApplicationModel&lt;span style="color:#719e07">.&lt;/span>defaultModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DubboBootstrap获取对象重载的getInstance(ApplicationModel applicationModel)方法:&lt;/p>
&lt;p>&lt;em>computeIfAbsent() 方法对 hashMap 中指定 key 的值进行重新计算，如果不存在这个 key，则添加到 hashMap 中。&lt;/em>&lt;/p>
&lt;p>instanceMap设计为Map&amp;lt;ApplicationModel, DubboBootstrap&amp;gt;类型 Key,意味着可以为多个应用程序模型创建不同的启动器,启动多个服务&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">getInstance&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instanceMap&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> _k &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DubboBootstrap&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="83-dubbobootstrap的构造器代码">8.3 DubboBootstrap的构造器代码&lt;/h2>
&lt;p>构造器代码是逻辑比较复杂的地方,我们先来看下代码&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">DubboBootstrap&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//存储应用程序启动模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取配置管理器ConfigManager: 配置管理器的扩展类型ApplicationExt ,扩展名字config
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取环境信息Environment: 环境信息的扩展类型为ApplicationExt,扩展名字为environment
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> environment &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getModelEnvironment&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//执行器存储仓库(线程池)ExecutorRepository: 扩展类型为ExecutorRepository,默认扩展扩展名字为default
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> executorRepository &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ExecutorRepository&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getDefaultExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化并启动应用程序实例ApplicationDeployer,DefaultApplicationDeployer类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationDeployer &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getDeployer&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// listen deploy events
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//为发布器 设置生命周期回调
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>addDeployListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> DeployListenerAdapter&lt;span style="color:#719e07">&amp;lt;&lt;/span>ApplicationModel&lt;span style="color:#719e07">&amp;gt;()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onStarted&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> notifyStarted&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onStopped&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> notifyStopped&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onFailure&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel scopeModel&lt;span style="color:#719e07">,&lt;/span> Throwable cause&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> notifyStopped&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将启动器对象注册到应用程序模型applicationModel的Bean工厂中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// register DubboBootstrap bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>registerBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文： &lt;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/">&amp;laquo;Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化&amp;raquo;&lt;/a>&lt;/p></description></item><item><title>Blog: 7-Dubbo的SPI扩展机制之自动激活扩展Activate源码解析</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="7-dubbo的spi扩展机制之自动激活扩展activate源码解析">7-Dubbo的SPI扩展机制之自动激活扩展Activate源码解析&lt;/h1>
&lt;h2 id="71-activate扩展的说明">7.1 Activate扩展的说明&lt;/h2>
&lt;p>此注解对于使用给定条件自动激活某些扩展非常有用，例如：@Activate可用于在有多个实现时加载某些筛选器扩展。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>group()&lt;/strong> 指定组条件。框架SPI定义了有效的组值。&lt;/li>
&lt;li>&lt;strong>value()&lt;/strong> 指定URL条件中的参数键。&lt;/li>
&lt;/ul>
&lt;p>SPI提供程序可以调用ExtensionLoader。getActivateExtension(URL、String、String)方法以查找具有给定条件的所有已激活扩展。&lt;/p>
&lt;p>比如后面我们会说到的&lt;strong>过滤器扩展对象&lt;/strong>的获取,如下通过调用&lt;strong>getActivateExtension方法的&lt;/strong>代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Filter&lt;span style="color:#719e07">&amp;gt;&lt;/span> filters&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filters &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Filter&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> moduleModels&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)).&lt;/span>getActivateExtension&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> key&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="72-获取自动激活扩展的源码">7.2 获取自动激活扩展的源码&lt;/h2>
&lt;p>前面我们看了激活扩展是通过调用getActivateExtension方法来获取对象的,那接下来就来看下这个方法做了什么操作:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">* @param url 服务的url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">* @param key 用于获取扩展点名称的url参数键 比如监听器:exporter.listener,过滤器:params-filter,telnet处理器:telnet
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">*/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getActivateExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> String key&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getActivateExtension&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> key&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续调用重载的方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param url 服务的url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param key 用于获取扩展点名称的url参数键 比如监听器:exporter.listener,过滤器:params-filter,telnet处理器:telnet
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param group group 用于筛选的分组,比如过滤器中使用此参数来区分消费者使用这个过滤器还是提供者使用这个过滤器他们的group参数分表为consumer,provider
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return 已激活的扩展列表。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getActivateExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> String key&lt;span style="color:#719e07">,&lt;/span> String group&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从参数中获取url指定的值
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String value &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用下个重载的方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> getActivateExtension&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">:&lt;/span> COMMA_SPLIT_PATTERN&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">),&lt;/span> group&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面的重载方法都是用来转换参数的,下面这个方法才是真正的逻辑&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 获取激活扩展.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param url 服务的url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param values 这个value是扩展点的名字 当指定了时候会使用指定的名字的扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param group group 用于筛选的分组,比如过滤器中使用此参数来区分消费者使用这个过滤器还是提供者使用这个过滤器他们的group参数分表为consumer,provider
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return 获取激活扩展.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getActivateExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> values&lt;span style="color:#719e07">,&lt;/span> String group&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查扩展加载器是否被销毁了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// solve the bug of using @SPI&amp;#39;s wrapper method to report a null pointer exception.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//创建个有序的Map集合,用来对扩展进行排序
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> activateExtensionsMap &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> TreeMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>activateComparator&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化扩展名字,指定了扩展名字values不为空
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> names &lt;span style="color:#719e07">=&lt;/span> values &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">:&lt;/span> asList&lt;span style="color:#719e07">(&lt;/span>values&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展名字使用Set集合进行去重
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> namesSet &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashSet&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>names&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//参数常量是 -default 扩展名字是否不包含默认的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>namesSet&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>REMOVE_VALUE_PREFIX &lt;span style="color:#719e07">+&lt;/span> DEFAULT_KEY&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//第一次进来肯定是没有缓存对象双重校验锁检查下
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedActivateGroups&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedActivateGroups&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// cache all extensions
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedActivateGroups&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载扩展类型对应的扩展类,这个具体细节参考源码或者《[Dubbo3.0.7源码解析系列]-5-Dubbo的SPI扩展机制与自适应扩展对象的创建与扩展文件的扫描源码解析》章节
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> getExtensionClasses&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> cachedActivates&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String name &lt;span style="color:#719e07">=&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object activate &lt;span style="color:#719e07">=&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> activateGroup&lt;span style="color:#719e07">,&lt;/span> activateValue&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历所有的activates列表获取group()和value()值
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>activate &lt;span style="color:#719e07">instanceof&lt;/span> Activate&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> activateGroup &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">((&lt;/span>Activate&lt;span style="color:#719e07">)&lt;/span> activate&lt;span style="color:#719e07">).&lt;/span>group&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> activateValue &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">((&lt;/span>Activate&lt;span style="color:#719e07">)&lt;/span> activate&lt;span style="color:#719e07">).&lt;/span>value&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>activate &lt;span style="color:#719e07">instanceof&lt;/span> com&lt;span style="color:#719e07">.&lt;/span>alibaba&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>extension&lt;span style="color:#719e07">.&lt;/span>Activate&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> activateGroup &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">((&lt;/span>com&lt;span style="color:#719e07">.&lt;/span>alibaba&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>extension&lt;span style="color:#719e07">.&lt;/span>Activate&lt;span style="color:#719e07">)&lt;/span> activate&lt;span style="color:#719e07">).&lt;/span>group&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> activateValue &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">((&lt;/span>com&lt;span style="color:#719e07">.&lt;/span>alibaba&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>extension&lt;span style="color:#719e07">.&lt;/span>Activate&lt;span style="color:#719e07">)&lt;/span> activate&lt;span style="color:#719e07">).&lt;/span>value&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存分组值
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedActivateGroups&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashSet&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>Arrays&lt;span style="color:#719e07">.&lt;/span>asList&lt;span style="color:#719e07">(&lt;/span>activateGroup&lt;span style="color:#719e07">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[][]&lt;/span> keyPairs &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[&lt;/span>activateValue&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">][];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历指定的激活扩展的扩展名字列表
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> activateValue&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>activateValue&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">].&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;:&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> keyPairs&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">2&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> arr &lt;span style="color:#719e07">=&lt;/span> activateValue&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">].&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;:&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> keyPairs&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">][&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> arr&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> keyPairs&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">][&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> arr&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> keyPairs&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> keyPairs&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">][&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> activateValue&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存指定扩展信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedActivateValues&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">,&lt;/span> keyPairs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// traverse all cached extensions
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//遍历所有激活的扩展名字和扩展分组集合
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedActivateGroups&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">((&lt;/span>name&lt;span style="color:#719e07">,&lt;/span> activateGroup&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//筛选当前扩展的扩展分组与激活扩展的扩展分组是否可以匹配
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isMatchGroup&lt;span style="color:#719e07">(&lt;/span>group&lt;span style="color:#719e07">,&lt;/span> activateGroup&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//不能是指定的扩展名字
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>namesSet&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//也不能是带有 -指定扩展名字
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>namesSet&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>REMOVE_VALUE_PREFIX &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果在Active注解中配置了value则当指定的键出现在URL的参数中时，激活当前扩展名。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果未配置value属性则默认都是匹配的(cachedActivateValues中不存在对应扩展名字的缓存的时候默认为true)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> isActive&lt;span style="color:#719e07">(&lt;/span>cachedActivateValues&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">),&lt;/span> url&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存激活的扩展类型映射的扩展名字
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> activateExtensionsMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>getExtensionClass&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">),&lt;/span> getExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>namesSet&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>DEFAULT_KEY&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// will affect order
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// `ext1,default,ext2` means ext1 will happens before all of the default extensions while ext2 will after them
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> extensionsResult &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>activateExtensionsMap&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">();&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String name &lt;span style="color:#719e07">=&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>i&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>name&lt;span style="color:#719e07">.&lt;/span>startsWith&lt;span style="color:#719e07">(&lt;/span>REMOVE_VALUE_PREFIX&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>namesSet&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>REMOVE_VALUE_PREFIX &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>DEFAULT_KEY&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>containsExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> extensionsResult&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> extensionsResult&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>activateExtensionsMap&lt;span style="color:#719e07">.&lt;/span>values&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extensionsResult&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// add extensions, will be sorted by its order
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">();&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String name &lt;span style="color:#719e07">=&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>i&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>name&lt;span style="color:#719e07">.&lt;/span>startsWith&lt;span style="color:#719e07">(&lt;/span>REMOVE_VALUE_PREFIX&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>namesSet&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>REMOVE_VALUE_PREFIX &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>DEFAULT_KEY&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>containsExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> activateExtensionsMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>getExtensionClass&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">),&lt;/span> getExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>activateExtensionsMap&lt;span style="color:#719e07">.&lt;/span>values&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>再来回顾下扫描扩展类型的时候,与激活扩展的相关扫描代码:
与激活注解关键的代码位置在这里ExtensionLoader的loadClass方法中
我来贴下loadClass方法核心的代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">.&lt;/span>isAnnotationPresent&lt;span style="color:#719e07">(&lt;/span>Adaptive&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cacheAdaptiveClass&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> overridden&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isWrapperClass&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cacheWrapperClass&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name &lt;span style="color:#719e07">=&lt;/span> findAnnotationName&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;No such extension name for the class &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> clazz&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; in the config &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> resourceURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> names &lt;span style="color:#719e07">=&lt;/span> NAME_SEPARATOR&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ArrayUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>names&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//位置在这里其他地方就不标记注释了,前面判断了如果不是Adaptive也不是Wrapper类型则我们可以来判断是否为Activate 类型如果是的话调用cacheActivateClass方法将扩展缓存进cachedActivates缓存中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cacheActivateClass&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> names&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String n &lt;span style="color:#719e07">:&lt;/span> names&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cacheName&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> n&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> saveInExtensionClass&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> clazz&lt;span style="color:#719e07">,&lt;/span> n&lt;span style="color:#719e07">,&lt;/span> overridden&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">cacheActivateClass&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz&lt;span style="color:#719e07">,&lt;/span> String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Activate activate &lt;span style="color:#719e07">=&lt;/span> clazz&lt;span style="color:#719e07">.&lt;/span>getAnnotation&lt;span style="color:#719e07">(&lt;/span>Activate&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>activate &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注解存在则加入激活注解缓存
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedActivates&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">,&lt;/span> activate&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// support com.alibaba.dubbo.common.extension.Activate
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> com&lt;span style="color:#719e07">.&lt;/span>alibaba&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>extension&lt;span style="color:#719e07">.&lt;/span>Activate oldActivate &lt;span style="color:#719e07">=&lt;/span> clazz&lt;span style="color:#719e07">.&lt;/span>getAnnotation&lt;span style="color:#719e07">(&lt;/span>com&lt;span style="color:#719e07">.&lt;/span>alibaba&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>extension&lt;span style="color:#719e07">.&lt;/span>Activate&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>oldActivate &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cachedActivates&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">,&lt;/span> oldActivate&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文： &lt;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源码解析》&lt;/a>&lt;/p></description></item><item><title>Blog: 06-Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="6-dubbo的spi扩展机制之普通扩展对象的创建与wrapper机制的源码解析">6 Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析&lt;/h1>
&lt;h2 id="61-普通扩展对象的加载与创建">6.1 普通扩展对象的加载与创建&lt;/h2>
&lt;p>这里我们要分析的是ExtensionLoader类型的getExtension(String name)方法, 有了前面自适应扩展的铺垫,这里就更容易来看了getExtension是根据扩展名字获取具体扩展的通用方法,我们来根据某个类型来获取扩展的时候就是走的这里,比如在这个博客开头的介绍:&lt;/p>
&lt;ul>
&lt;li>ApplicationModel中获取配置管理器对象&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> configManager &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigManager&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ApplicationExt&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>ConfigManager&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="611-getextension方法源码">6.1.1 getExtension方法源码&lt;/h3>
&lt;p>先来看下getExtension方法的源码,根据扩展名字查询扩展对象&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">public&lt;/span> T &lt;span style="color:#268bd2">getExtension&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里并不能看到什么,只多传了个参数wrap为true调用另外一个重载的方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> T extension &lt;span style="color:#719e07">=&lt;/span> getExtension(name, &lt;span style="color:#b58900">true&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (extension &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException(&lt;span style="color:#2aa198">&amp;#34;Not find extension: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extension;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">getExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> wrap&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查扩展加载器是否已被销毁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Extension name == null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展名字为true则加载默认扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getDefaultExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//非wrap类型则将缓存的扩展名字key加上_origin后缀
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//wrap是aop机制 俗称切面,这个origin在aop里面可以称为切点,下面的wrap扩展可以称为增强通知的类型,普通扩展和wrap扩展的扩展名字是一样的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String cacheKey &lt;span style="color:#719e07">=&lt;/span> name&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>wrap&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cacheKey &lt;span style="color:#719e07">+=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;_origin&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从cachedInstances缓存中查询
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Holder&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> holder &lt;span style="color:#719e07">=&lt;/span> getOrCreateHolder&lt;span style="color:#719e07">(&lt;/span>cacheKey&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object instance &lt;span style="color:#719e07">=&lt;/span> holder&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存中不存在则创建扩展对象 双重校验锁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>holder&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//双重校验锁的方式
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> holder&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> createExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">,&lt;/span> wrap&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> holder&lt;span style="color:#719e07">.&lt;/span>set&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们先来看一下默认扩展的加载代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">getDefaultExtension&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载扩展类型对应的所有扩展SPI实现类型,在加载所有扩展实现类型的时候会缓存这个扩展的默认实现类型,将对象缓存在cachedDefaultName中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> getExtensionClasses&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isBlank&lt;span style="color:#719e07">(&lt;/span>cachedDefaultName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>cachedDefaultName&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//再回到加载扩展的方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> getExtension&lt;span style="color:#719e07">(&lt;/span>cachedDefaultName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>创建扩展对象方法这个和自适应扩展的创建扩展类似
createExtension:
具体过程如下:&lt;/p>
&lt;ul>
&lt;li>加载扩展类型:getExtensionClasses()&lt;/li>
&lt;li>创建扩展对象:createExtensionInstance(clazz)&lt;/li>
&lt;li>注入自适应扩展: injectExtension(instance);&lt;/li>
&lt;li>wrap处理&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">private&lt;/span> T &lt;span style="color:#268bd2">createExtension&lt;/span>(String name, boolean wrap) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展的创建的第一步扫描所有jar中的扩展实现,这里扫描完之后获取对应扩展名字的扩展实现类型的Class对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz &lt;span style="color:#719e07">=&lt;/span> getExtensionClasses().get(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//出现异常了 转换下异常信息 再抛出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (clazz &lt;span style="color:#719e07">==&lt;/span> null &lt;span style="color:#719e07">||&lt;/span> unacceptableExceptions.contains(name)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> findException(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//当前扩展对象是否已经创建过了则直接从缓存中获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> T instance &lt;span style="color:#719e07">=&lt;/span> (T) extensionInstances.get(clazz);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//第一次获取缓存中肯定没有则创建扩展对象然后缓存起来
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//createExtensionInstance 这个是与自适应扩展对象创建对象的不同之处
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> (T) extensionInstances.get(clazz);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> postProcessBeforeInitialization(instance, name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注入扩展自适应方法,这个方法前面讲自适应扩展时候说了,注入自适应扩展方法的自适应扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> injectExtension(instance);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> postProcessAfterInitialization(instance, name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否开启了wrap
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//Dubbo通过Wrapper实现AOP的方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (wrap) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个可以参考下Dubbo扩展的加载
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> wrapperClassesList &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//wrap类型排序 这个wrap类型是如何来的呢,在前面扫描扩展类型的时候如果当前扩展类型不是Adaptive注解修饰的,并且当前类型type有个构造器参数是type自身的也是前面加载扩展类型时候说的装饰器模式 可以参考DubboProtocol的构造器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (cachedWrapperClasses &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> wrapperClassesList.addAll(cachedWrapperClasses);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据Wrapper注解的order值来进行排序值越小越在列表的前面
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> wrapperClassesList.sort(WrapperComparator.COMPARATOR);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//反转之后值越大就会在列表的前面
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Collections.reverse(wrapperClassesList);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从缓存中查到了wrapper扩展则遍历这些wrapp扩展进行筛选
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isNotEmpty(wrapperClassesList)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> wrapperClass : wrapperClassesList) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Wrapper wrapper &lt;span style="color:#719e07">=&lt;/span> wrapperClass.getAnnotation(Wrapper.&lt;span style="color:#719e07">class&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//需要包装的扩展名。当此数组为空时，默认值为匹配
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//看下当前扩展是否匹配这个wrap,如何判断呢?
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//wrapper注解不存在或者matches匹配,或者mismatches不包含当前扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> boolean match &lt;span style="color:#719e07">=&lt;/span> (wrapper &lt;span style="color:#719e07">==&lt;/span> null) &lt;span style="color:#719e07">||&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ((ArrayUtils.isEmpty(wrapper.matches()) &lt;span style="color:#719e07">||&lt;/span> ArrayUtils.contains(wrapper.matches(), name)) &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">!&lt;/span>ArrayUtils.contains(wrapper.mismatches(), name));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这是扩展类型是匹配wrapp的则开始注入
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (match) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//匹配到了就创建所有的wrapper类型的对象同时构造器参数设置为当前类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> postProcessAfterInitialization(instance, name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;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.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//初始化扩展,如果当前扩展是Lifecycle类型则调用初始化方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initExtension(instance);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (Throwable t) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> &lt;span style="color:#268bd2">IllegalStateException&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;Extension instance (name: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, class: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;)&lt;/span> couldn&amp;#39;t be instantiated: &lt;span style="color:#2aa198">&amp;#34; + t.getMessage(), t)&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="612-创建扩展对象">6.1.2 创建扩展对象&lt;/h3>
&lt;p>前面加载扩展类型在自适应扩展的时候已经说过了这里就不重复了,这里我们来看下
扩展对象的创建过程:createExtensionInstance(clazz)&lt;/p>
&lt;p>前面看自适应扩展对象创建的时候自适应扩展对象仅仅是使用反射newInstance了一个扩展对象,而普通的扩展类型创建对象的过程就相对复杂一点,接下来我们来看下:&lt;/p>
&lt;p>ExtensionLoader的createExtensionInstance方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">private&lt;/span> Object &lt;span style="color:#268bd2">createExtensionInstance&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> type) &lt;span style="color:#719e07">throws&lt;/span> ReflectiveOperationException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//在ExtensionLoader构造器中,有个initInstantiationStrategy()方法中new了一个初始化策略InstantiationStrategy类型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> instantiationStrategy.instantiate(type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>InstantiationStrategy的实例化对象方法instantiate&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T instantiate(Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type) &lt;span style="color:#719e07">throws&lt;/span> ReflectiveOperationException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// should not use default constructor directly, maybe also has another constructor matched scope model arguments
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 1. try to get default constructor
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Constructor&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> defaultConstructor &lt;span style="color:#719e07">=&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//反射获取对应类型的无参构造器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> defaultConstructor &lt;span style="color:#719e07">=&lt;/span> type.getConstructor();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (NoSuchMethodException e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore no default constructor
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 2. use matched constructor if found
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Constructor&lt;span style="color:#719e07">&amp;gt;&lt;/span> matchedConstructors &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取所有构造器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Constructor&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span>[] declaredConstructors &lt;span style="color:#719e07">=&lt;/span> type.getConstructors();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历构造器列表,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> (Constructor&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> constructor : declaredConstructors) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果存在构造器则构造器参数类型是否为ScopeModel类型,如果为ScopeModel则为匹配的构造器 说明我们扩展类型在这个版本如果想要让这个构造器生效必须参数类型为ScopeModel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (isMatched(constructor)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> matchedConstructors.add(constructor);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// remove default constructor from matchedConstructors
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (defaultConstructor &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> matchedConstructors.remove(defaultConstructor);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// match order:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 1. the only matched constructor with parameters
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 2. default constructor if absent
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Constructor targetConstructor;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//匹配的参数ScopeModel的构造器太多了就抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (matchedConstructors.size() &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> &lt;span style="color:#268bd2">IllegalArgumentException&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;Expect only one but found &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> matchedConstructors.size() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; matched constructors for type: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type.getName() &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, matched constructors: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> matchedConstructors);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#268bd2">if&lt;/span> (matchedConstructors.size() &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//一个参数一般为一个参数类型ScopeModel的构造器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> targetConstructor &lt;span style="color:#719e07">=&lt;/span> matchedConstructors.get(&lt;span style="color:#2aa198">0&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#268bd2">if&lt;/span> (defaultConstructor &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果没有自定义构造器则使用空参数构造器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> targetConstructor &lt;span style="color:#719e07">=&lt;/span> defaultConstructor;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//一个构造器也没匹配上也要报错
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> &lt;span style="color:#268bd2">IllegalArgumentException&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;None matched constructor was found for type: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type.getName());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// create instance with arguments
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//反射获取构造器参数的参数类型列表
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Class[] parameterTypes &lt;span style="color:#719e07">=&lt;/span> targetConstructor.getParameterTypes();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果存在参数则为参数设置值
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Object[] args &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Object[parameterTypes.length];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>; i &lt;span style="color:#719e07">&amp;lt;&lt;/span> parameterTypes.length; i&lt;span style="color:#719e07">++&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//借助scopeModelAccessor工具获取参数类型,这个参数类型为当前的域模型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> args[i] &lt;span style="color:#719e07">=&lt;/span> getArgumentValueForType(parameterTypes[i]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> (T) targetConstructor.newInstance(args);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="62-wrap机制">6.2 wrap机制&lt;/h2>
&lt;h3 id="621-wrapper机制说明">6.2.1 Wrapper机制说明&lt;/h3>
&lt;p>Dubbo通过Wrapper实现AOP的方法&lt;/p>
&lt;p>Wrapper机制，即扩展点自动包装。Wrapper 类同样实现了扩展点接口，但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时，包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例，Wrapper 持有了实际的扩展点实现类。
扩展点的 Wrapper 类可以有多个，也可以根据需要新增。
通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑，有些类似 AOP，即 Wrapper 代理了扩展点。&lt;/p>
&lt;p>Wrapper的规范
Wrapper 机制不是通过注解实现的，而是通过一套 Wrapper 规范实现的。
Wrapper 类在定义时需要遵循如下规范。&lt;/p>
&lt;ul>
&lt;li>该类要实现 SPI 接口&lt;/li>
&lt;li>该类中要有 SPI 接口的引用&lt;/li>
&lt;li>该类中必须含有一个含参的构造方法且参数只能有一个类型为SPI接口&lt;/li>
&lt;li>在接口实现方法中要调用 SPI 接口引用对象的相应方法&lt;/li>
&lt;li>该类名称以 Wrapper 结尾&lt;/li>
&lt;/ul>
&lt;p>比如如下几个扩展类型&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">class&lt;/span> &lt;span style="color:#268bd2">org&lt;/span>.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">class&lt;/span> &lt;span style="color:#268bd2">org&lt;/span>.apache.dubbo.qos.protocol.QosProtocolWrapper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">class&lt;/span> &lt;span style="color:#268bd2">org&lt;/span>.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">class&lt;/span> &lt;span style="color:#268bd2">org&lt;/span>.apache.dubbo.qos.protocol.QosProtocolWrapper
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>回顾下Wrapper扩展类型的扫描于对象的创建&lt;/p>
&lt;h3 id="622-wrapper类型的扫描">6.2.2 Wrapper类型的扫描&lt;/h3>
&lt;p>&lt;strong>Wrapper类型的扫描代码如下:&lt;/strong>&lt;/p>
&lt;p>来自4.5.2.3小节ExtensionLoader类型中的loadClass方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展子类型是否存在这个注解@Adaptive
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (clazz.isAnnotationPresent(Adaptive.&lt;span style="color:#719e07">class&lt;/span>)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cacheAdaptiveClass(clazz, overridden);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#268bd2">if&lt;/span> (isWrapperClass(clazz)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展子类型构造器中是否有这个类型的接口 (这个可以想象下我们了解的Java IO流中的类型使用到的装饰器模式 构造器传个类型)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cacheWrapperClass(clazz);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>isWrapperClass方法通过判断构造器类型是否为当前类型来判断是否为Wrapper类型&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">private&lt;/span> boolean &lt;span style="color:#268bd2">isWrapperClass&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clazz.getConstructor(type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#b58900">true&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (NoSuchMethodException e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#b58900">false&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="623-wrapper类型的创建">6.2.3 Wrapper类型的创建&lt;/h3>
&lt;p>这个可以看下4.6.1 getExtension方法源码的获取扩展对象时候查询扩展对象是否有对应的Wrapper类型的扩展为其创建Wrapper扩展对象,如下代码&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//Dubbo通过Wrapper实现AOP的方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (wrap) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个可以参考下Dubbo扩展的加载
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> wrapperClassesList &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//wrap类型排序 这个wrap类型是如何来的呢,在前面扫描扩展类型的时候如果当前扩展类型不是Adaptive注解修饰的,并且当前类型type有个构造器参数是type自身的也是前面加载扩展类型时候说的装饰器模式 可以参考DubboProtocol的构造器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (cachedWrapperClasses &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> wrapperClassesList.addAll(cachedWrapperClasses);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据Wrapper注解的order值来进行排序值越小越在列表的前面
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> wrapperClassesList.sort(WrapperComparator.COMPARATOR);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//反转之后值越大就会在列表的前面
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Collections.reverse(wrapperClassesList);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从缓存中查到了wrapper扩展则遍历这些wrapp扩展进行筛选
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isNotEmpty(wrapperClassesList)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> wrapperClass : wrapperClassesList) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Wrapper wrapper &lt;span style="color:#719e07">=&lt;/span> wrapperClass.getAnnotation(Wrapper.&lt;span style="color:#719e07">class&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//需要包装的扩展名。当此数组为空时，默认值为匹配
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//看下当前扩展是否匹配这个wrap,如何判断呢?
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//wrapper注解不存在或者matches匹配,或者mismatches不包含当前扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> boolean match &lt;span style="color:#719e07">=&lt;/span> (wrapper &lt;span style="color:#719e07">==&lt;/span> null) &lt;span style="color:#719e07">||&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ((ArrayUtils.isEmpty(wrapper.matches()) &lt;span style="color:#719e07">||&lt;/span> ArrayUtils.contains(wrapper.matches(), name)) &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">!&lt;/span>ArrayUtils.contains(wrapper.mismatches(), name));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这是扩展类型是匹配wrapp的则开始注入
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (match) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//匹配到了就创建所有的wrapper类型的对象同时构造器参数设置为当前类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> postProcessAfterInitialization(instance, name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>主要来看下什么情况下才为当前扩展类型创建Wrapper包装类型:&lt;/p>
&lt;ul>
&lt;li>wrapper注解不存在(前面判断过Wrapper类型是构造器满足条件的)&lt;/li>
&lt;li>存在Wrapper注解:
&lt;ul>
&lt;li>matches匹配,&lt;/li>
&lt;li>或者mismatches不包含当前扩展&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装&lt;/p>
&lt;p>原文： &lt;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机制的源码解析》&lt;/a>&lt;/p></description></item><item><title>Blog: 05-自适应扩展对象的创建getAdaptiveExtension方法</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h2 id="5-自适应扩展对象的创建getadaptiveextension方法">5 自适应扩展对象的创建getAdaptiveExtension方法&lt;/h2>
&lt;p>自适应扩展又称为动态扩展,可以在运行时生成扩展对象&lt;/p>
&lt;p>ExtensionLoader中的getAdaptiveExtension()方法,这个方法也是我们看到的第一个获取扩展对象的方法. ,这个方法可以帮助我们通过SPI机制从扩展文件中找到需要的扩展类型并创建它的对象,
&lt;strong>自适应扩展:&lt;strong>如果对设计模式比较了解的可能会联想到&lt;/strong>适配器模式&lt;/strong>,自适应扩展其实就是适配器模式的思路,自适应扩展有两种策略:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>一种是我们自己实现自适应扩展:然后使用@Adaptive修饰这个时候适配器的逻辑由我们自己实现,当扩展加载器去查找具体的扩展的时候可以通过找到我们这个对应的适配器扩展,然后适配器扩展帮忙去查询真正的扩展,这个比如我们下面要举的扩展注入器的例子,具体扩展通过扩展注入器适配器,注入器适配器来查询具体的注入器扩展实现来帮忙查找扩展。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>还有一种方式是我们未实现这个自适应扩展,Dubbo在运行时通过字节码动态代理的方式在运行时生成一个适配器,使用这个适配器映射到具体的扩展. 第二种情况往往用在比如 Protocol、Cluster、LoadBalance 等。有时，有些拓展并不想在框架启动阶段被加载，而是希望在拓展方法被调用时，根据运行时参数进行加载。(如果还不了解可以考虑看下@Adaptive注解加载方法上面的时候扩展是如何加载的)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">getAdaptiveExtension&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查当前扩展加载器是否已经被销毁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从自适应扩展缓存中查询扩展对象如果存在就直接返回,这个自适应扩展类型只会有一个扩展实现类型如果是多个的话根据是否可以覆盖参数决定扩展实现类是否可以相互覆盖
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Object instance &lt;span style="color:#719e07">=&lt;/span> cachedAdaptiveInstance&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个if判断不太优雅 容易多层嵌套,上面instance不为空就可以直接返回了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建异常则抛出异常直接返回(多线程场景下可能第一个线程异常了第二个线程进来之后走到这里)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>createAdaptiveInstanceError &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to create adaptive instance: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createAdaptiveInstanceError&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createAdaptiveInstanceError&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加锁排队 (单例模式创建对象的思想 双重校验锁)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedAdaptiveInstance&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加锁的时候对象都是空的,进来之后先判断下防止重复创建
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> cachedAdaptiveInstance&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//只有第一个进来锁的对象为空开始创建扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据SPI机制获取类型,创建对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> createAdaptiveExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//存入缓存
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedAdaptiveInstance&lt;span style="color:#719e07">.&lt;/span>set&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createAdaptiveInstanceError &lt;span style="color:#719e07">=&lt;/span> t&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to create adaptive instance: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">(),&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面使用单例思想来调用创建自适应扩展对象的方法,下面就让我们深入探究下创建自适应扩展对象的整个过程createAdaptiveExtension();方法:&lt;/p>
&lt;h2 id="51-创建扩展对象的生命周期方法-注意这个后续会详细解析这个声明周期方法的细节">5.1 创建扩展对象的生命周期方法-注意这个后续会详细解析这个声明周期方法的细节&lt;/h2>
&lt;p>createAdaptiveExtension()
我们先来看ExtensionLoader类型中的createAdaptiveExtension();方法,这个方法包含了扩展对象创建初始化的整个生命周期,如下代码所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> T &lt;span style="color:#268bd2">createAdaptiveExtension&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取扩展类型实现类, 创建扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> T instance &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> getAdaptiveExtensionClass&lt;span style="color:#719e07">().&lt;/span>newInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注入扩展对象之前的回调方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> postProcessBeforeInitialization&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注入扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> injectExtension&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注入扩展对象之后的回调方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> instance &lt;span style="color:#719e07">=&lt;/span> postProcessAfterInitialization&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化扩展对象的属性,如果当前扩展实例的类型实现了Lifecycle则调用当前扩展对象的生命周期回调方法initialize()(来自Lifecycle接口)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//参考样例第一个instance为ExtensionInjector的自适应扩展对象类型为AdaptiveExtensionInjector,自适应扩展注入器(适配器)用来查询具体支持的扩展注入器比如scope,spi,spring注入器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initExtension&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Can&amp;#39;t create adaptive extension &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, cause: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="52-spi机制获取扩展对象实现类型getadaptiveextensionclass">5.2 SPI机制获取扩展对象实现类型getAdaptiveExtensionClass()&lt;/h2>
&lt;p>这个方法可以帮助我们了解具体的Dubbo SPI机制 如果找到扩展类型的实现类,会寻找哪些文件,扩展文件的优先级又是什么,对我们自己写扩展方法很有帮助,接下来我们就来看下它的源码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> getAdaptiveExtensionClass&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取扩展类型,将扩展类型存入成员变量cachedClasses中进行缓存
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> getExtensionClasses&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//在上个方法的详细解析中的最后一步loadClass方法中如果扩展类型存在Adaptive注解将会将扩展类型赋值给cachedAdaptiveClass,否则的话会把扩展类型都缓存起来存储在扩展集合extensionClasses中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedAdaptiveClass &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> cachedAdaptiveClass&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展实现类型没有一个这个自适应注解Adaptive时候会走到这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//刚刚我们扫描到了扩展类型然后将其存入cachedClasses集合中了 接下来我们看下如何创建扩展类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> cachedAdaptiveClass &lt;span style="color:#719e07">=&lt;/span> createAdaptiveExtensionClass&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续看获取扩展类型的方法&lt;strong>getExtensionClasses()&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> getExtensionClasses&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存中查询扩展类型是否存在
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> classes &lt;span style="color:#719e07">=&lt;/span> cachedClasses&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>classes &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//单例模式双重校验锁判断
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedClasses&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classes &lt;span style="color:#719e07">=&lt;/span> cachedClasses&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>classes &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载扩展类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> classes &lt;span style="color:#719e07">=&lt;/span> loadExtensionClasses&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将我们扫描到的扩展类型存入成员变量cachedClasses中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedClasses&lt;span style="color:#719e07">.&lt;/span>set&lt;span style="color:#719e07">(&lt;/span>classes&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> classes&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="521-使用不同的的策略加载加载不同目录下的扩展">5.2.1 使用不同的的策略加载加载不同目录下的扩展&lt;/h3>
&lt;p>加载扩展类型的方法&lt;strong>loadExtensionClasses()&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">private&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String, Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> loadExtensionClasses() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查扩展加载器是否被销毁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkDestroyed();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存默认的扩展名到成员变量cachedDefaultName中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cacheDefaultExtensionName();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载到的扩展集合
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String, Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> extensionClasses &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展策略,在4.3章节中我们介绍了这个类型的UML与说明
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//LoadingStrategy扩展加载策略,目前有3个扩展加载策略
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//DubboInternalLoadingStrategy:Dubbo内置的扩展加载策略,将加载文件目录为META-INF/dubbo/internal/的扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//DubboLoadingStrategy:Dubbo普通的扩展加载策略,将加载目录为META-INF/dubbo/的扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//ServicesLoadingStrategy:JAVA SPI加载策略 ,将加载目录为META-INF/services/的扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//扩展策略集合对象在什么时候初始化的呢在成员变量初始化的时候就创建了集合对象,这个可以看方法loadLoadingStrategies() 通过Java的 SPI加载策略
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> (LoadingStrategy strategy : strategies) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据策略从指定文件目录中加载扩展类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadDirectory(extensionClasses, strategy, type.getName());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// compatible with old ExtensionFactory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//如果当前要加载的扩展类型是扩展注入类型则扫描下ExtensionFactory类型的扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">this&lt;/span>.type &lt;span style="color:#719e07">==&lt;/span> ExtensionInjector.&lt;span style="color:#719e07">class&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个方法和上面那个方法是一样的就不详细说了 扫描文件 找到扩展类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadDirectory(extensionClasses, strategy, ExtensionFactory.&lt;span style="color:#719e07">class&lt;/span>.&lt;span style="color:#268bd2">getName&lt;/span>());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//通过loadDirectory扫描 扫描到了ExtensionInjector类型的扩展实现类有3个 我们将会得到这样一个集合例子:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//&amp;#34;spring&amp;#34; -&amp;gt; &amp;#34;class org.apache.dubbo.config.spring.extension.SpringExtensionInjector&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//&amp;#34;scopeBean&amp;#34; -&amp;gt; &amp;#34;class org.apache.dubbo.common.beans.ScopeBeanExtensionInjector&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//&amp;#34;spi&amp;#34; -&amp;gt; &amp;#34;class org.apache.dubbo.common.extension.inject.SpiExtensionInjector&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> extensionClasses;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>从文件中加载扩展实现loadDirectory方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">loadDirectory&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> extensionClasses&lt;span style="color:#719e07">,&lt;/span> LoadingStrategy strategy&lt;span style="color:#719e07">,&lt;/span> String type&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载并根据策略的参数来加载
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadDirectory&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>directory&lt;span style="color:#719e07">(),&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>preferExtensionClassLoader&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> strategy&lt;span style="color:#719e07">.&lt;/span>overridden&lt;span style="color:#719e07">(),&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>includedPackages&lt;span style="color:#719e07">(),&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>excludedPackages&lt;span style="color:#719e07">(),&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>onlyExtensionClassLoaderPackages&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//下面两行就是要兼容alibaba的扩展包了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String oldType &lt;span style="color:#719e07">=&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>replace&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;org.apache&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.alibaba&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadDirectory&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>directory&lt;span style="color:#719e07">(),&lt;/span> oldType&lt;span style="color:#719e07">,&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>preferExtensionClassLoader&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> strategy&lt;span style="color:#719e07">.&lt;/span>overridden&lt;span style="color:#719e07">(),&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>includedPackagesInCompatibleType&lt;span style="color:#719e07">(),&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>excludedPackages&lt;span style="color:#719e07">(),&lt;/span> strategy&lt;span style="color:#719e07">.&lt;/span>onlyExtensionClassLoaderPackages&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>带扩展策略参数的loadDirectory方法&lt;/p>
&lt;p>关于扩展策略的参数列表我这里列个表格方便大家来看&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>扩展类型&lt;/th>
&lt;th>dir(目录)&lt;/th>
&lt;th>extensionLoaderClassLoaderFirst(优先扩展类型的类加载器)&lt;/th>
&lt;th>overridden(是否允许覆盖同名扩展)&lt;/th>
&lt;th>includedPackages (明确包含的扩展包)&lt;/th>
&lt;th>excludedPackages (明确排除的扩展包)&lt;/th>
&lt;th>onlyExtensionClassLoaderPackages(限制应该从Dubbo的类加载器加载的类)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>DubboInternalLoadingStrategy&lt;/td>
&lt;td>META-INF/dubbo/internal/&lt;/td>
&lt;td>false&lt;/td>
&lt;td>false&lt;/td>
&lt;td>null&lt;/td>
&lt;td>null&lt;/td>
&lt;td>[]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DubboLoadingStrategy&lt;/td>
&lt;td>META-INF/dubbo/&lt;/td>
&lt;td>false&lt;/td>
&lt;td>true&lt;/td>
&lt;td>null&lt;/td>
&lt;td>null&lt;/td>
&lt;td>[]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ServicesLoadingStrategy&lt;/td>
&lt;td>META-INF/services/&lt;/td>
&lt;td>false&lt;/td>
&lt;td>true&lt;/td>
&lt;td>null&lt;/td>
&lt;td>null&lt;/td>
&lt;td>[]&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 不同的扩展策略传递了不同的参数,但是扩展的加载流程是相同的,这里我们可以参考上面表格
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param extensionClasses
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param dir
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param type 这里我们参考的示例这个值为org.apache.dubbo.common.extension.ExtensionInjector
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param extensionLoaderClassLoaderFirst
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param overridden false
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param includedPackages
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param excludedPackages
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param onlyExtensionClassLoaderPackages
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">loadDirectory&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> extensionClasses&lt;span style="color:#719e07">,&lt;/span> String dir&lt;span style="color:#719e07">,&lt;/span> String type&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> extensionLoaderClassLoaderFirst&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> overridden&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> includedPackages&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> excludedPackages&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> onlyExtensionClassLoaderPackages&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展目录 + 扩展类型全路径 比如: META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String fileName &lt;span style="color:#719e07">=&lt;/span> dir &lt;span style="color:#719e07">+&lt;/span> type&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader&lt;span style="color:#719e07">&amp;gt;&lt;/span> classLoadersToLoad &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> LinkedList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// try to load from ExtensionLoader&amp;#39;s ClassLoader first
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//是否优先使用扩展加载器的 类加载器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>extensionLoaderClassLoaderFirst&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassLoader extensionLoaderClassLoader &lt;span style="color:#719e07">=&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getClassLoader&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ClassLoader&lt;span style="color:#719e07">.&lt;/span>getSystemClassLoader&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> extensionLoaderClassLoader&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classLoadersToLoad&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>extensionLoaderClassLoader&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load from scope model
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//获取域模型对象的类型加载器 ,这个域模型对象在初始化的时候会将自己的类加载器放入集合中可以参考《3.2.2 初始化ScopeModel》章节
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader&lt;span style="color:#719e07">&amp;gt;&lt;/span> classLoaders &lt;span style="color:#719e07">=&lt;/span> scopeModel&lt;span style="color:#719e07">.&lt;/span>getClassLoaders&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//没有可用的类加载器则从使用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>classLoaders&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从用于加载类的搜索路径中查找指定名称的所有资源。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Enumeration&lt;span style="color:#719e07">&amp;lt;&lt;/span>java&lt;span style="color:#719e07">.&lt;/span>net&lt;span style="color:#719e07">.&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> resources &lt;span style="color:#719e07">=&lt;/span> ClassLoader&lt;span style="color:#719e07">.&lt;/span>getSystemResources&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>resources &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">(&lt;/span>resources&lt;span style="color:#719e07">.&lt;/span>hasMoreElements&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadResource&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span> resources&lt;span style="color:#719e07">.&lt;/span>nextElement&lt;span style="color:#719e07">(),&lt;/span> overridden&lt;span style="color:#719e07">,&lt;/span> includedPackages&lt;span style="color:#719e07">,&lt;/span> excludedPackages&lt;span style="color:#719e07">,&lt;/span> onlyExtensionClassLoaderPackages&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classLoadersToLoad&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>classLoaders&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用类加载资源加载器(ClassLoaderResourceLoader)来加载具体的资源
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader&lt;span style="color:#719e07">,&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>java&lt;span style="color:#719e07">.&lt;/span>net&lt;span style="color:#719e07">.&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> resources &lt;span style="color:#719e07">=&lt;/span> ClassLoaderResourceLoader&lt;span style="color:#719e07">.&lt;/span>loadResources&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">,&lt;/span> classLoadersToLoad&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历从所有资源文件中读取到资源url地址,key为类加载器,值为扩展文件url如夏所示
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;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
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> resources&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(((&lt;/span>classLoader&lt;span style="color:#719e07">,&lt;/span> urls&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从文件中加载完资源之后开始根据类加载器和url加载具体的扩展类型,最后将扩展存放进extensionClasses集合
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadFromClass&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> overridden&lt;span style="color:#719e07">,&lt;/span> urls&lt;span style="color:#719e07">,&lt;/span> classLoader&lt;span style="color:#719e07">,&lt;/span> includedPackages&lt;span style="color:#719e07">,&lt;/span> excludedPackages&lt;span style="color:#719e07">,&lt;/span> onlyExtensionClassLoaderPackages&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Exception occurred when loading extension class (interface: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, description file: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;).&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="522-借助类加载器的getresources方法遍历所有文件进行扩展文件的查询">5.2.2 借助类加载器的getResources方法遍历所有文件进行扩展文件的查询&lt;/h3>
&lt;p>查找扩展类型对应的扩展文件的url方法:ClassLoaderResourceLoader类型的loadResources源码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">public&lt;/span> &lt;span style="color:#719e07">static&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader, Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> loadResources(String fileName, List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader&lt;span style="color:#719e07">&amp;gt;&lt;/span> classLoaders) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader, Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> resources &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//不同的类加载器之间使用不同的线程异步的方式进行扫描
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> CountDownLatch countDownLatch &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CountDownLatch(classLoaders.size());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (ClassLoader classLoader : classLoaders) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//多线程扫描,这个是个newCachedThreadPool的类型的线程池
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GlobalResourcesRepository.getGlobalExecutorService().submit(() &lt;span style="color:#719e07">-&amp;gt;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> resources.put(classLoader, loadResources(fileName, classLoader));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> countDownLatch.countDown();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> });
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> countDownLatch.await();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (InterruptedException e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e.printStackTrace();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> Collections.unmodifiableMap(&lt;span style="color:#719e07">new&lt;/span> LinkedHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>(resources));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>加载具体类加载器中的资源文件的loadResources方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">public&lt;/span> &lt;span style="color:#719e07">static&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> loadResources(String fileName, ClassLoader currentClassLoader) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader, Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String, Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&amp;gt;&amp;gt;&lt;/span> classLoaderCache;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//第一次进来类加载器资源缓存是空的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (classLoaderResourcesCache &lt;span style="color:#719e07">==&lt;/span> null &lt;span style="color:#719e07">||&lt;/span> (classLoaderCache &lt;span style="color:#719e07">=&lt;/span> classLoaderResourcesCache.get()) &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//类对象锁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> synchronized (ClassLoaderResourceLoader.&lt;span style="color:#719e07">class&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (classLoaderResourcesCache &lt;span style="color:#719e07">==&lt;/span> null &lt;span style="color:#719e07">||&lt;/span> (classLoaderCache &lt;span style="color:#719e07">=&lt;/span> classLoaderResourcesCache.get()) &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classLoaderCache &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个类资源映射url的软引用缓存对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//软引用(soft references)，用于帮助垃圾收集器管理内存使用和消除潜在的内存泄漏。当内存快要不足的时候，GC会迅速的把所有的软引用清除掉，释放内存空间
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> classLoaderResourcesCache &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> SoftReference&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>(classLoaderCache);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//第一次进来时候类加载器url映射缓存是空的,给类加载器缓存对象新增一个值,key是类加载器,值是map类型用来存储文件名对应的url集合
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>classLoaderCache.containsKey(currentClassLoader)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classLoaderCache.putIfAbsent(currentClassLoader, &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String, Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> urlCache &lt;span style="color:#719e07">=&lt;/span> classLoaderCache.get(currentClassLoader);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存中没有就从文件里面找
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>urlCache.containsKey(fileName)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> set &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> LinkedHashSet&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Enumeration&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> urls;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//getResources这个方法是这样的:加载当前类加载器以及父类加载器所在路径的资源文件,将遇到的所有资源文件全部返回！这个可以理解为使用双亲委派模型中的类加载器 加载各个位置的资源文件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> urls &lt;span style="color:#719e07">=&lt;/span> currentClassLoader.getResources(fileName);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//native配置 是否为本地镜像(k可以参考官方文档:https://dubbo.apache.org/zh/docs/references/graalvm/support-graalvm/
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> boolean isNative &lt;span style="color:#719e07">=&lt;/span> NativeUtils.isNative();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (urls &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历找到的对应扩展的文件url将其加入集合
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">while&lt;/span> (urls.hasMoreElements()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL url &lt;span style="color:#719e07">=&lt;/span> urls.nextElement();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (isNative) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;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
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//动态修改jdk底层url对象的ref变量为可访问,让我们在用反射时访问私有变量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> setRef(url);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> set.add(url);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (IOException e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e.printStackTrace();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//存入缓存
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> urlCache.put(fileName, set);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//返回结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> urlCache.get(fileName);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="523-使用找到的扩展资源url加载具体扩展类型到内存">5.2.3 使用找到的扩展资源url加载具体扩展类型到内存&lt;/h3>
&lt;p>ExtensionLoader类型中的loadFromClass方法 遍历url 开始加载扩展类型&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">loadFromClass&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> extensionClasses&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> overridden&lt;span style="color:#719e07">,&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>java&lt;span style="color:#719e07">.&lt;/span>net&lt;span style="color:#719e07">.&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> urls&lt;span style="color:#719e07">,&lt;/span> ClassLoader classLoader&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> includedPackages&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> excludedPackages&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> onlyExtensionClassLoaderPackages&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>urls&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>java&lt;span style="color:#719e07">.&lt;/span>net&lt;span style="color:#719e07">.&lt;/span>URL url &lt;span style="color:#719e07">:&lt;/span> urls&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadResource&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> classLoader&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> overridden&lt;span style="color:#719e07">,&lt;/span> includedPackages&lt;span style="color:#719e07">,&lt;/span> excludedPackages&lt;span style="color:#719e07">,&lt;/span> onlyExtensionClassLoaderPackages&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionLoader类型中的loadResource方法 使用IO流读取扩展文件的内容
读取内容之前我这里先贴一下我们参考的扩展注入类型的文件中的内容如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>adaptive&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.common.extension.inject.AdaptiveExtensionInjector
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spi&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.common.extension.inject.SpiExtensionInjector
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>scopeBean&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.common.beans.ScopeBeanExtensionInjector
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>扩展中的文件都是一行一行的,并且扩展名字和扩展类型之间使用等号隔开=
了解了文件内容之后 应该下面的代码大致思路就知道了,我们可以详细看下&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">loadResource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> extensionClasses&lt;span style="color:#719e07">,&lt;/span> ClassLoader classLoader&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> java&lt;span style="color:#719e07">.&lt;/span>net&lt;span style="color:#719e07">.&lt;/span>URL resourceURL&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> overridden&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> includedPackages&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> excludedPackages&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> onlyExtensionClassLoaderPackages&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里固定了文件的格式为utf8
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">(&lt;/span>BufferedReader reader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> BufferedReader&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> InputStreamReader&lt;span style="color:#719e07">(&lt;/span>resourceURL&lt;span style="color:#719e07">.&lt;/span>openStream&lt;span style="color:#719e07">(),&lt;/span> StandardCharsets&lt;span style="color:#719e07">.&lt;/span>UTF_8&lt;span style="color:#719e07">)))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String line&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String clazz&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//按行读取 例如读取到的内容:spring=org.apache.dubbo.config.spring.extension.SpringExtensionInjector
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">((&lt;/span>line &lt;span style="color:#719e07">=&lt;/span> reader&lt;span style="color:#719e07">.&lt;/span>readLine&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//不知道为何会有这么一行代码删除#之后的字符串
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> ci &lt;span style="color:#719e07">=&lt;/span> line&lt;span style="color:#719e07">.&lt;/span>indexOf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#39;#&amp;#39;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ci &lt;span style="color:#719e07">&amp;gt;=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> line &lt;span style="color:#719e07">=&lt;/span> line&lt;span style="color:#719e07">.&lt;/span>substring&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ci&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> line &lt;span style="color:#719e07">=&lt;/span> line&lt;span style="color:#719e07">.&lt;/span>trim&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>line&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展文件可能如上面我贴的那样 名字和类型等号隔开,也可能是无类型的,例如扩展加载策略使用的是JDK自带的方式services内容中只包含具体的扩展类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> line&lt;span style="color:#719e07">.&lt;/span>indexOf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#39;=&amp;#39;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>i &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name &lt;span style="color:#719e07">=&lt;/span> line&lt;span style="color:#719e07">.&lt;/span>substring&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> i&lt;span style="color:#719e07">).&lt;/span>trim&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clazz &lt;span style="color:#719e07">=&lt;/span> line&lt;span style="color:#719e07">.&lt;/span>substring&lt;span style="color:#719e07">(&lt;/span>i &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">).&lt;/span>trim&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clazz &lt;span style="color:#719e07">=&lt;/span> line&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//isExcluded是否为加载策略要排除的配置,参数这里为空代表全部类型不排除
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//isIncluded是否为加载策略包含的类型,参数这里为空代表全部文件皆可包含
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//onlyExtensionClassLoaderPackages参数是否只有扩展类的类加载器可以加载扩展,其他扩展类型的类加载器不能加载扩展 这里结果为false 不排除任何类加载器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>isExcluded&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> excludedPackages&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> isIncluded&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> includedPackages&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>isExcludedByClassLoader&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> classLoader&lt;span style="color:#719e07">,&lt;/span> onlyExtensionClassLoaderPackages&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据类全路径加载类到内存
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadClass&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> resourceURL&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">.&lt;/span>forName&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">,&lt;/span> classLoader&lt;span style="color:#719e07">),&lt;/span> name&lt;span style="color:#719e07">,&lt;/span> overridden&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> IllegalStateException e &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to load extension class (interface: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, class line: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> line &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) in &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> resourceURL &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, cause: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exceptions&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>line&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Exception occurred when loading extension class (interface: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, class file: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> resourceURL &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) in &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> resourceURL&lt;span style="color:#719e07">,&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionLoader类型中的loadClass方法加载具体的类到内存&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">loadClass&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> extensionClasses&lt;span style="color:#719e07">,&lt;/span> java&lt;span style="color:#719e07">.&lt;/span>net&lt;span style="color:#719e07">.&lt;/span>URL resourceURL&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz&lt;span style="color:#719e07">,&lt;/span> String name&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> overridden&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> NoSuchMethodException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//当前clazz是否为type的子类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这里第一次访问到的type是ExtensionInjector,clazz是SpringExtensionInjector 父子类型关系满足情况
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>type&lt;span style="color:#719e07">.&lt;/span>isAssignableFrom&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Error occurred when loading extension class (interface: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, class line: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> clazz&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;), class &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+&lt;/span> clazz&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; is not subtype of interface.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展子类型是否存在这个注解@Adaptive
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">.&lt;/span>isAnnotationPresent&lt;span style="color:#719e07">(&lt;/span>Adaptive&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cacheAdaptiveClass&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> overridden&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isWrapperClass&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展子类型构造器中是否有这个类型的接口 (这个可以想象下我们了解的Java IO流中的类型使用到的装饰器模式 构造器传个类型)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cacheWrapperClass&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//无自适应注解,也没有构造器是扩展类型参数 ,这个name我们在扩展文件中找到了就是等号前面那个
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//低版本中可以使用@Extension 扩展注解来标注扩展类型,这里获取注解有两个渠道:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//先查询@Extension注解是否存在如果存在则取value值,如果不存在@Extension注解则获取当前类型的名字
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> name &lt;span style="color:#719e07">=&lt;/span> findAnnotationName&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;No such extension name for the class &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> clazz&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; in the config &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> resourceURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取扩展名字数组,扩展名字可能为逗号隔开的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> names &lt;span style="color:#719e07">=&lt;/span> NAME_SEPARATOR&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ArrayUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>names&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//@Activate注解修饰的扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cacheActivateClass&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> names&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String n &lt;span style="color:#719e07">:&lt;/span> names&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//cachedNames缓存集合缓存当前扩展类型的扩展名字
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cacheName&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> n&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将扩展类型加入结果集合extensionClasses中,不允许覆盖的话出现同同名字扩展将抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> saveInExtensionClass&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> clazz&lt;span style="color:#719e07">,&lt;/span> n&lt;span style="color:#719e07">,&lt;/span> overridden&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionLoader类型中cacheAdaptiveClass
Adaptive 机制，即扩展类的自适应机制。即其可以指定想要加载的扩展名，也可以不指定。若不指定，则直接加载默认的扩展类。即其会自动匹配，做到自适应。其是通过@Adaptive注解实现的。
自适应注解修饰的扩展同一个扩展名字只能有一个扩展实现类型, 扩展策略中提供的参数overridden是否允许覆盖扩展覆盖&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">cacheAdaptiveClass&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> overridden&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedAdaptiveClass &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> overridden&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//成员变量存储这个自适应扩展类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedAdaptiveClass &lt;span style="color:#719e07">=&lt;/span> clazz&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>cachedAdaptiveClass&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;More than 1 adaptive class found: &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+&lt;/span> cachedAdaptiveClass&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> clazz&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionLoader类型中cacheWrapperClass
Wrapper 机制，即扩展类的包装机制。就是对扩展类中的 SPI 接口方法进行增强，进行包装，是 AOP 思想的体现，是 Wrapper 设计模式的应用。一个 SPI 可以包含多个 Wrapper。这个也是可以同一个类型多个&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">cacheWrapperClass&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedWrapperClasses &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cachedWrapperClasses &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashSet&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存这个Wrapper类型的扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedWrapperClasses&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionLoader类型中cacheActivateClass
Activate用于激活扩展类的。 这个扩展类型可以出现多个比如过滤器可以同一个扩展名字多个过滤器实现,所以不需要有override判断
Activate 机制，即扩展类的激活机制。通过指定的条件来激活当前的扩展类。其是通过@Activate 注解实现的。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">cacheActivateClass&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz, String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Activate activate &lt;span style="color:#719e07">=&lt;/span> clazz.getAnnotation(Activate.&lt;span style="color:#719e07">class&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (activate &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存Activate类型的扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedActivates.put(name, activate);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// support com.alibaba.dubbo.common.extension.Activate
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> com.alibaba.dubbo.common.extension.Activate oldActivate &lt;span style="color:#719e07">=&lt;/span> clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.&lt;span style="color:#719e07">class&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (oldActivate &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cachedActivates.put(name, oldActivate);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionLoader类型中的saveInExtensionClass方法&lt;/p>
&lt;p>上面扩展对象加载了这么多最终的目的就是将这个扩展类型存放进结果集合extensionClasses中,扩展策略中提供的参数overridden是否允许覆盖扩展覆盖&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">saveInExtensionClass&lt;/span>(Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String, Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> extensionClasses, Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz, String name, boolean overridden) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> c &lt;span style="color:#719e07">=&lt;/span> extensionClasses.get(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (c &lt;span style="color:#719e07">==&lt;/span> null &lt;span style="color:#719e07">||&lt;/span> overridden) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//上面扩展对象加载了这么多最终的目的就是将这个扩展类型存放进结果集合中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> extensionClasses.put(name, clazz);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (c &lt;span style="color:#719e07">!=&lt;/span> clazz) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// duplicate implementation is unacceptable
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> unacceptableExceptions.add(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String duplicateMsg &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Duplicate extension &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type.getName() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; name &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; on &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> c.getName() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; and &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> clazz.getName();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.error(duplicateMsg);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException(duplicateMsg);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="53-自适应扩展代理对象的代码生成与编译">5.3 自适应扩展代理对象的代码生成与编译&lt;/h2>
&lt;h3 id="531-自适应扩展对象的创建">5.3.1 自适应扩展对象的创建&lt;/h3>
&lt;p>Dubbo 的&lt;strong>自适应扩展机制&lt;/strong>中如果 &lt;strong>自己生成了自适应扩展的代理类&lt;/strong>&lt;/p>
&lt;p>Dubbo 的自适应扩展为了做什么：&lt;strong>在运行时动态调用扩展方法&lt;/strong>。以及怎么做的：生成扩展代理类。比如: 代理类中根据 URL 获取扩展名，使用 SPI 加载扩展类，并调用同名方法，返回执行结果。&lt;/p>
&lt;p>看了上一个章节,我们了解到了Dubbo是如何通过扫描目录来查询扩展实现类的这一次我们看下扩展类我们找到了之后,如果这个扩展类型未加上这个@Adaptive注解那么是如何创建这个类型的,接下来看createAdaptiveExtensionClass方法,这个方法是借助字节码工具来动态生成所需要的扩展类型的包装类型的代码,这个代码在编译时我们可能看不到,但是在Debug的时候,我们还是可以看到这个对象名字的,但是往往Debug的时候又进不到具体的代码位置,这里可以注意下&lt;/p>
&lt;p>当扩展点的方法被@Adaptive修饰时，在Dubbo初始化扩展点时会自动生成和编译一个动态的Adaptive类。&lt;/p>
&lt;p>下面我们可以以interface org.apache.dubbo.rpc.Protocol 这个协议扩展类型来看 协议扩展类型目前没有一个是带有自适应注解的&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> createAdaptiveExtensionClass&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Adaptive Classes&amp;#39; ClassLoader should be the same with Real SPI interface classes&amp;#39; ClassLoader
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//获取加载器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ClassLoader classLoader &lt;span style="color:#719e07">=&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getClassLoader&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// //native配置 是否为本地镜像(可以参考官方文档:https://dubbo.apache.org/zh/docs/references/graalvm/support-graalv
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>NativeUtils&lt;span style="color:#719e07">.&lt;/span>isNative&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> classLoader&lt;span style="color:#719e07">.&lt;/span>loadClass&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;$Adaptive&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable ignore&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个代码生成器,来生成代码 详细内容我们就下一章来看
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String code &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AdaptiveClassCodeGenerator&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> cachedDefaultName&lt;span style="color:#719e07">).&lt;/span>generate&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取编译器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>compiler&lt;span style="color:#719e07">.&lt;/span>Compiler compiler &lt;span style="color:#719e07">=&lt;/span> extensionDirector&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>compiler&lt;span style="color:#719e07">.&lt;/span>Compiler&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//生成的代码进行编译
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> compiler&lt;span style="color:#719e07">.&lt;/span>compile&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> code&lt;span style="color:#719e07">,&lt;/span> classLoader&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="54-为扩展对象的set方法注入自适应扩展对象">5.4 为扩展对象的set方法注入自适应扩展对象&lt;/h2>
&lt;p>在4.4.5小节中我们已经讲解了获取扩展类型实现类, 创建扩展对象&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> T instance &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> getAdaptiveExtensionClass&lt;span style="color:#719e07">().&lt;/span>newInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>接下来就让我们来看下为扩展对象的set方法注入自适应的扩展对象
调用方法代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注入扩展对象之前的回调方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>injectExtension&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionLoader类型的injectExtension方法具体代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">private&lt;/span> T &lt;span style="color:#268bd2">injectExtension&lt;/span>(T instance) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果注入器为空则直接返回当前对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (injector &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取当前对象的当前类的所有方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> (Method method : instance.getClass().getMethods()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否为set方法 不是的话则跳过,在这里合法的set方法满足3个条件:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//set开头,参数只有一个,public修饰
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>isSetter(method)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Check {@link DisableInject} to see if we need auto injection for this property
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//方法上面是否有注解DisableInject修饰,这种情况也直接跳过
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (method.isAnnotationPresent(DisableInject.&lt;span style="color:#719e07">class&lt;/span>)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//方法的参数如果是原生类型也跳过
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> pt &lt;span style="color:#719e07">=&lt;/span> method.getParameterTypes()[&lt;span style="color:#2aa198">0&lt;/span>];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (ReflectUtils.isPrimitives(pt)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取set方法对应的成员变量如setProtocol 属性为protocol
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String property &lt;span style="color:#719e07">=&lt;/span> getSetterProperty(method);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据参数类型如Protocol和属性名字如protocol获取应该注入的对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Object object &lt;span style="color:#719e07">=&lt;/span> injector.getInstance(pt, property);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (object &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//执行对应对象和对应参数的这个方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> method.invoke(instance, object);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (Exception e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.error(&lt;span style="color:#2aa198">&amp;#34;Failed to inject via method &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> method.getName()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; of interface &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type.getName() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e.getMessage(), e);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (Exception e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.error(e.getMessage(), e);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="541-获取注入对象">5.4.1 获取注入对象&lt;/h3>
&lt;p>这里我们主要来看下如何通过注入器找到需要注入的那个对象 调用代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span> Object object &lt;span style="color:#719e07">=&lt;/span> injector.getInstance(pt, property);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在前面看注入器扩展对象的获取的时候是会获取到ExtensionInjector扩展的一个自适应扩展注入器实现类型 AdaptiveExtensionInjector,这个地方对应的getInstance也是这个扩展里面的,我们来看下它的方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T &lt;span style="color:#268bd2">getInstance&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历所有的扩展注入器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ExtensionInjector injector &lt;span style="color:#719e07">:&lt;/span> injectors&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历所有的扩展注入器,如果可以获取到扩展对象则直接返回
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> T extension &lt;span style="color:#719e07">=&lt;/span> injector&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>extension &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extension&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到上面代码按扩展注入器顺序来遍历的第一个找到的对象就直接返回了,&lt;/p>
&lt;p>这个AdaptiveExtensionInjector在初始化的时候会获取所有的ExtensionInjector的扩展,非自适应的,它本身自适应的扩展,这里会获取非自适应的扩展列表一共有3个按顺序为:&lt;/p>
&lt;ul>
&lt;li>ScopeBeanExtensionInjector&lt;/li>
&lt;li>SpiExtensionInjector&lt;/li>
&lt;li>SpringExtensionInjector&lt;/li>
&lt;/ul>
&lt;p>接下来我们详细看下每种扩展注入器加载扩展对象的策略:&lt;/p>
&lt;h3 id="542-域模型中的bean扩展注入器scopebeanextensioninjector">5.4.2 域模型中的Bean扩展注入器ScopeBeanExtensionInjector&lt;/h3>
&lt;p>ScopeBeanExtensionInjector的getInstance方法:
每个域模型都会有个ScopeBeanFactory类型的对象用于存储共享对象,并且域模型之间按照层级子类型的Bean工厂可以从父域的Bean工厂中查询对象,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>@Override
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T getInstance(Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type, String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> beanFactory.getBean(name, type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ScopeBeanFactory的getBean方法
先从当前域空间查询对象,如果找不到对应类型的扩展对象则从父域工厂查询扩展对象&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T getBean(String name, Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//当前域下注册的扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> T bean &lt;span style="color:#719e07">=&lt;/span> getBeanInternal(name, type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (bean &lt;span style="color:#719e07">==&lt;/span> null &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> parent &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//父域中查找扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> parent.getBean(name, type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> bean;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ScopeBeanFactory的getBeanInternal方法
从当前域下找注册的参数类型的对象&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">private&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T getBeanInternal(String name, Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> checkDestroyed();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// All classes are derived from java.lang.Object, cannot filter bean by it
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (type &lt;span style="color:#719e07">==&lt;/span> Object.&lt;span style="color:#719e07">class&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>BeanInfo&lt;span style="color:#719e07">&amp;gt;&lt;/span> candidates &lt;span style="color:#719e07">=&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> BeanInfo firstCandidate &lt;span style="color:#719e07">=&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历列表查询
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> (BeanInfo beanInfo : registeredBeanInfos) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if required bean type is same class/superclass/interface of the registered bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (type.isAssignableFrom(beanInfo.instance.getClass())) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (StringUtils.isEquals(beanInfo.name, name)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> (T) beanInfo.instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// optimize for only one matched bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (firstCandidate &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> firstCandidate &lt;span style="color:#719e07">=&lt;/span> beanInfo;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (candidates &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> candidates &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> candidates.add(firstCandidate);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> candidates.add(beanInfo);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if bean name not matched and only single candidate
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (candidates &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (candidates.size() &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> (T) candidates.get(&lt;span style="color:#2aa198">0&lt;/span>).instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#268bd2">if&lt;/span> (candidates.size() &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> candidateBeanNames &lt;span style="color:#719e07">=&lt;/span> candidates.stream().map(beanInfo &lt;span style="color:#719e07">-&amp;gt;&lt;/span> beanInfo.name).collect(Collectors.toList());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ScopeBeanException(&lt;span style="color:#2aa198">&amp;#34;expected single matching bean but found &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> candidates.size() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; candidates for type [&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type.getName() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;]: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> candidateBeanNames);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#268bd2">if&lt;/span> (firstCandidate &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> (T) firstCandidate.instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="543-spi扩展机制注入器spiextensioninjector">5.4.3 SPI扩展机制注入器SpiExtensionInjector&lt;/h3>
&lt;p>SPI是Dubbo自行实现的一套扩展机制,我们来看下它是如何查找扩展对象的&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>@Override
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T getInstance(Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type, String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果是一个标准的被@SPI注解修饰的扩展接口则满足条件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (type.isInterface() &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> type.isAnnotationPresent(SPI.&lt;span style="color:#719e07">class&lt;/span>)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用扩展访问器来获取对应类型的扩展加载器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> loader &lt;span style="color:#719e07">=&lt;/span> extensionAccessor.getExtensionLoader(type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (loader &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用对应类型的扩展加载器来加载自适应扩展 这个加载的扩展可以参考4.4.6小节
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>loader.getSupportedExtensions().isEmpty()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> loader.getAdaptiveExtension();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="544-spring扩展注入器">5.4.4 Spring扩展注入器&lt;/h3>
&lt;p>SpringExtensionInjector&lt;/p>
&lt;p>Spring扩展注入器主要是用来从Spring容器中查询当前类型的Bean是否存在的,如下代码直接看代码吧&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>@Override
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @SuppressWarnings(&lt;span style="color:#2aa198">&amp;#34;unchecked&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T getInstance(Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type, String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (context &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore if spring context is not bound
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//check @SPI annotation ,类型需要满足SPI机制 @SPI修饰的接口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (type.isInterface() &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> type.isAnnotationPresent(SPI.&lt;span style="color:#719e07">class&lt;/span>)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从Spring容器中查询Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> T bean &lt;span style="color:#719e07">=&lt;/span> getOptionalBean(context, name, type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (bean &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> bean;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//logger.warn(&amp;#34;No spring extension (bean) named:&amp;#34; + name + &amp;#34;, try to find an extension (bean) of type &amp;#34; + type.getName());
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">private&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T getOptionalBean(ListableBeanFactory beanFactory, String name, Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//要搜索的扩展名字为空就根据类型搜索
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (StringUtils.isEmpty(name)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//返回与给定类型（包括子类）匹配的bean的名称，对于FactoryBeans
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String[] beanNamesForType &lt;span style="color:#719e07">=&lt;/span> beanFactory.getBeanNamesForType(type, &lt;span style="color:#b58900">true&lt;/span>, &lt;span style="color:#b58900">false&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (beanNamesForType &lt;span style="color:#719e07">!=&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (beanNamesForType.length &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//返回指定bean的实例，该实例可以是共享的，也可以是独立的。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//根据Bean Name和类型 查询具体的扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> beanFactory.getBean(beanNamesForType[&lt;span style="color:#2aa198">0&lt;/span>], type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#268bd2">if&lt;/span> (beanNamesForType.length &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException(&lt;span style="color:#2aa198">&amp;#34;Expect single but found &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> beanNamesForType.length &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; beans in spring context: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Arrays.toString(beanNamesForType));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展名字不为空则直接通过名字搜索Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> (beanFactory.containsBean(name)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> beanFactory.getBean(name, type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文： &lt;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方法》&lt;/a>&lt;/p></description></item><item><title>Blog: 04-Dubbo的扩展机制</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/blog/2022/08/04/04-dubbo%E7%9A%84%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6/</guid><description>
&lt;h1 id="4-dubbo的扩展机制">4-Dubbo的扩展机制&lt;/h1>
&lt;h2 id="41-回顾我们前面使用到扩展场景">4.1 回顾我们前面使用到扩展场景&lt;/h2>
&lt;p>在上一章中我们初始化应用模型对象的时候,了解到有几个地方用到了扩展机制来创建对象,这一章我们会详细来讲一下这个扩展对象的加载过程,这里我们先来回顾下哪些地方用到了扩展机制:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 使用扩展机制获取TypeBuilder
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>TypeBuilder&lt;span style="color:#719e07">&amp;gt;&lt;/span> tbs &lt;span style="color:#719e07">=&lt;/span> model&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>TypeBuilder&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//获取域模型初始化器ScopeModelInitializer扩展对象,执行初始化方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializerExtensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializers &lt;span style="color:#719e07">=&lt;/span> initializerExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OrderedPropertiesConfiguration 中获取有序配置提供器对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>OrderedPropertiesProvider&lt;span style="color:#719e07">&amp;gt;&lt;/span> propertiesProviderExtensionLoader &lt;span style="color:#719e07">=&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>OrderedPropertiesProvider&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ApplicationModel中获取配置管理器对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigManager&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ApplicationExt&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>ConfigManager&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ModuleModel中获取模块扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ModuleExt&lt;span style="color:#719e07">&amp;gt;&lt;/span> exts &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ModuleExt&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ApplicationModel中获Environment对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>environment &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Environment&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ApplicationExt&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>Environment&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ApplicationModel中获取应用初始化监听器ApplicationInitListener扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ApplicationInitListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> extensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ApplicationInitListener&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> listenerNames &lt;span style="color:#719e07">=&lt;/span> extensionLoader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensions&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ScopeModel中创建扩展访问器:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>extensionDirector &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ExtensionDirector&lt;span style="color:#719e07">(&lt;/span>parent &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> parent&lt;span style="color:#719e07">.&lt;/span>getExtensionDirector&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span> scope&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>有了以上的应用场景我们可以来看下扩展机制了&lt;/p>
&lt;h2 id="42-为什么要用到扩展机制">4.2 为什么要用到扩展机制?&lt;/h2>
&lt;p>为什么要用到扩展这个想必每个编程人员都比较了解,一个好的程序是要遵循一定的设计规范比如设计模式中的&lt;strong>开闭原则&lt;/strong> 英文全称是 Open Closed Principle，简写为 OCP,对扩展开放、对修改关闭:&lt;/p>
&lt;p>&lt;strong>对扩展开放：&lt;/strong> 指的是我们系统中的模块、类、方法对它们的提供者（开发者）应该是开放的，提供者可以对系统进行扩展（新增）新的功能。&lt;/p>
&lt;p>&lt;strong>对修改关闭：&lt;/strong> 指的是系统中的模块、类、方法对它们的使用者（调用者）应该是关闭的。使用者使用这些功能时，不会因为提供方新增了功能而导致使用者也进行相应修改。&lt;/p>
&lt;p>我们再来了解下Dubbo的一些基本特性:
下面这句话是我摘自官网的:
&lt;em>Apache Dubbo 是一款微服务开发框架，它提供了 &lt;strong>RPC通信&lt;/strong> 与 &lt;strong>微服务治理&lt;/strong> 两大关键能力。这意味着，使用 Dubbo 开发的微服务，将具备相互之间的远程发现与通信能力， 同时利用 Dubbo 提供的丰富服务治理能力，可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的，用户几乎可以在&lt;strong>任意功能点去定制自己的实现&lt;/strong>，以改变框架的默认行为来满足自己的业务需求。
Dubbo3 基于 Dubbo2 演进而来，在保持原有核心功能特性的同时， Dubbo3 在易用性、超大规模微服务实践、云原生基础设施适配、安全设计等几大方向上进行了全面升级。 以下文档都将基于 Dubbo3 展开。&lt;/em>&lt;/p>
&lt;p>&lt;strong>对修改关闭的地方:&lt;/strong> 对于Apache Dubbo来说 不变的是RPC调用流程,微服务治理这些抽象的概念,我们可以用摘自官网的下面几个图表示:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/4-dubbo-arch.png" alt="在这里插入图片描述">&lt;/p>
&lt;center>图4.1 Dubbo架构图&lt;/center>
&lt;p>再来看一个调用链路的架构图&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/4-dubbo-arch2.png" alt="在这里插入图片描述">&lt;/p>
&lt;center>图4.2 Dubbo RPC调用链路&lt;/center>
&lt;br/>
上面两个图整体来看都是Dubbo不变的地方涉及到服务的RPC调用和服务治理的一些概念与流程,但是对于每个环节又可以使用各种方式实现,比如序列化机制可以是Json,Java序列化,Hession2或者Protobuf等等,网络传输层可以是netty实现的tcp通信,也可以使用http协议,那Dubbo又是如何封装不变部分扩展这种可变部分呢?,那就是接下来要说的**微内核机制**,这个我们待会说
&lt;p>&lt;strong>对扩展开放：&lt;/strong> : 对于Apache Dubbo来说 变化的是RPC调用流程和微服务治理这些抽象的概念的具体实现,每个点应该用什么技术实现,又是用什么场景,这个可以用如下图来表示下:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/4-dubbo-arch3.png" alt="在这里插入图片描述">&lt;/p>
&lt;center>图4.3 Dubbo的扩展生态&lt;/center>
&lt;/br>
&lt;p>看到这里 应该各位就明白了,我们写程序是为了业务,而针对不同的业务需求很多场景下我们是需要使用不同的实现来满足的,Dubbo使用微内核的架构,将具体的实现开放出来,让使用者可以根据自己的需求来选择,定制. Dubbo开放了很多的扩展点供大家扩展,可想而知使用Dubbo的灵活性是非常高的。&lt;/p>
&lt;p>&lt;strong>微内核架构:&lt;/strong>
微内核架构由两大架构模块组成：&lt;strong>核心系统&lt;/strong>与&lt;strong>插件模块&lt;/strong>,设计一个微内核体系关键工作全部集中于核心系统怎么构建。
&lt;strong>核心系统&lt;/strong> : 负责和具体业务功能无关的通用功能，例如模块加载、模块间通信等,这个其实对应着Dubbo的SPI机制。
&lt;strong>插件模块&lt;/strong> : 负责实现具体的业务逻辑，Dubbo,SPI接口与实现。&lt;/p>
&lt;h2 id="43-dubbo的扩展机制包含了哪些重要的组成部分">4.3 Dubbo的扩展机制包含了哪些重要的组成部分?&lt;/h2>
&lt;p>前面我们说了为什么要使用扩展机制,这里我们来看下具体实现&lt;/p>
&lt;p>先将扩展包里面的代码截个图认识认识各类型的单词
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/4-dubbo-extension.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>顺便我们先简单看下类结构图,后续再详细看每个类型的解释:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/4-dubbo-extension2.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>为了后续看具体的扩展加载流程我们先看下以上类型的解释说明:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>ExtensionAccessor&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>扩展的统一访问器&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ExtensionDirector&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>ExtensionDirector是一个作用域扩展加载程序管理器。&lt;/li>
&lt;li>ExtensionDirector支持多个级别，子级可以继承父级的扩展实例。
查找和创建扩展实例的方法类似于Java classloader。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ExtensionScope&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>扩展SPI域,目前有FRAMEWORK,APPLICATION,MODULE,SELF&lt;/li>
&lt;li>&lt;strong>FRAMEWORK&lt;/strong> : 扩展实例在框架内使用，与所有应用程序和模块共享。
框架范围SPI扩展只能获取FrameworkModel，无法获取ApplicationModel和ModuleModel。
考虑：
一些SPI需要在框架内的应用程序之间共享数据
无状态SPI在框架内是安全共享的&lt;/li>
&lt;li>&lt;strong>APPLICATION&lt;/strong> 扩展实例在一个应用程序中使用，与应用程序的所有模块共享，不同的应用程序创建不同的扩展实例。
应用范围SPI扩展可以获取FrameworkModel和ApplicationModel，无法获取ModuleModel。
考虑：
在框架内隔离不同应用程序中的扩展数据
在应用程序内部的所有模块之间共享扩展数据&lt;/li>
&lt;li>&lt;strong>MODULE&lt;/strong> 扩展实例在一个模块中使用，不同的模块创建不同的扩展实例。
模块范围SPI扩展可以获得FrameworkModel、ApplicationModel和ModuleModel。
考虑：
隔离应用程序内部不同模块中的扩展数据&lt;/li>
&lt;li>&lt;strong>SELF&lt;/strong> 自给自足，为每个作用域创建一个实例，用于特殊的SPI扩展，如ExtensionInjector&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ExtensionLoader&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>ApplicationModel、DubboBootstrap和这个类目前被设计为单例或静态（本身完全静态或使用一些静态字段）。因此，从它们返回的实例属于process或classloader范围。如果想在一个进程中支持多个dubbo服务器，可能需要重构这三个类。&lt;/li>
&lt;li>加载dubbo扩展&lt;/li>
&lt;li>自动注入依赖项扩展&lt;/li>
&lt;li>包装器中的自动包装扩展&lt;/li>
&lt;li>默认扩展是一个自适应实例&lt;/li>
&lt;li>JDK自带SPI参考地址 &lt;a href="https://docs.oracle.com/javase/1.5.0/docs/guide/jar/jar.html#Service%20Provider">点击查看&lt;/a>&lt;/li>
&lt;li>@SPI 服务扩展接口 详细内容看后面&lt;/li>
&lt;li>@Adaptive自适应扩展点注解 详细内容看后面&lt;/li>
&lt;li>@Activate自动激活扩展点注解 详细内容看后面&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ExtensionPostProcessor&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>在扩展初始化之前或之后调用的后处理器。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>LoadingStrategy&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>扩展加载策略,目前有3个扩展加载策略分别从不同文件目录加载扩展&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>DubboInternalLoadingStrategy&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Dubbo内置的扩展加载策略,将加载文件目录为META-INF/dubbo/internal/的扩展&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>DubboLoadingStrategy&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Dubbo普通的扩展加载策略,将加载目录为META-INF/dubbo/的扩展&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ServicesLoadingStrategy&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>JAVA SPI加载策略 ,将加载目录为META-INF/services/的扩展&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Wrapper&lt;/strong>注解&lt;/p>
&lt;/li>
&lt;li>
&lt;p>SPI注解&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ExtensionInjector&lt;/strong>接口&lt;/p>
&lt;ul>
&lt;li>为SPI扩展提供资源的注入器。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ExtensionAccessorAware&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>SPI扩展可以实现这个感知接口，以获得适当的xtensionAccessor实例。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>DisableInject注解&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>AdaptiveClassCodeGenerator&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>自适应类的代码生成器&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Adaptive&lt;/strong>注解&lt;/p>
&lt;/li>
&lt;li>
&lt;p>为ExtensionLoader注入依赖扩展实例提供有用信息。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Activate&lt;/strong>注解&lt;/p>
&lt;ul>
&lt;li>Activate。此注解对于使用给定条件自动激活某些扩展非常有用，例如：@Activate可用于在有多个实现时加载某些筛选器扩展。
**group()**指定组条件。框架SPI定义了有效的组值。
**value()**指定URL条件中的参数键。
SPI提供程序可以调用ExtensionLoader。getActivateExtension(URL、String、String)方法以查找具有给定条件的所有已激活扩展。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ActivateComparator&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Activate扩展的排序器&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>MultiInstanceActivateComparator&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>WrapperComparator&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>AdaptiveExtensionInjector&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>SpiExtensionInjector&lt;/strong>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="44-扩展加载创建之前的调用过程">4.4 扩展加载创建之前的调用过程&lt;/h2>
&lt;h3 id="441-扩展的调用代码示例">4.4.1 扩展的调用代码示例&lt;/h3>
&lt;p>了解了这么多与扩展相关的概念,接下来我们就来从前面的代码调用中找几个例子来看下扩展的调用过程:&lt;/p>
&lt;p>代码来源于FrameworkModel对象的初始化initialize()中的如下代码调用:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> TypeDefinitionBuilder&lt;span style="color:#719e07">.&lt;/span>initBuilders&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>TypeDefinitionBuilder中初始化类型构建器代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initBuilders&lt;/span>&lt;span style="color:#719e07">(&lt;/span>FrameworkModel model&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>TypeBuilder&lt;span style="color:#719e07">&amp;gt;&lt;/span> tbs &lt;span style="color:#719e07">=&lt;/span> model&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>TypeBuilder&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> BUILDERS &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>tbs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="442-dubbo的分层模型获取扩展加载器对象">4.4.2 Dubbo的分层模型获取扩展加载器对象&lt;/h3>
&lt;p>以上扩展调用的时候对于扩展加载器对象的获取代码如下所示,我们来看下它的调用链路&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>model&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>TypeBuilder&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>getExtensionLoader方法来源于FrameworkModel类型的父类型ScopeModel的实现的接口ExtensionAccessor中的默认方法(JDK8 默认方法)&lt;/p>
&lt;p>ExtensionAccessor接口中的getExtensionLoader方法如下代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getExtensionLoader&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionDirector&lt;span style="color:#719e07">().&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>获取扩展加载器之前需要先获取扩展访问器:
这里的链路先梳理下:&lt;/p>
&lt;p>&lt;strong>模型对象(FrameworkModel)-&lt;/strong>&amp;ndash;&amp;gt; &lt;strong>扩展访问器(ExtensionAccessor)&lt;/strong> &amp;mdash;&amp;gt; &lt;strong>作用域扩展加载程序管理器(ExtensionDirector)&lt;/strong> &amp;mdash;&amp;gt;&lt;/p>
&lt;p>这个getExtensionDirector()方法来源于FrameworkModel的抽象父类型ScopeModel中的getExtensionDirector()如下代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> ExtensionDirector &lt;span style="color:#268bd2">getExtensionDirector&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extensionDirector&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里直接返回了extensionDirector,,不知道介绍到这里记得这个扩展加载程序管理器extensionDirector对象的由来不, 在上个章节&lt;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对象的初始化》&lt;/a>中3.2.2 初始化ScopeModel的章节中的ScopeModel类型的初始化方法initialize()方法中我们提到过这个对象的创建,具体代码如下所示(这个代码比较简单):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>extensionDirector &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ExtensionDirector&lt;span style="color:#719e07">(&lt;/span>parent &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> parent&lt;span style="color:#719e07">.&lt;/span>getExtensionDirector&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span> scope&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们继续前面getExtensionLoader(type)方法调用逻辑,前面我们知道了这个扩展访问器的对象是ExtensionDirector,接下来我们看下ExtensionDirector中获取扩展加载器的代码(如下所示):
在详细介绍扩展加载器对象获取之前我们先来看下当前我们要加载的扩展类型的源码,后续会用到:
我们要加载的扩展类型TypeBuilder接口&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SPI&lt;/span>&lt;span style="color:#719e07">(&lt;/span>scope &lt;span style="color:#719e07">=&lt;/span> ExtensionScope&lt;span style="color:#719e07">.&lt;/span>FRAMEWORK&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">TypeBuilder&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> Prioritized &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Whether the build accept the class passed in.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">accept&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Build type definition with the type or class.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TypeDefinition &lt;span style="color:#268bd2">build&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Type type&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz&lt;span style="color:#719e07">,&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> TypeDefinition&lt;span style="color:#719e07">&amp;gt;&lt;/span> typeCache&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionDirector类型中获取扩展加载器的代码
这个代码非常有意思 &lt;strong>其实就是前面说到的域模型架构的数据访问架构&lt;/strong>类似于JVM类加载器访问加载类的情况,但是这个顺序可能有所不同,Dubbo的扩展加载器是如何访问的呢? 遵循以下顺序:&lt;/p>
&lt;ul>
&lt;li>先从&lt;strong>缓存中&lt;/strong>查询扩展加载器&lt;/li>
&lt;li>如果前面没找到则查询扩展类型的scope所属域,如果是&lt;strong>当前域扩展&lt;/strong>则从直接创建扩展加载器&lt;/li>
&lt;li>如果前面没找到就从&lt;strong>父扩展访问器&lt;/strong>中查询,查询这个扩展是否数据父扩展域&lt;/li>
&lt;li>前面都没找到就尝试创建&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getExtensionLoader&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果扩展加载器已经被销毁则抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里参数类型传的是TypeBuilder.class不为空
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>type &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Extension type == null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展类型不为接口也要抛出异常,这个TypeBuilder.class具体类型代码往上看,这个类型是一个接口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>type&lt;span style="color:#719e07">.&lt;/span>isInterface&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Extension type (&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) is not an interface!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个判断逻辑是判断这个扩展接口是有有@SPI注解,TypeBuilder是有的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>withExtensionAnnotation&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Extension type (&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;) is not an extension, because it is NOT annotated with @&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> SPI&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getSimpleName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 1. find in local cache
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//被加载的扩展类型对应的扩展加载器会放到extensionLoadersMap这个ConcurrentHashMap类型的集合中方便缓存
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> loader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> extensionLoadersMap&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//查询扩展所属域,这个类型的扩展域是框架级别的ExtensionScope.FRAMEWORK
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//extensionScopeMap为ConcurrentHashMap类型的扩展域缓存集合
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionScope scope &lt;span style="color:#719e07">=&lt;/span> extensionScopeMap&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>scope &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SPI annotation &lt;span style="color:#719e07">=&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getAnnotation&lt;span style="color:#719e07">(&lt;/span>SPI&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> scope &lt;span style="color:#719e07">=&lt;/span> annotation&lt;span style="color:#719e07">.&lt;/span>scope&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> extensionScopeMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> scope&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//首次访问的时候当前类型的扩展加载器类型肯定是空的,会走如下两个逻辑中的其中一个进行创建扩展加载器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//1)如果 扩展域为SELF 自给自足，为每个作用域创建一个实例，用于特殊的SPI扩展，如{@link ExtensionInjector}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>loader &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> scope &lt;span style="color:#719e07">==&lt;/span> ExtensionScope&lt;span style="color:#719e07">.&lt;/span>SELF&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// create an instance in self scope
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loader &lt;span style="color:#719e07">=&lt;/span> createExtensionLoader0&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 2. find in parent
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//3) 从父扩展加载器中查询当前扩展加载器是否存在,这里parent是空的先不考虑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>loader &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>parent &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>parent&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 3. create it
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//4) 这个是我们本次会走的逻辑,大部分是会走这个逻辑来创建扩展加载器对象的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>loader &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loader &lt;span style="color:#719e07">=&lt;/span> createExtensionLoader&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> loader&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面提到的withExtensionAnnotation判断代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">withExtensionAnnotation&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> type&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>isAnnotationPresent&lt;span style="color:#719e07">(&lt;/span>SPI&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionDirector类型的createExtensionLoader方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">createExtensionLoader&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> loader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//当前类型注解的scope与当前扩展访问器ExtensionDirector的scope是否一致,不一致则抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//当前类型ExtensionDirector的scope是在构造器中传递的,在Model对象初始化的时候创建的本类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isScopeMatched&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if scope is matched, just create it
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loader &lt;span style="color:#719e07">=&lt;/span> createExtensionLoader0&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if scope is not matched, ignore it
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> loader&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionDirector类型的createExtensionLoader0方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">createExtensionLoader0&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查当前扩展访问器是否被销毁掉了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> loader&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为当前扩展类型创建一个扩展访问器并缓存到,当前成员变量extensionLoadersMap中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> extensionLoadersMap&lt;span style="color:#719e07">.&lt;/span>putIfAbsent&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> scopeModel&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> extensionLoadersMap&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> loader&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="443-扩展加载器对象extensionloader的构造器">4.4.3 扩展加载器对象ExtensionLoader的构造器&lt;/h3>
&lt;p>扩展加载器相对来说是比较复杂的实现内容比较多,用到哪里我们说下哪里,这里先来看ExtensionLoader的构造器代码如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> ExtensionDirector extensionDirector&lt;span style="color:#719e07">,&lt;/span> ScopeModel scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//当前扩展加载器,需要加载的扩展的类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>type &lt;span style="color:#719e07">=&lt;/span> type&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建扩展加载器的扩展访问器对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>extensionDirector &lt;span style="color:#719e07">=&lt;/span> extensionDirector&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从扩展访问器中获取扩展执行前后的回调器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>extensionPostProcessors &lt;span style="color:#719e07">=&lt;/span> extensionDirector&lt;span style="color:#719e07">.&lt;/span>getExtensionPostProcessors&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建实例化对象的策略对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initInstantiationStrategy&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果当前扩展类型为扩展注入器类型则设置当前注入器变量为空,否则的话获取一个扩展注入器扩展对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>injector &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>type &lt;span style="color:#719e07">==&lt;/span> ExtensionInjector&lt;span style="color:#719e07">.&lt;/span>class &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">:&lt;/span> extensionDirector&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ExtensionInjector&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建Activate注解的排序器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>activateComparator &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ActivateComparator&lt;span style="color:#719e07">(&lt;/span>extensionDirector&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为扩展加载器下的域模型对象赋值
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>scopeModel &lt;span style="color:#719e07">=&lt;/span> scopeModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>先来看 创建实例化对象的策略对象代码 initInstantiationStrategy();&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initInstantiationStrategy&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ExtensionPostProcessor extensionPostProcessor &lt;span style="color:#719e07">:&lt;/span> extensionPostProcessors&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//ScopeModelAwareExtensionProcessor在域模型对象时候为扩展访问器添加了这个域模型扩展处理器对象ScopeModelAwareExtensionProcessor,这个类型实现了ScopeModelAccessor域模型访问器可以用来获取域模型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>extensionPostProcessor &lt;span style="color:#719e07">instanceof&lt;/span> ScopeModelAccessor&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instantiationStrategy &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InstantiationStrategy&lt;span style="color:#719e07">((&lt;/span>ScopeModelAccessor&lt;span style="color:#719e07">)&lt;/span> extensionPostProcessor&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instantiationStrategy &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instantiationStrategy &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InstantiationStrategy&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>再来看ExtensionInjector扩展对象的获取&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//1)这里有个type为空的判断,普通的扩展类型肯定不是ExtensionInjector类型 这里必定会为每个非扩展注入ExtensionInjector类型创建一个ExtensionInjector类型的扩展对象,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//2) 这里代码会走extensionDirector.getExtensionLoader(ExtensionInjector.class)这一步进去之后的代码刚刚看过就不再看了,这个代码会创建一个为ExtensionInjector扩展对象的加载器对象ExtensionLoader
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//3) getAdaptiveExtension() 这个方法就是通过扩展加载器获取具体的扩展对象的方法我们会详细说
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>injector &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>type &lt;span style="color:#719e07">==&lt;/span> ExtensionInjector&lt;span style="color:#719e07">.&lt;/span>class &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">:&lt;/span> extensionDirector&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ExtensionInjector&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文： &lt;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/">&amp;laquo;Dubbo的扩展机制&amp;raquo;&lt;/a>&lt;/p></description></item><item><title>Blog: 03-框架,应用程序,模块领域模型Model对象的初始化</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="3-框架应用程序模块领域模型model对象的初始化">3-框架,应用程序,模块领域模型Model对象的初始化&lt;/h1>
&lt;p>在上一章中我们详细看了服务配置ServiceConfig类型的初始化，不过我们跳过了AbstractMethodConfig的构造器中创建模块模型对象的过程，那这一章我们就来看下模块模型对象的初始化过程:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractMethodConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel&lt;span style="color:#719e07">.&lt;/span>defaultModel&lt;span style="color:#719e07">().&lt;/span>getDefaultModule&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>那为什么会在Dubbo3的新版本中加入这个域模型呢&lt;/strong>，主要有如下原因
之前dubbo都是只有一个作用域的，通过静态类 属性共享
增加域模型是为了:&lt;/p>
&lt;ol>
&lt;li>让Dubbo支持多应用的部署，这块一些大企业有诉求&lt;/li>
&lt;li>从架构设计上，解决静态属性资源共享、清理的问题&lt;/li>
&lt;li>分层模型将应用的管理和服务的管理分开&lt;/li>
&lt;/ol>
&lt;p>可能比较抽象，可以具体点来看。Dubbo3中在启动时候需要启动配置中心、元数据中心，这个配置中心和元数据中心可以归应用模型来管理。Dubbo作为RPC框架又需要启动服务和引用服务，服务级别的管理就交给了这个模块模型来管理。分层次的管理方便我们理解和处理逻辑，父子级别的模型又方便了数据传递。&lt;/p>
&lt;p>了解过JVM类加载机制的同学应该就比较清楚JVM类加载过程中的数据访问模型。子类加载器先交给父类加载器查找，找不到再从子类加载器中查找。Dubbo的分层模型类似这样一种机制，这一章先来简单了解下，后面用到时候具体细说。&lt;/p>
&lt;h2 id="31-模型对象的关系">3.1 模型对象的关系&lt;/h2>
&lt;p>为了不增加复杂性，我们这里仅仅列出模型对象类型类型之间的继承关系如下所示:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/3-model.png" alt="在这里插入图片描述">&lt;/p>
&lt;center>图3.1 模型对象的继承关系&lt;/center>
&lt;p>模型对象一共有4个，公共的属性和操作放在了域模型类型中，下面我们来详细说下这几个模型类型:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>ExtensionAccessor&lt;/strong> 扩展的统一访问器&lt;/p>
&lt;ul>
&lt;li>用于获取扩展加载管理器ExtensionDirector对象&lt;/li>
&lt;li>&lt;strong>获取扩展对象ExtensionLoader&lt;/strong>&lt;/li>
&lt;li>根据扩展名字&lt;strong>获取具体扩展对象&lt;/strong>&lt;/li>
&lt;li>获取自适应扩展对象&lt;/li>
&lt;li>获取默认扩展对象&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ScopeModel&lt;/strong> 模型对象的公共抽象父类型&lt;/p>
&lt;ul>
&lt;li>内部id用于表示模型树的层次结构&lt;/li>
&lt;li>公共模型名称，可以被用户设置&lt;/li>
&lt;li>描述信息&lt;/li>
&lt;li>类加载器管理&lt;/li>
&lt;li>父模型管理parent&lt;/li>
&lt;li>当前模型的所属域ExtensionScope有:&lt;strong>FRAMEWORK(框架)&lt;/strong>，&lt;strong>APPLICATION(应用)&lt;/strong>，&lt;strong>MODULE(模块)&lt;/strong>，&lt;strong>SELF(自给自足&lt;/strong>，为每个作用域创建一个实例，用于特殊的SPI扩展，如ExtensionInjector)&lt;/li>
&lt;li>具体的扩展加载程序管理器对象的管理:&lt;strong>ExtensionDirector&lt;/strong>&lt;/li>
&lt;li>域Bean工厂管理，一个内部共享的Bean工厂&lt;strong>ScopeBeanFactory&lt;/strong>&lt;/li>
&lt;li>等等&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>FrameworkModel&lt;/strong> dubbo框架模型，可与多个应用程序共享&lt;/p>
&lt;ul>
&lt;li>FrameworkModel实例对象集合，allInstances&lt;/li>
&lt;li>所有ApplicationModel实例对象集合，applicationModels&lt;/li>
&lt;li>发布的ApplicationModel实例对象集合pubApplicationModels&lt;/li>
&lt;li>框架的服务存储库&lt;strong>FrameworkServiceRepository&lt;/strong>类型对象(数据存储在内存中)&lt;/li>
&lt;li>内部的应用程序模型对象internalApplicationModel&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ApplicationModel&lt;/strong> 表示正在使用Dubbo的应用程序，并存储基本&lt;strong>元数据信息&lt;/strong>，以便在RPC调用过程中使用。
ApplicationModel包括许多关于&lt;strong>发布服务&lt;/strong>的ProviderModel和许多关于订阅服务的Consumer Model。&lt;/p>
&lt;ul>
&lt;li>ExtensionLoader、DubboBootstrap和这个类目前被设计为单例或静态（本身完全静态或使用一些静态字段）。因此，从它们返回的实例属于流程范围。如果想在一个进程中支持多个dubbo服务器，可能需要重构这三个类。&lt;/li>
&lt;li>&lt;strong>所有ModuleModel实例&lt;/strong>对象集合moduleModels&lt;/li>
&lt;li>&lt;strong>发布的ModuleModel实例&lt;/strong>对象集合pubModuleModels&lt;/li>
&lt;li>&lt;strong>环境信息Environment实例&lt;/strong>对象environment&lt;/li>
&lt;li>&lt;strong>配置管理ConfigManager实例&lt;/strong>对象configManager&lt;/li>
&lt;li>&lt;strong>服务存储库ServiceRepository实例&lt;/strong>对象serviceRepository&lt;/li>
&lt;li>&lt;strong>应用程序部署器ApplicationDeployer实例&lt;/strong>对象deployer&lt;/li>
&lt;li>&lt;strong>所属框架FrameworkModel实例&lt;/strong>对象frameworkModel&lt;/li>
&lt;li>&lt;strong>内部的模块模型ModuleModel实例&lt;/strong>对象internalModule&lt;/li>
&lt;li>&lt;strong>默认的模块模型ModuleModel实例&lt;/strong>对象defaultModule&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ModuleModel&lt;/strong> 服务模块的模型&lt;/p>
&lt;ul>
&lt;li>&lt;strong>所属应用程序模型ApplicationModel实例&lt;/strong>对象applicationModel&lt;/li>
&lt;li>&lt;strong>模块环境信息ModuleEnvironment实例&lt;/strong>对象moduleEnvironment&lt;/li>
&lt;li>&lt;strong>模块服务存储库ModuleServiceRepository实例&lt;/strong>对象serviceRepository&lt;/li>
&lt;li>&lt;strong>模块的服务配置管理ModuleConfigManager实例&lt;/strong>对象moduleConfigManager&lt;/li>
&lt;li>&lt;strong>模块部署器ModuleDeployer实例&lt;/strong>对象deployer用于导出和引用服务&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>了解了这几个模型对象的关系我们可以了解到这几个模型对象的管理层级从框架到应用程序，然后到模块的管理(FrameworkModel-&amp;gt;ApplicationModel-&amp;gt;ModuleModel)，他们主要用来针对框架，应用程序，模块的&lt;strong>存储&lt;/strong>，&lt;strong>发布管理，&lt;/strong>，&lt;strong>配置管理&lt;/strong>&lt;/p>
&lt;p>看来Dubbo3 针对应用服务治理与运维这一块也是在努力尝试.&lt;/p>
&lt;h3 id="311-abstractmethodconfig-配置对象中获取模型对象的调用">3.1.1 AbstractMethodConfig 配置对象中获取模型对象的调用&lt;/h3>
&lt;p>模块模型(ModuleModel)参数对象的创建
这个AbstractMethodConfig构造器在初始化的时候调调用了这么一行代码做为参数向父类型传递对象.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ApplicationModel&lt;span style="color:#719e07">.&lt;/span>defaultModel&lt;span style="color:#719e07">().&lt;/span>getDefaultModule&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>默认情况下使用ApplicationModel的静态方法获取默认的模型对象和默认的模块对象&lt;/p>
&lt;p>&lt;strong>ApplicationModel&lt;/strong>(应用程序领域模型)类型中获取默认模型对象的方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> ApplicationModel &lt;span style="color:#268bd2">defaultModel&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// should get from default FrameworkModel, avoid out of sync
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> FrameworkModel&lt;span style="color:#719e07">.&lt;/span>defaultModel&lt;span style="color:#719e07">().&lt;/span>defaultApplication&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里可以看到要想获取应用程序模型必须先通过框架领域模型来获取层级也是框架领域模型到应用程序领域模型&lt;/p>
&lt;h3 id="312-使用双重校验锁获取框架模型对象">3.1.2 使用双重校验锁获取框架模型对象&lt;/h3>
&lt;p>FrameworkModel(框架模型)的默认模型获取工厂方法defaultModel()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 在源码的注释上有这么一句话:在销毁默认的 FrameworkModel 时， FrameworkModel.defaultModel()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *或ApplicationModel.defaultModel() 将返回一个损坏的模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *可能会导致不可预知的问题。建议：尽量避免使用默认模型。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> FrameworkModel &lt;span style="color:#268bd2">defaultModel&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//双重校验锁的形式创建单例对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> FrameworkModel instance &lt;span style="color:#719e07">=&lt;/span> defaultInstance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>globalLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//重置默认框架模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> resetDefaultFrameworkModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>defaultInstance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> defaultInstance &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> FrameworkModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> defaultInstance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>notNull&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Default FrameworkModel is null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="313-刷新重置默认框架模型对象">3.1.3 刷新重置默认框架模型对象&lt;/h3>
&lt;p>FrameworkModel中的重置默认框架模型resetDefaultFrameworkModel&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">resetDefaultFrameworkModel&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//全局悲观锁，同一个时刻只能有一个线程执行重置操作
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>globalLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//defaultInstance为当前成员变量FrameworkModel类型代表当前默认的FrameworkModel类型的实例对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>defaultInstance &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>defaultInstance&lt;span style="color:#719e07">.&lt;/span>isDestroyed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FrameworkModel oldDefaultFrameworkModel &lt;span style="color:#719e07">=&lt;/span> defaultInstance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//存在实例模型列表则直接从内存缓存中查后续不需要创建了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>allInstances&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//当前存在的有FrameworkModel框架实例多个列表则取第一个为默认的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> defaultInstance &lt;span style="color:#719e07">=&lt;/span> allInstances&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> defaultInstance &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>oldDefaultFrameworkModel &lt;span style="color:#719e07">!=&lt;/span> defaultInstance&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>LOGGER&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LOGGER&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Reset global default framework from &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> safeGetModelDesc&lt;span style="color:#719e07">(&lt;/span>oldDefaultFrameworkModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> safeGetModelDesc&lt;span style="color:#719e07">(&lt;/span>defaultInstance&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面单例做了很多的初始化操作，这里开始调用构造器来创建框架模型对象，如下代码:&lt;/p>
&lt;h2 id="32--创建frameworkmodel对象">3.2 创建FrameworkModel对象&lt;/h2>
&lt;p>FrameworkModel()构造器&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">FrameworkModel&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用父类型ScopeModel传递参数，这个构造器的第一个参数为空代表这是一个顶层的域模型，第二个代表了这个是框架FRAMEWORK域，第三个false不是内部域
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ExtensionScope&lt;span style="color:#719e07">.&lt;/span>FRAMEWORK&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//内部id用于表示模型树的层次结构，如层次结构:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//FrameworkModel（索引=1）-&amp;gt;ApplicationModel（索引=2）-&amp;gt;ModuleModel（索引=1，第一个用户模块）
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这个index变量是static类型的为静态全局变量默认值从1开始，如果有多个框架模型对象则internalId编号从1开始依次递增
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>setInternalId&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>valueOf&lt;span style="color:#719e07">(&lt;/span>index&lt;span style="color:#719e07">.&lt;/span>getAndIncrement&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register FrameworkModel instance early
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//将当前新创建的框架实例对象添加到容器中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>globalLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将当前框架模型实例添加到所有框架模型缓存对象中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> allInstances&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如上面代码所示重置默认的框架模型对象，这里将会是缓存实例列表的第一个，新增了一个刷新默认实例对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> resetDefaultFrameworkModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>LOGGER&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LOGGER&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>getDesc&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; is created&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化框架模型领域对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ExtensionScope.FRAMEWORK&lt;/p>
&lt;h3 id="321-初始化frameworkmodel">3.2.1 初始化FrameworkModel&lt;/h3>
&lt;p>FrameworkModel框架模型的初始化方法initialize()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里初始化之前先调用下父类型ScopeModel的初始化方法我们在下面来看
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用TypeDefinitionBuilder的静态方法initBuilders来初始化类型构建器TypeBuilder类型集合
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> TypeDefinitionBuilder&lt;span style="color:#719e07">.&lt;/span>initBuilders&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//框架服务存储仓库对象，可以用于快速查询服务提供者信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceRepository &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> FrameworkServiceRepository&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取ScopeModelInitializer类型(域模型初始化器)的扩展加载器ExtensionLoader，每个扩展类型都会创建一个扩展加载器缓存起来
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializerExtensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取ScopeModelInitializer类型的支持的扩展集合，这里当前版本存在8个扩展类型实现
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializers &lt;span style="color:#719e07">=&lt;/span> initializerExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历这些扩展实现调用他们的initializeFrameworkModel方法来传递FrameworkModel类型对象，细节我们待会再详细说下
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ScopeModelInitializer initializer &lt;span style="color:#719e07">:&lt;/span> initializers&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initializer&lt;span style="color:#719e07">.&lt;/span>initializeFrameworkModel&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个内部的ApplicationModel类型，细节下面说
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> internalApplicationModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationModel&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建ApplicationConfig类型对象同时传递应用程序模型对象internalApplicationModel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//获取ConfigManager类型对象，然后设置添加当前应用配置对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> internalApplicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">().&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>internalApplicationModel&lt;span style="color:#719e07">,&lt;/span> CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_INTERNAL_APPLICATION&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//设置公开的模块名字为常量DUBBO_INTERNAL_APPLICATION
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> internalApplicationModel&lt;span style="color:#719e07">.&lt;/span>setModelName&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_INTERNAL_APPLICATION&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续上面代码的调用链路，我们来看
FrameworkModel的super.initialize();方法 调用父类型ScopeModel的initialize()方法&lt;/p>
&lt;h3 id="322-初始化scopemodel">3.2.2 初始化ScopeModel&lt;/h3>
&lt;p>ScopeModel类型的初始化方法initialize():&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化ExtensionDirector是一个作用域扩展加载程序管理器。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//ExtensionDirector支持多个级别，子级可以继承父级的扩展实例。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//查找和创建扩展实例的方法类似于Java classloader。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>extensionDirector &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ExtensionDirector&lt;span style="color:#719e07">(&lt;/span>parent &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> parent&lt;span style="color:#719e07">.&lt;/span>getExtensionDirector&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span> scope&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个参考了Spring的生命周期回调思想，添加一个扩展初始化的前后调用的处理器，在扩展初始化之前或之后调用的后处理器，参数类型为ExtensionPostProcessor
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>extensionDirector&lt;span style="color:#719e07">.&lt;/span>addExtensionPostProcessor&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ScopeModelAwareExtensionProcessor&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个内部共享的域工厂对象，用于注册Bean，创建Bean，获取Bean，初始化Bean等
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>beanFactory &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ScopeBeanFactory&lt;span style="color:#719e07">(&lt;/span>parent &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> parent&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span> extensionDirector&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用数据结构链表，创建销毁监听器容器，一般用于关闭进程，重置应用程序对象等操作时候调用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>destroyListeners &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> LinkedList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用ConcurrentHashMap属性集合
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>attributes &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用ConcurrentHashSet存储当前域下的类加载器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>classLoaders &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashSet&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Add Framework&amp;#39;s ClassLoader by default
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//将当前类的加载器存入加载器集合classLoaders中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ClassLoader dubboClassLoader &lt;span style="color:#719e07">=&lt;/span> ScopeModel&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getClassLoader&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>dubboClassLoader &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>addClassLoader&lt;span style="color:#719e07">(&lt;/span>dubboClassLoader&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="323-初始类型定义构建器">3.2.3 初始类型定义构建器&lt;/h3>
&lt;p>TypeDefinitionBuilder的初始化类型构造器方法initBuilders&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initBuilders&lt;/span>&lt;span style="color:#719e07">(&lt;/span>FrameworkModel model&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>TypeBuilder&lt;span style="color:#719e07">&amp;gt;&lt;/span> tbs &lt;span style="color:#719e07">=&lt;/span> model&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>TypeBuilder&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> BUILDERS &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>tbs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3231-服务存储仓库对象的创建">3.2.3.1 服务存储仓库对象的创建&lt;/h4>
&lt;p>FrameworkServiceRepository对象的初始化&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">FrameworkServiceRepository&lt;/span>&lt;span style="color:#719e07">(&lt;/span>FrameworkModel frameworkModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>frameworkModel &lt;span style="color:#719e07">=&lt;/span> frameworkModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="324--域模型初始化器的获取与初始化回调">3.2.4 域模型初始化器的获取与初始化回调&lt;/h3>
&lt;p>域模型初始化器的获取与初始化(ScopeModelInitializer类型和initializeFrameworkModel方法)
加载到的ScopeModelInitializer类型的SPI扩展实现&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializerExtensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取ScopeModelInitializer类型的支持的扩展集合，这里当前版本存在8个扩展类型实现
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializers &lt;span style="color:#719e07">=&lt;/span> initializerExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//遍历这些扩展实现调用他们的initializeFrameworkModel方法来传递FrameworkModel类型对象，细节我们待会再详细说下
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ScopeModelInitializer initializer &lt;span style="color:#719e07">:&lt;/span> initializers&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initializer&lt;span style="color:#719e07">.&lt;/span>initializeFrameworkModel&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过Debug查到域模型初始化器的SPI扩展类型有如下8个:&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/3-initextent.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>这里我随机找两个说一下吧:
容错域模型初始化器:ClusterScopeModelInitializer的initializeFrameworkModel方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ClusterScopeModelInitializer&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> ScopeModelInitializer &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initializeFrameworkModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>FrameworkModel frameworkModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ScopeBeanFactory beanFactory &lt;span style="color:#719e07">=&lt;/span> frameworkModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> beanFactory&lt;span style="color:#719e07">.&lt;/span>registerBean&lt;span style="color:#719e07">(&lt;/span>RouterSnapshotSwitcher&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">CommonScopeModelInitializer&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> ScopeModelInitializer &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initializeFrameworkModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>FrameworkModel frameworkModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ScopeBeanFactory beanFactory &lt;span style="color:#719e07">=&lt;/span> frameworkModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> beanFactory&lt;span style="color:#719e07">.&lt;/span>registerBean&lt;span style="color:#719e07">(&lt;/span>FrameworkExecutorRepository&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ConfigScopeModelInitializer&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> ScopeModelInitializer &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initializeFrameworkModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>FrameworkModel frameworkModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> frameworkModel&lt;span style="color:#719e07">.&lt;/span>addDestroyListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> FrameworkModelCleaner&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="325-将内部应用配置对象创建与添加至应用模型中">3.2.5 将内部应用配置对象创建与添加至应用模型中&lt;/h3>
&lt;p>创建ApplicationConfig对象让后将其添加至应用模型中
内部应用程序模型，这里为应用配置管理器设置一个应用配置对象，将这个应用配置的模块名字配置名字设置为DUBBO_INTERNAL_APPLICATION，应用配置记录着我们常见的应用配置信息，如下面表格所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取ConfigManager类型对象，然后设置添加当前应用配置对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> internalApplicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">().&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>internalApplicationModel&lt;span style="color:#719e07">,&lt;/span> CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_INTERNAL_APPLICATION&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//设置公开的模块名字为常量DUBBO_INTERNAL_APPLICATION
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> internalApplicationModel&lt;span style="color:#719e07">.&lt;/span>setModelName&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_INTERNAL_APPLICATION&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>来自官网目前版本的配置解释:
官网当前的配置描述知道到了元数据类型，后面我再补充几个&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性&lt;/th>
&lt;th>对应URL参数&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>是否必填&lt;/th>
&lt;th>缺省值&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>描述&lt;/th>
&lt;th>兼容性&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>name&lt;/td>
&lt;td>application&lt;/td>
&lt;td>string&lt;/td>
&lt;td>必填&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>当前应用名称，用于注册中心计算应用间依赖关系，注意：消费者和提供者应用名不要一样，此参数不是匹配条件，你当前项目叫什么名字就填什么，和提供者消费者角色无关，比如：kylin应用调用了morgan应用的服务，则kylin项目配成kylin，morgan项目配成morgan，可能kylin也提供其它服务给别人使用，但kylin项目永远配成kylin，这样注册中心将显示kylin依赖于morgan&lt;/td>
&lt;td>1.0.16以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>version&lt;/td>
&lt;td>application.version&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>当前应用的版本&lt;/td>
&lt;td>2.2.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>owner&lt;/td>
&lt;td>owner&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>应用负责人，用于服务治理，请填写负责人公司邮箱前缀&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>organization&lt;/td>
&lt;td>organization&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>组织名称(BU或部门)，用于注册中心区分服务来源，此配置项建议不要使用autoconfig，直接写死在配置中，比如china,intl,itu,crm,asc,dw,aliexpress等&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>architecture&lt;/td>
&lt;td>architecture&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>用于服务分层对应的架构。如，intl、china。不同的架构使用不同的分层。&lt;/td>
&lt;td>2.0.7以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>environment&lt;/td>
&lt;td>environment&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>应用环境，如：develop/test/product，不同环境使用不同的缺省值，以及作为只用于开发测试功能的限制条件&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>compiler&lt;/td>
&lt;td>compiler&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>javassist&lt;/td>
&lt;td>性能优化&lt;/td>
&lt;td>Java字节码编译器，用于动态类的生成，可选：jdk或javassist&lt;/td>
&lt;td>2.1.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>logger&lt;/td>
&lt;td>logger&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>slf4j&lt;/td>
&lt;td>性能优化&lt;/td>
&lt;td>日志输出方式，可选：slf4j,jcl,log4j,log4j2,jdk&lt;/td>
&lt;td>2.2.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>metadata-type&lt;/td>
&lt;td>metadata-type&lt;/td>
&lt;td>String&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>local&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>metadata 传递方式，是以 Provider 视角而言的，Consumer 侧配置无效，可选值有：remote - Provider 把 metadata 放到远端注册中心，Consumer 从注册中心获取local - Provider 把 metadata 放在本地，Consumer 从 Provider 处直接获取&lt;/td>
&lt;td>2.7.6以上版本&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>当前在Dubbo3.0.7中还有一些的配置我下面列举下:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性&lt;/th>
&lt;th>对应URL参数&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>是否必填&lt;/th>
&lt;th>缺省值&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>描述&lt;/th>
&lt;th>兼容性&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>register-consumer&lt;/td>
&lt;td>register-consumer&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>是否注册使用者实例，默认为false。&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>register-mode&lt;/td>
&lt;td>register-mode&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>all&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>将interface/instance/all 地址注册到注册中心，默认为all。&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>enable-empty-protection&lt;/td>
&lt;td>enable-empty-protection&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>在空地址通知上启用空保护，默认为true&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>protocol&lt;/td>
&lt;td>protocol&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>dubbo&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>此应用程序的首选协议（名称）&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="33-创建applicationmodel对象">3.3 创建ApplicationModel对象&lt;/h2>
&lt;p>ApplicationModel对象的初始化调用
在前面 3.2.4 FrameworkModel框架模型的初始化方法initialize() 章节中，我们看到了代码ApplicationModel对象的初始化调用如下代码，这里我们来详细说一下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> internalApplicationModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationModel&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> internalApplicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">().&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>internalApplicationModel&lt;span style="color:#719e07">,&lt;/span> CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_INTERNAL_APPLICATION&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> internalApplicationModel&lt;span style="color:#719e07">.&lt;/span>setModelName&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_INTERNAL_APPLICATION&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="331-applicationmodel的构造器">3.3.1 ApplicationModel的构造器&lt;/h3>
&lt;p>ApplicationModel(FrameworkModel frameworkModel, boolean isInternal)
刚刚3.2.9那个地方我们看到了使用代码&lt;strong>new ApplicationModel(this, true)&lt;/strong> 来创建对象这里我们详细看下代码细节:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ApplicationModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>FrameworkModel frameworkModel&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> isInternal&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用父类型ScopeModel传递参数，这个构造器的传递没与前面看到的FrameworkModel构造器的中的调用参数有些不同第一个参数我们为frameworkModel代表父域模型，第二个参数标记域为应用程序级别APPLICATION，第三个参数我们传递的为true代表为内部域
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>frameworkModel&lt;span style="color:#719e07">,&lt;/span> ExtensionScope&lt;span style="color:#719e07">.&lt;/span>APPLICATION&lt;span style="color:#719e07">,&lt;/span> isInternal&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>notNull&lt;span style="color:#719e07">(&lt;/span>frameworkModel&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;FrameworkModel can not be null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//应用程序域成员变量记录frameworkModel对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>frameworkModel &lt;span style="color:#719e07">=&lt;/span> frameworkModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//frameworkModel对象添加当前应用程序域对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> frameworkModel&lt;span style="color:#719e07">.&lt;/span>addApplication&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>LOGGER&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LOGGER&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>getDesc&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; is created&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化应用程序
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3311-将applicationmodel添加至frameworkmodel容器中">3.3.1.1 将ApplicationModel添加至FrameworkModel容器中&lt;/h4>
&lt;p>FrameworkModel的添加应用程序方法addApplication:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addApplication&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// can not add new application if it&amp;#39;s destroying
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//检查FrameworkModel对象是否已经被标记为销毁状态，如果已经被销毁了则抛出异常无需执行逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果还未添加过当前参数传递应用模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModels&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为当前应用模型生成内部id
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>setInternalId&lt;span style="color:#719e07">(&lt;/span>buildInternalId&lt;span style="color:#719e07">(&lt;/span>getInternalId&lt;span style="color:#719e07">(),&lt;/span> appIndex&lt;span style="color:#719e07">.&lt;/span>getAndIncrement&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//添加到成员变量集合applicationModels中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModels&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果非内部的则也向公开应用模型集合pubApplicationModels中添加一下
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>isInternal&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>pubApplicationModels&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resetDefaultAppModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>内部id生成算法buildInternalId方法代码如下:
看代码胜过，文字解释&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> String &lt;span style="color:#268bd2">buildInternalId&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String parentInternalId&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">long&lt;/span> childIndex&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// FrameworkModel 1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// ApplicationModel 1.1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// ModuleModel 1.1.1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>hasText&lt;span style="color:#719e07">(&lt;/span>parentInternalId&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> parentInternalId &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;.&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> childIndex&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> childIndex&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>重置默认的应用模型对象&lt;/strong>
FrameworkModel 重置默认的应用模型对象 resetDefaultAppModel()方法
与默认框架模型设置方式类似取集合的第一个，这里应用模型需要使用公开的应用模型的第一个做为默认应用模型，代码如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">resetDefaultAppModel&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>defaultAppModel &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>defaultAppModel&lt;span style="color:#719e07">.&lt;/span>isDestroyed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//取第一个公开的应用模型做为默认应用模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationModel oldDefaultAppModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>defaultAppModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>pubApplicationModels&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>defaultAppModel &lt;span style="color:#719e07">=&lt;/span> pubApplicationModels&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>defaultAppModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>defaultInstance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#719e07">this&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> oldDefaultAppModel &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>defaultAppModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>LOGGER&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LOGGER&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Reset global default application from &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> safeGetModelDesc&lt;span style="color:#719e07">(&lt;/span>oldDefaultAppModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> safeGetModelDesc&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>defaultAppModel&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="332-初始化applicationmodel">3.3.2 初始化ApplicationModel&lt;/h3>
&lt;p>ApplicationModel的初始化initialize()方法
在前面&lt;strong>3.2.10 ApplicationModel的构造器ApplicationModel(FrameworkModel frameworkModel, boolean isInternal)&lt;/strong> 中的最后一行开始初始化应用模型我们还未详细说明下面可以来看下&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个是调用域模型来初始化基础信息如扩展访问器等，可以参考 3.2.5 ScopeModel类型的初始化方法initialize()章节
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个内部的模块模型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> internalModule &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ModuleModel&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个独立服务存储对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceRepository &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceRepository&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取应用程序初始化监听器ApplicationInitListener扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ApplicationInitListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> extensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ApplicationInitListener&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果存在应用程序初始化监听器扩展则执行这个初始化方法，在当前的版本还未看到有具体的扩展实现类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> listenerNames &lt;span style="color:#719e07">=&lt;/span> extensionLoader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensions&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String listenerName &lt;span style="color:#719e07">:&lt;/span> listenerNames&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> extensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>listenerName&lt;span style="color:#719e07">).&lt;/span>init&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化扩展(这个是应用程序生命周期的方法调用，这里调用初始化方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initApplicationExts&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取域模型初始化器扩展对象列表，然后执行初始化方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializerExtensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializers &lt;span style="color:#719e07">=&lt;/span> initializerExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ScopeModelInitializer initializer &lt;span style="color:#719e07">:&lt;/span> initializers&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initializer&lt;span style="color:#719e07">.&lt;/span>initializeApplicationModel&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="334-initapplicationexts--初始化应用程序扩展方法">3.3.4 initApplicationExts() 初始化应用程序扩展方法&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initApplicationExts&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个扩展实现一共有两个可以看下面那个图扩展类型为ConfigManager和Environment
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ApplicationExt&lt;span style="color:#719e07">&amp;gt;&lt;/span> exts &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ApplicationExt&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ApplicationExt ext &lt;span style="color:#719e07">:&lt;/span> exts&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ext&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/3-extension.png" alt="在这里插入图片描述">&lt;/p>
&lt;h4 id="3341-configmanager类型的initialize方法">3.3.4.1 ConfigManager类型的initialize方法&lt;/h4>
&lt;p>先简单说下ConfigManager的作用，无锁配置管理器（通过ConcurrentHashMap），用于快速读取操作。写入操作锁带有配置类型的子配置映射，用于安全检查和添加新配置。
其实ConfigManager实现类中并没有这个初始化方法initialize，不过ConfigManager的父类型AbstractConfigManager中是有initialize方法的，如下所示:&lt;/p>
&lt;p>AbstractConfigManager的初始化方法initialize&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//乐观锁判断是否初始化过
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>initialized&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从模块环境中获取组合配置，目前Environment中有6种重要的配置，我们后面详细说
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> CompositeConfiguration configuration &lt;span style="color:#719e07">=&lt;/span> scopeModel&lt;span style="color:#719e07">.&lt;/span>getModelEnvironment&lt;span style="color:#719e07">().&lt;/span>getConfiguration&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// dubbo.config.mode获取配置模式，配置模式对应枚举类型ConfigMode，目前有这么几个STRICT，OVERRIDE，OVERRIDE_ALL，OVERRIDE_IF_ABSENT，IGNORE，这个配置决定了属性覆盖的顺序，当有同一个配置key多次出现时候，以最新配置为准，还是以最老的那个配置为准，还是配置重复则抛出异常，默认值为严格模式STRICT重复则抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String configModeStr &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">)&lt;/span> configuration&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_MODE&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>hasText&lt;span style="color:#719e07">(&lt;/span>configModeStr&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>configMode &lt;span style="color:#719e07">=&lt;/span> ConfigMode&lt;span style="color:#719e07">.&lt;/span>valueOf&lt;span style="color:#719e07">(&lt;/span>configModeStr&lt;span style="color:#719e07">.&lt;/span>toUpperCase&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String msg &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Illegal &amp;#39;&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_MODE &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#39; config value [&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> configModeStr &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;], available values &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> Arrays&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">(&lt;/span>ConfigMode&lt;span style="color:#719e07">.&lt;/span>values&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>msg&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>msg&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// dubbo.config.ignore-duplicated-interface
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//忽略重复的接口（服务/引用）配置。默认值为false
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String ignoreDuplicatedInterfaceStr &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">)&lt;/span> configuration
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ignoreDuplicatedInterfaceStr &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>ignoreDuplicatedInterface &lt;span style="color:#719e07">=&lt;/span> Boolean&lt;span style="color:#719e07">.&lt;/span>parseBoolean&lt;span style="color:#719e07">(&lt;/span>ignoreDuplicatedInterfaceStr&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// print 打印配置信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> map &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> LinkedHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> map&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_MODE&lt;span style="color:#719e07">,&lt;/span> configMode&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> map&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>ignoreDuplicatedInterface&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Config settings: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> map&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3342-environment类型的initialize方法">3.3.4.2 Environment类型的initialize方法&lt;/h4>
&lt;p>这是一个与环境配置有关系的类型，我们先来简单了解下它的初始化方法，后期再详细说明:&lt;/p>
&lt;p>Environment类型的initialize方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//乐观锁判断是否进行过初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//PropertiesConfiguration从系统属性和dubbo.properties中获取配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>propertiesConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> PropertiesConfiguration&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//SystemConfiguration获取的是JVM参数 启动命令中-D指定的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>systemConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> SystemConfiguration&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//EnvironmentConfiguration是从环境变量中获取的配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>environmentConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> EnvironmentConfiguration&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//外部的Global配置config-center global/default config
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>externalConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InmemoryConfiguration&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;ExternalConfig&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//外部的应用配置如:config-center中的应用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>appExternalConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InmemoryConfiguration&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;AppExternalConfig&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地应用配置 ， 如Spring Environment/PropertySources/application.properties
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>appConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InmemoryConfiguration&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;AppConfig&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务迁移配置加载 dubbo2升级dubbo3的一些配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> loadMigrationRule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务迁移配置加载 JVM &amp;gt; env &amp;gt; 代码路径dubbo-migration.yaml
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">loadMigrationRule&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//文件路径配置的key dubbo.migration.file
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// JVM参数中获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String path &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_MIGRATION_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//env环境变量中获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> path &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getenv&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO_MIGRATION_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//类路径下获取文件dubbo-migration.yaml
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> path &lt;span style="color:#719e07">=&lt;/span> CommonConstants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_DUBBO_MIGRATION_FILE&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>localMigrationRule &lt;span style="color:#719e07">=&lt;/span> ConfigUtils&lt;span style="color:#719e07">.&lt;/span>loadMigrationRule&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">.&lt;/span>getClassLoaders&lt;span style="color:#719e07">(),&lt;/span> path&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>ConfigUtils中读取迁移规则配置文件loadMigrationRule&lt;/strong>
这个我们不细说了，贴一下代码感兴趣可以了解下，这个代码主要是读取文件到内存字符串:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> String &lt;span style="color:#268bd2">loadMigrationRule&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader&lt;span style="color:#719e07">&amp;gt;&lt;/span> classLoaders&lt;span style="color:#719e07">,&lt;/span> String fileName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String rawRule &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>checkFileNameExist&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">(&lt;/span>FileInputStream input &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> FileInputStream&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> readString&lt;span style="color:#719e07">(&lt;/span>input&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to load &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; file from &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;(ignore this file): &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ClassLoader&lt;span style="color:#719e07">&amp;gt;&lt;/span> classLoadersToLoad &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> LinkedList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classLoadersToLoad&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>ClassUtils&lt;span style="color:#719e07">.&lt;/span>getClassLoader&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classLoadersToLoad&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>classLoaders&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>URL&lt;span style="color:#719e07">&amp;gt;&lt;/span> urls &lt;span style="color:#719e07">:&lt;/span> ClassLoaderResourceLoader&lt;span style="color:#719e07">.&lt;/span>loadResources&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">,&lt;/span> classLoadersToLoad&lt;span style="color:#719e07">).&lt;/span>values&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>URL url &lt;span style="color:#719e07">:&lt;/span> urls&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> InputStream is &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>openStream&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>is &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> readString&lt;span style="color:#719e07">(&lt;/span>is&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to load &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; file from &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> fileName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;(ignore this file): &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> rawRule&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> String &lt;span style="color:#268bd2">readString&lt;/span>&lt;span style="color:#719e07">(&lt;/span>InputStream is&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> StringBuilder stringBuilder &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> StringBuilder&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">char&lt;/span>&lt;span style="color:#719e07">[]&lt;/span> buffer &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> &lt;span style="color:#dc322f">char&lt;/span>&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">10&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">(&lt;/span>BufferedReader reader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> BufferedReader&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> InputStreamReader&lt;span style="color:#719e07">(&lt;/span>is&lt;span style="color:#719e07">)))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> n&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">((&lt;/span>n &lt;span style="color:#719e07">=&lt;/span> reader&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">(&lt;/span>buffer&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>n &lt;span style="color:#719e07">&amp;lt;&lt;/span> &lt;span style="color:#2aa198">10&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> buffer &lt;span style="color:#719e07">=&lt;/span> Arrays&lt;span style="color:#719e07">.&lt;/span>copyOf&lt;span style="color:#719e07">(&lt;/span>buffer&lt;span style="color:#719e07">,&lt;/span> n&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stringBuilder&lt;span style="color:#719e07">.&lt;/span>append&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>valueOf&lt;span style="color:#719e07">(&lt;/span>buffer&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> buffer &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> &lt;span style="color:#dc322f">char&lt;/span>&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">10&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>IOException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Read migration file error.&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> stringBuilder&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * check if the fileName can be found in filesystem
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param fileName
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">checkFileNameExist&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String fileName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> File file &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> File&lt;span style="color:#719e07">(&lt;/span>fileName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> file&lt;span style="color:#719e07">.&lt;/span>exists&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>​&lt;/p>
&lt;h2 id="34-创建modulemodel对象">3.4 创建ModuleModel对象&lt;/h2>
&lt;p>前面ApplicationModel对象初始化的时候创建了ModuleModel如下代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>internalModule &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ModuleModel&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里我们来看下这个它所对应的构造器&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ModuleModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> isInternal&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用ScopeModel传递3个参数父模型，模型域为模块域，是否为内部模型参数为true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> ExtensionScope&lt;span style="color:#719e07">.&lt;/span>MODULE&lt;span style="color:#719e07">,&lt;/span> isInternal&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>notNull&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;ApplicationModel can not be null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化成员变量applicationModel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将模块模型添加至应用模型中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>addModule&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> isInternal&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>LOGGER&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LOGGER&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>getDesc&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; is created&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化模块模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>notNull&lt;span style="color:#719e07">(&lt;/span>serviceRepository&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;ModuleServiceRepository can not be null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>notNull&lt;span style="color:#719e07">(&lt;/span>moduleConfigManager&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;ModuleConfigManager can not be null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>assertTrue&lt;span style="color:#719e07">(&lt;/span>moduleConfigManager&lt;span style="color:#719e07">.&lt;/span>isInitialized&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#2aa198">&amp;#34;ModuleConfigManager can not be initialized&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// notify application check state
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//获取应用程序发布对象，通知检查状态
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationDeployer applicationDeployer &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getDeployer&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>applicationDeployer &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>notifyModuleChanged&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> DeployState&lt;span style="color:#719e07">.&lt;/span>PENDING&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="341-将模块模型添加至应用模型中">3.4.1 将模块模型添加至应用模型中&lt;/h3>
&lt;p>如上面代码所示调用如下代码将模块模型添加到应用模型中:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> applicationModel&lt;span style="color:#719e07">.&lt;/span>addModule&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> isInternal&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里我们来看下添加过程&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addModule&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ModuleModel moduleModel&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> isInternal&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加锁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>moduleLock&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//不存在则添加
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleModels&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查应用模型是否已销毁
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkDestroyed&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//添加至模块模型成员变量中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleModels&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//设置模块模型内部id，这个内部id生成过程与上面将应用模型添加到框架模型中的方式是一致的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//可以参考 3.3.2 将ApplicationModel添加至FrameworkModel容器中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>setInternalId&lt;span style="color:#719e07">(&lt;/span>buildInternalId&lt;span style="color:#719e07">(&lt;/span>getInternalId&lt;span style="color:#719e07">(),&lt;/span> moduleIndex&lt;span style="color:#719e07">.&lt;/span>getAndIncrement&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果不是内部模型则添加到公开模块模型中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>isInternal&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pubModuleModels&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="342-初始化模块模型">3.4.2 初始化模块模型&lt;/h3>
&lt;p>前面ModuleModel构造器中通过initialize()方法来进行初始化操作如下代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用域模型ScopeModel的初始化，可以参考 3.2.5 ScopeModel类型的初始化方法initialize()章节
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建模块服务存储库对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceRepository &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ModuleServiceRepository&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建模块配置管理对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleConfigManager &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ModuleConfigManager&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化模块配置管理对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleConfigManager&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化模块配置扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initModuleExt&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化域模型扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializerExtensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializers &lt;span style="color:#719e07">=&lt;/span> initializerExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ScopeModelInitializer initializer &lt;span style="color:#719e07">:&lt;/span> initializers&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initializer&lt;span style="color:#719e07">.&lt;/span>initializeModuleModel&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3421-模块服务存储库的创建">3.4.2.1 模块服务存储库的创建&lt;/h4>
&lt;p>ModuleServiceRepository是模块模型中用来存储服务的通过如下代码调用&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//创建模块服务存储库对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>serviceRepository &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ModuleServiceRepository&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里我们来看下模块服务存储库的构造器代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ModuleServiceRepository&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ModuleModel moduleModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化模块模型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleModel &lt;span style="color:#719e07">=&lt;/span> moduleModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> frameworkServiceRepository &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getFrameworkModel&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">).&lt;/span>getServiceRepository&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ModuleServiceRepository存储库中使用框架存储库frameworkServiceRepository来间接存储
这里我们看下怎么通过模块模型获取框架服务存储库frameworkServiceRepository:通过代码&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getFrameworkModel&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">).&lt;/span>getServiceRepository&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ScopeModelUtil工具类获取getFrameworkModel代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> FrameworkModel &lt;span style="color:#268bd2">getFrameworkModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>scopeModel &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> FrameworkModel&lt;span style="color:#719e07">.&lt;/span>defaultModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//通过成员变量获取(构造器初始化的时候将FrameworkModel赋值给了ApplicationModel的成员变量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>scopeModel &lt;span style="color:#719e07">instanceof&lt;/span> ApplicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//直接获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">((&lt;/span>ApplicationModel&lt;span style="color:#719e07">)&lt;/span> scopeModel&lt;span style="color:#719e07">).&lt;/span>getFrameworkModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>scopeModel &lt;span style="color:#719e07">instanceof&lt;/span> ModuleModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ModuleModel moduleModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ModuleModel&lt;span style="color:#719e07">)&lt;/span> scopeModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//间接通过ApplicationModel获取，不越级获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">().&lt;/span>getFrameworkModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>scopeModel &lt;span style="color:#719e07">instanceof&lt;/span> FrameworkModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>FrameworkModel&lt;span style="color:#719e07">)&lt;/span> scopeModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Unable to get FrameworkModel from &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3422-模块配置管理器对象的创建与初始化">3.4.2.2 模块配置管理器对象的创建与初始化&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建模块配置管理对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleConfigManager &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ModuleConfigManager&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化模块配置管理对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleConfigManager&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ModuleConfigManager的构造器代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ModuleConfigManager&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ModuleModel moduleModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//向抽象的配置管理器AbstractConfigManager传递参数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//模块模型参数，模块支持的配置类型集合
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">,&lt;/span> Arrays&lt;span style="color:#719e07">.&lt;/span>asList&lt;span style="color:#719e07">(&lt;/span>ModuleConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> ServiceConfigBase&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> ReferenceConfigBase&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> ProviderConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> ConsumerConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取应用程序配置管理器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationConfigManager &lt;span style="color:#719e07">=&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">().&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ModuleConfigManager类型的初始化方法代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>initialized&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取组合配置对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> CompositeConfiguration configuration &lt;span style="color:#719e07">=&lt;/span> scopeModel&lt;span style="color:#719e07">.&lt;/span>getModelEnvironment&lt;span style="color:#719e07">().&lt;/span>getConfiguration&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// dubbo.config.mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//3.3.4.1提到过这里再重复一次 dubbo.config.mode获取配置模式，配置模式对应枚举类型ConfigMode，目前有这么几个STRICT，OVERRIDE，OVERRIDE_ALL，OVERRIDE_IF_ABSENT，IGNORE，这个配置决定了属性覆盖的顺序，当有同一个配置key多次出现时候，以最新配置为准，还是以最老的那个配置为准，还是配置重复则抛出异常，默认值为严格模式STRICT重复则抛出异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String configModeStr &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">)&lt;/span> configuration&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_MODE&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>hasText&lt;span style="color:#719e07">(&lt;/span>configModeStr&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>configMode &lt;span style="color:#719e07">=&lt;/span> ConfigMode&lt;span style="color:#719e07">.&lt;/span>valueOf&lt;span style="color:#719e07">(&lt;/span>configModeStr&lt;span style="color:#719e07">.&lt;/span>toUpperCase&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String msg &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Illegal &amp;#39;&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_MODE &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#39; config value [&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> configModeStr &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;], available values &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> Arrays&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">(&lt;/span>ConfigMode&lt;span style="color:#719e07">.&lt;/span>values&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>msg&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>msg&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// dubbo.config.ignore-duplicated-interface
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//忽略重复的接口（服务/引用）配置。默认值为false
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String ignoreDuplicatedInterfaceStr &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">)&lt;/span> configuration
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ignoreDuplicatedInterfaceStr &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>ignoreDuplicatedInterface &lt;span style="color:#719e07">=&lt;/span> Boolean&lt;span style="color:#719e07">.&lt;/span>parseBoolean&lt;span style="color:#719e07">(&lt;/span>ignoreDuplicatedInterfaceStr&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// print 打印配置信息到控制台
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> map &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> LinkedHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> map&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_MODE&lt;span style="color:#719e07">,&lt;/span> configMode&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> map&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>ConfigKeys&lt;span style="color:#719e07">.&lt;/span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>ignoreDuplicatedInterface&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Config settings: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> map&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3423-模块配置扩展的初始化">3.4.2.3 模块配置扩展的初始化&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>initModuleExt&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initModuleExt&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//目前这里的扩展只支持有一个类型ModuleEnvironment
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ModuleExt&lt;span style="color:#719e07">&amp;gt;&lt;/span> exts &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ModuleExt&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ModuleExt ext &lt;span style="color:#719e07">:&lt;/span> exts&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ext&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ModuleEnvironment的初始化&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>orderedPropertiesConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> OrderedPropertiesConfiguration&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>OrderedPropertiesConfiguration对象的创建&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">OrderedPropertiesConfiguration&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ModuleModel moduleModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>moduleModel &lt;span style="color:#719e07">=&lt;/span> moduleModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refresh&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> properties &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Properties&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//有序的配置提供器扩展获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>OrderedPropertiesProvider&lt;span style="color:#719e07">&amp;gt;&lt;/span> propertiesProviderExtensionLoader &lt;span style="color:#719e07">=&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>OrderedPropertiesProvider&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> propertiesProviderNames &lt;span style="color:#719e07">=&lt;/span> propertiesProviderExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensions&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>propertiesProviderNames&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>OrderedPropertiesProvider&lt;span style="color:#719e07">&amp;gt;&lt;/span> orderedPropertiesProviders &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String propertiesProviderName &lt;span style="color:#719e07">:&lt;/span> propertiesProviderNames&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> orderedPropertiesProviders&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>propertiesProviderExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>propertiesProviderName&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//order the propertiesProvider according the priority descending
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//根据优先级进行排序，值越小优先级越高
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> orderedPropertiesProviders&lt;span style="color:#719e07">.&lt;/span>sort&lt;span style="color:#719e07">((&lt;/span>a&lt;span style="color:#719e07">,&lt;/span> b&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> b&lt;span style="color:#719e07">.&lt;/span>priority&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&lt;/span> a&lt;span style="color:#719e07">.&lt;/span>priority&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//override the properties. 目前没看到有具体的扩展实现
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>OrderedPropertiesProvider orderedPropertiesProvider &lt;span style="color:#719e07">:&lt;/span> orderedPropertiesProviders&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> properties&lt;span style="color:#719e07">.&lt;/span>putAll&lt;span style="color:#719e07">(&lt;/span>orderedPropertiesProvider&lt;span style="color:#719e07">.&lt;/span>initProperties&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原文： &lt;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/">&amp;laquo;框架,应用程序,模块领域模型Model对象的初始化&amp;raquo;&lt;/a>&lt;/p></description></item><item><title>Blog: 02-启动服务前服务配置ServiceConfig类型是如何初始化的?</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="2-启动服务前服务配置serviceconfig类型是如何初始化的">2-启动服务前服务配置ServiceConfig类型是如何初始化的?&lt;/h1>
&lt;h2 id="21-示例源码回顾">2.1 示例源码回顾:&lt;/h2>
&lt;p>为了方便我们理解记忆,这里先来回顾下上一章我们说的示例代码,如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">Application&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startWithBootstrap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startWithBootstrap&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> DemoServiceImpl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>service&lt;span style="color:#719e07">(&lt;/span>service&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面这几行代码虽然看似简单,仅仅几行的启动,但是完全掌握也得下一翻大功夫,接下来我们重点看启动代码中的第一行,创建一个服务配置对象:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="22-了解一下服务配置的建模">2.2 了解一下服务配置的建模&lt;/h2>
&lt;p>下面是一个简单的UML继承关系图,当然这个图很是简单的,这里仅仅列出了当前服务提供者的相关服务配置继承关系, 服务提供者独有的配置标注颜色为蓝色,一些可能与服务引用配置所共有的父类型我们用红色背景,当然这里为了简便起见不会提起服务引用相关的配置类型,这里列举了如下服务提供者类型,他们各司其职:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/2-ServiceConfig.png" alt="在这里插入图片描述">&lt;/p>
&lt;center>图2.1 服务引用类继承关系UML&lt;/center>
&lt;ul>
&lt;li>AbstractConfig
&lt;ul>
&lt;li>&lt;strong>抽象的配置类型&lt;/strong>,也是最顶层的服务配置类型,封装着解析配置的实用方法和公共方法,比如服务id的设置,服务标签名字的处理,服务参数的添加,属性的提取等等&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>AbstractMethodConfig
&lt;ul>
&lt;li>&lt;strong>抽象的方法配置&lt;/strong>,同样这个类型也是见名知意,服务方法的相关配置处理,这个类型主要用于对服务方法的一些配置信息建模比如服务方法的调用超时时间,重试次数,最大并发调用数,负载均衡策略,是否异步调用,是否确认异步发送等等配置信息.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>AbstractInterfaceConfig
&lt;ul>
&lt;li>&lt;strong>抽象的接口配置&lt;/strong>,与前面介绍的方法配置类似,这个类型是对服务接口的建模,主要的配置信息有暴漏服务的接口名字,服务接口的版本号,客户/提供方将引用的远程服务分组,&lt;strong>服务元数据&lt;/strong>,服务接口的本地impl类名,服务监控配置,对于生成动态代理的策略，可以选择两种策略：jdk和javassist,容错类型等等配置&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>AbstractServiceConfig
- &lt;strong>抽象的服务配置&lt;/strong>,这个就与我们的服务提供者有了具体的关系了,主要记录了一些服务提供者的公共配置,如服务版本,服务分组,服务延迟注册毫秒数,是否暴漏服务,服务权重,是否为动态服务,服务协议类型,是否注册等等.&lt;/li>
&lt;li>ServiceConfigBase
- &lt;strong>服务的基础配置类&lt;/strong>,这个类型仍旧是个抽象的类型提取了一些基础的配置:导出服务的接口类,服务名称,接口实现的引用类型,提供者配置,是否是通用服务GenericService&lt;/li>
&lt;li>ServiceConfig
- &lt;strong>服务配置实现类&lt;/strong>, 上面的类型都是抽象类型不能做为具体存在的事物,这个类型是我们出现的第一个服务配置实现类型,服务配置实现类已经从父类型中继承了这么多的属性,这里主要为实现服务提供了一些配置如服务的协议配置,服务的代理工厂JavassistProxyFactory是将生成导出服务代理的ProxyFactory实现，是其默认实现,服务提供者模型,是否导出服务,导出的服务列表,服务监听器等等.&lt;/li>
&lt;li>ServiceBean
- &lt;strong>服务工厂Bean&lt;/strong> ,这个主要是Spring模块来简化配置的一个服务工厂Bean这里就先不详细介绍Spring相关的配置. -&lt;/li>
&lt;/ul>
&lt;h2 id="23-serviceconfig构造器的初始化调用链">2.3 ServiceConfig构造器的初始化调用链&lt;/h2>
&lt;p>有了上面的类型继承关系我们就比较好分析了,接下来我们开始创建服务配置对象如下代码所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>根据Java基础的构造器知识,在每个构造器的第一行都会有个super方法来调用父类的构造器,当前这个super方法我们可以不写但是Java编译器底层还是会为我们默认加上这么一行super()代码来调用父类构造器的.&lt;/p>
&lt;p>对于上面我提到的这几个构造器&lt;strong>根据代码被调用的先后顺序&lt;/strong>,这里重点说几个重要的,这里我仍旧按代码执行的先后顺序来说:&lt;/p>
&lt;h3 id="231-父类型abstractmethodconfig构造器的初始化">2.3.1 父类型AbstractMethodConfig构造器的初始化&lt;/h3>
&lt;p>根据super调用链这里先来看AbstractMethodConfig抽象方法配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractMethodConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel&lt;span style="color:#719e07">.&lt;/span>defaultModel&lt;span style="color:#719e07">().&lt;/span>getDefaultModule&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这个构造器中只有个super方法用来调用父类型的构造器,但是在调用之前会先使用代码 &lt;strong>ApplicationModel.defaultModel().getDefaultModule()&lt;/strong> 创建一个模块模型对象&lt;strong>ModuleModel&lt;/strong>
关于模型对象的细节我们会在下个章节来说,这里我们继续来看调用链&lt;/p>
&lt;h3 id="232-最顶层类型abstractconfig构造器的初始化">2.3.2 最顶层类型AbstractConfig构造器的初始化&lt;/h3>
&lt;p>&lt;strong>AbstractConfig&lt;/strong>的构造器初始化一共有两个,第一个步骤就是创建一个应用程序模型对象&lt;strong>ApplicationModel&lt;/strong>,刚刚我们在&lt;strong>AbstractMethodConfig&lt;/strong>的构造器中了解到使用这个代码&lt;strong>ApplicationModel.defaultModel().getDefaultModule()&lt;strong>创建了个模块模型对象&lt;/strong>ModuleModel&lt;/strong>,具体他们细节我们下一章细说,了解了子类型&lt;strong>AbstractMethodConfig&lt;/strong>的构造器是带参数的那我们就直接看第二个构造器&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel&lt;span style="color:#719e07">.&lt;/span>defaultModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>将会调用第二个构造器初始化域模型&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractConfig&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当前类型设置ScopeModel类型对象&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setScopeModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//第一次初始化的当前成员变量是空的可以设置变量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>scopeModel &lt;span style="color:#719e07">!=&lt;/span> scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查参数是否合法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkScopeModel&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ScopeModel oldScopeModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>scopeModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>scopeModel &lt;span style="color:#719e07">=&lt;/span> scopeModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// reinitialize spi extension and change referenced config&amp;#39;s scope model
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//被子类重写的方法,根据多态会调用具体子类型的这个方法我们下面来看
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//子类应该重写此方法以初始化其SPI扩展并更改引用的配置的范围模型。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>postProcessAfterScopeModelChanged&lt;span style="color:#719e07">(&lt;/span>oldScopeModel&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>检查ScopeModel参数是否合法,合法的参数是不能为空并且必须是ApplicationModel类型或者子类型&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">checkScopeModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>scopeModel &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;scopeModel cannot be null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!(&lt;/span>scopeModel &lt;span style="color:#719e07">instanceof&lt;/span> ApplicationModel&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Invalid scope model, expect to be a ApplicationModel but got: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="2321-重写的postprocessafterscopemodelchanged调用逻辑">2.3.2.1 重写的postProcessAfterScopeModelChanged调用逻辑&lt;/h4>
&lt;p>当ScopeModel模型对象发生了改变,上面调用了postProcessAfterScopeModelChanged方法来通知模型对象改变的时候要执行的操作,根据多态重写的逻辑我们从实现类的postProcessAfterScopeModelChanged来看,在下面的调用链路中部分父类型并未实现postProcessAfterScopeModelChanged方法我们就直接忽略了&lt;/p>
&lt;p>第一个被调用到的是&lt;strong>ServiceConfig&lt;/strong>类型的postProcessAfterScopeModelChanged方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">postProcessAfterScopeModelChanged&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel oldScopeModel&lt;span style="color:#719e07">,&lt;/span> ScopeModel newScopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>postProcessAfterScopeModelChanged&lt;span style="color:#719e07">(&lt;/span>oldScopeModel&lt;span style="color:#719e07">,&lt;/span> newScopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化当前协议对象,通过扩展机制获取协议Protocol类型的对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> protocolSPI &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Protocol&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化当前代理工厂对象,通过扩展机制获取ProxyFactory类型的对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> proxyFactory &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ProxyFactory&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>第二个被调用到的方法为&lt;strong>ServiceConfigBase&lt;/strong>的postProcessAfterScopeModelChanged方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">postProcessAfterScopeModelChanged&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel oldScopeModel&lt;span style="color:#719e07">,&lt;/span> ScopeModel newScopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>postProcessAfterScopeModelChanged&lt;span style="color:#719e07">(&lt;/span>oldScopeModel&lt;span style="color:#719e07">,&lt;/span> newScopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//当服务提供者配置对象不为空时候为服务提供者对象设置域模型,这里服务提供者对象仍旧为空,这个一般用在兼容Dubbo低版本
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>provider &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>provider&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> scopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>provider&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>第三个被调用到的是&lt;strong>AbstractInterfaceConfig&lt;/strong>类型的postProcessAfterScopeModelChanged方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">postProcessAfterScopeModelChanged&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel oldScopeModel&lt;span style="color:#719e07">,&lt;/span> ScopeModel newScopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>postProcessAfterScopeModelChanged&lt;span style="color:#719e07">(&lt;/span>oldScopeModel&lt;span style="color:#719e07">,&lt;/span> newScopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// remove this config from old ConfigManager
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// if (oldScopeModel != null &amp;amp;&amp;amp; oldScopeModel instanceof ModuleModel) {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ((ModuleModel)oldScopeModel).getConfigManager().removeConfig(this);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// change referenced config&amp;#39;s scope model
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//获取应用程序模型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationModel applicationModel &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为配置中心对象设置ApplicationModel类型对象(当前阶段配置中心配置对象为空)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>configCenter &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>configCenter&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>configCenter&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为元数据配置对象设置ApplicationModel类型对象(当前阶段数据配置配置对象为空)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataReportConfig &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataReportConfig&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataReportConfig&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为MonitorConfig服务监控配置对象设置ApplicationModel类型对象(当前阶段数据配置配置对象为空)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>monitor &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>monitor&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>monitor&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个if判断和上面的上面是重复的估计是写代码人加班加的太久了,没注意看
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataReportConfig &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataReportConfig&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>metadataReportConfig&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果注册中心配置列表不为空则为每个注册中心配置设置一个ApplicationModel类型对象(当前注册中心对象都为空)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>registries&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>registries&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>registryConfig &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>最后被调用到的是最顶层父类型&lt;strong>AbstractConfig&lt;/strong>的postProcessAfterScopeModelChanged方法
这个方法什么也没干只是在父类型创建的模版方法让子类型来重写用的&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">postProcessAfterScopeModelChanged&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ScopeModel oldScopeModel&lt;span style="color:#719e07">,&lt;/span> ScopeModel newScopeModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// remove this config from old ConfigManager
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// if (oldScopeModel != null &amp;amp;&amp;amp; oldScopeModel instanceof ApplicationModel) {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ((ApplicationModel)oldScopeModel).getApplicationConfigManager().removeConfig(this);
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="233-serviceconfigbase构造器的初始化">2.3.3 ServiceConfigBase构造器的初始化&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ServiceConfigBase&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务元数据对象创建
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceMetadata&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为服务元数据对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>addAttribute&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;ORIGIN_CONFIG&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>注意，&lt;/strong> &lt;strong>ServiceMetadata&lt;/strong>这个类目前在Dubbo中没有用法。与服务级别相关的数据，例如名称、版本、业务服务的类加载器、安全信息等，还带有用于扩展的AttributeMap。&lt;/p>
&lt;p>&lt;strong>服务配置对象的创建过程就这样结束了&lt;/strong>,当然有一些细节会&lt;strong>放到后面来写&lt;/strong>
上面主要顺序是按照代码执行的顺序来写的部分地方可能稍微做了调整,如果有条件的同学一定要&lt;strong>自己进行DEBUG&lt;/strong>了解下细节.&lt;/p>
&lt;p>关于服务配置官网提供了xml的配置信息这里我拷贝过来,可以做为参考:
当然这个配置不是最新的比如服务配置的&lt;strong>标签配置tag&lt;/strong>,
&lt;strong>warmup 预热时间&lt;/strong>单位毫秒,暂时还没有说明&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性&lt;/th>
&lt;th>对应URL参数&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>是否必填&lt;/th>
&lt;th>缺省值&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>描述&lt;/th>
&lt;th>兼容性&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>interface&lt;/td>
&lt;td>&lt;/td>
&lt;td>class&lt;/td>
&lt;td>必填&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务接口名&lt;/td>
&lt;td>1.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ref&lt;/td>
&lt;td>&lt;/td>
&lt;td>object&lt;/td>
&lt;td>必填&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务对象实现引用&lt;/td>
&lt;td>1.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>version&lt;/td>
&lt;td>version&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>0.0.0&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务版本，建议使用两位数字版本，如：1.0，通常在接口不兼容时版本号才需要升级&lt;/td>
&lt;td>1.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>group&lt;/td>
&lt;td>group&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务分组，当一个接口有多个实现，可以用分组区分&lt;/td>
&lt;td>1.0.7以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>path&lt;/td>
&lt;td>&lt;code>&amp;lt;path&amp;gt;&lt;/code>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省为接口名&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务路径 (注意：1.0不支持自定义路径，总是使用接口名，如果有1.0调2.0，配置服务路径可能不兼容)&lt;/td>
&lt;td>1.0.12以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>delay&lt;/td>
&lt;td>delay&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>0&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>延迟注册服务时间(毫秒) ，设为-1时，表示延迟到Spring容器初始化完成时暴露服务&lt;/td>
&lt;td>1.0.14以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>timeout&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>1000&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>远程服务调用超时时间(毫秒)&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>retries&lt;/td>
&lt;td>retries&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>2&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>远程服务调用重试次数，不包括第一次调用，不需要重试请设为0&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>connections&lt;/td>
&lt;td>connections&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>100&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>对每个提供者的最大连接数，rmi、http、hessian等短连接协议表示限制连接数，dubbo等长连接协表示建立的长连接个数&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>loadbalance&lt;/td>
&lt;td>loadbalance&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>random&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>负载均衡策略，可选值：random,roundrobin,leastactive，分别表示：随机，轮询，最少活跃调用&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>async&lt;/td>
&lt;td>async&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>是否缺省异步执行，不可靠异步，只是忽略返回值，不阻塞执行线程&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>local&lt;/td>
&lt;td>local&lt;/td>
&lt;td>class/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>设为true，表示使用缺省代理类名，即：接口名 + Local后缀，已废弃，请使用stub&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>stub&lt;/td>
&lt;td>stub&lt;/td>
&lt;td>class/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>设为true，表示使用缺省代理类名，即：接口名 + Stub后缀，服务接口客户端本地代理类名，用于在客户端执行本地逻辑，如本地缓存等，该本地代理类的构造函数必须允许传入远程代理对象，构造函数如：public XxxServiceStub(XxxService xxxService)&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>mock&lt;/td>
&lt;td>mock&lt;/td>
&lt;td>class/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>设为true，表示使用缺省Mock类名，即：接口名 + Mock后缀，服务接口调用失败Mock实现类，该Mock类必须有一个无参构造函数，与Local的区别在于，Local总是被执行，而Mock只在出现非业务异常(比如超时，网络异常等)时执行，Local在远程调用之前执行，Mock在远程调用后执行。&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>token&lt;/td>
&lt;td>token&lt;/td>
&lt;td>string/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>令牌验证，为空表示不开启，如果为true，表示随机生成动态令牌，否则使用静态令牌，令牌的作用是防止消费者绕过注册中心直接访问，保证注册中心的授权功能有效，如果使用点对点调用，需关闭令牌功能&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>registry&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省向所有registry注册&lt;/td>
&lt;td>配置关联&lt;/td>
&lt;td>向指定注册中心注册，在多个注册中心时使用，值为&lt;a href="dubbo:registry">dubbo:registry&lt;/a>的id属性，多个注册中心ID用逗号分隔，如果不想将该服务注册到任何registry，可将值设为N/A&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>provider&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用第一个provider配置&lt;/td>
&lt;td>配置关联&lt;/td>
&lt;td>指定provider，值为&lt;a href="dubbo:provider">dubbo:provider&lt;/a>的id属性&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>deprecated&lt;/td>
&lt;td>deprecated&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务是否过时，如果设为true，消费方引用时将打印服务过时警告error日志&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dynamic&lt;/td>
&lt;td>dynamic&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务是否动态注册，如果设为false，注册后将显示后disable状态，需人工启用，并且服务提供者停止时，也不会自动取消册，需人工禁用。 2.0.5以上版本&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>accesslog&lt;/td>
&lt;td>accesslog&lt;/td>
&lt;td>string/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>设为true，将向logger中输出访问日志，也可填写访问日志文件路径，直接把访问日志输出到指定文件&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>owner&lt;/td>
&lt;td>owner&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务负责人，用于服务治理，请填写负责人公司邮箱前缀&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>document&lt;/td>
&lt;td>document&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务文档URL&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>weight&lt;/td>
&lt;td>weight&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务权重&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>executes&lt;/td>
&lt;td>executes&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>0&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务提供者每服务每方法最大可并行执行请求数&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>proxy&lt;/td>
&lt;td>proxy&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>javassist&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>生成动态代理方式，可选：jdk/javassist&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cluster&lt;/td>
&lt;td>cluster&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>failover&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>集群方式，可选：failover/failfast/failsafe/failback/forking&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>filter&lt;/td>
&lt;td>service.filter&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>default&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务提供方远程调用过程拦截器名称，多个名称用逗号分隔&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>listener&lt;/td>
&lt;td>exporter.listener&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>default&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务提供方导出服务监听器名称，多个名称用逗号分隔&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>protocol&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>配置关联&lt;/td>
&lt;td>使用指定的协议暴露服务，在多协议时使用，值为&lt;a href="dubbo:protocol">dubbo:protocol&lt;/a>的id属性，多个协议ID用逗号分隔&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>layer&lt;/td>
&lt;td>layer&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务提供者所在的分层。如：biz、dao、intl:web、china:acton。&lt;/td>
&lt;td>2.0.7以上版本&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>register&lt;/td>
&lt;td>register&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>true&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>该协议的服务是否注册到注册中心&lt;/td>
&lt;td>2.0.8以上版本&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>原文： &lt;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/">&amp;laquo;ServiceConfig对象的建模&amp;raquo;&lt;/a>&lt;/p></description></item><item><title>Blog: 01 从一个服务提供者的Demo说起</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h1 id="1-从一个服务提供者的demo说起">1 从一个服务提供者的Demo说起&lt;/h1>
&lt;p>为了更方便了解原理,我们先来编写一个Demo,从例子中来看源码实现:&lt;/p>
&lt;h2 id="11-启动zookeeper">1.1 启动Zookeeper&lt;/h2>
&lt;p>为了Demo可以正常启动,需要我们先在本地启动一个Zookeeper如下图所示:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/1-zookeeper.png" alt="在这里插入图片描述">&lt;/p>
&lt;h2 id="12-服务提供者">1.2 服务提供者&lt;/h2>
&lt;p>接下来给大家贴一下示例源码,这个源码来源于Dubbo源码目录的 dubbo-demo/dubbo-demo-api 目录下面的dubbo-demo-api-provider子项目,这里我做了删减,方便看核心代码:
首先我们定义一个服务接口如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 同步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 用于异步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>completedFuture&lt;span style="color:#719e07">(&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>服务实现类如下&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.RpcContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.Logger&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.LoggerFactory&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Logger logger &lt;span style="color:#719e07">=&lt;/span> LoggerFactory&lt;span style="color:#719e07">.&lt;/span>getLogger&lt;span style="color:#719e07">(&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, request from consumer: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getServiceContext&lt;span style="color:#719e07">().&lt;/span>getRemoteAddress&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getServiceContext&lt;span style="color:#719e07">().&lt;/span>getLocalAddress&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="13-启用服务">1.3 启用服务&lt;/h2>
&lt;p>有了服务接口之后我们来启用服务,启用服务的源码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.constants.CommonConstants&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.ApplicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.MetadataReportConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.ProtocolConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.RegistryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.ServiceConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.bootstrap.DubboBootstrap&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.demo.DemoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">Application&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startWithBootstrap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startWithBootstrap&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> DemoServiceImpl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>service&lt;span style="color:#719e07">(&lt;/span>service&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="14-启用服务后写入zookeeper的节点数据">1.4 启用服务后写入Zookeeper的节点数据&lt;/h2>
&lt;p>启动服务,这个时候我们打开Zookeeper图形化客户端来看看这个服务在Zookeeper上面写入来哪些数据,如下图:
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/source-blog/1-zookeeper-data.png" alt="在这里插入图片描述">
写入Zookeper上的节点用于服务在分布式场景下的协调,这些节点是比较重要的。&lt;/p>
&lt;p>如果了解过Dubbo的同学,应该会知道Dubbo在低版本的时候会向注册中心中写入服务接口,具体路径在上面的 &lt;strong>dubbo目录下&lt;/strong> ,然后在 &lt;strong>/dubbo/服务接口/&lt;/strong> 路径下写入如下信息:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>服务提供者&lt;/strong>配置信息URL形式&lt;/li>
&lt;li>&lt;strong>服务消费者&lt;/strong>的配置信息URL形式&lt;/li>
&lt;li>服务&lt;strong>路由信息&lt;/strong>&lt;/li>
&lt;li>&lt;strong>配置信息&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>上面这个图就是Dubbo3的注册信息了,后面我们也会围绕细节来说明下,这里可以看下新增了:&lt;/p>
&lt;ul>
&lt;li>/dubbo/metadata &lt;strong>元数据信息&lt;/strong>&lt;/li>
&lt;li>/dubbo/mapping 服务和应用的&lt;strong>映射信息&lt;/strong>&lt;/li>
&lt;li>/dubbo/config &lt;strong>注册中心配置&lt;/strong>&lt;/li>
&lt;li>/services目录&lt;strong>应用信息&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>在这里可以大致了解下,在后面会有更详细的源码解析这个示例代码.通过透析代码来看透Dubbo3服务注册原理,服务提供原理。&lt;/p>
&lt;p>原文： &lt;a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">&amp;laquo;从一个服务提供者的Demo说起&amp;raquo;&lt;/a>&lt;/p></description></item><item><title>Blog: 浅析 Dubbo 3.0 中接口级地址推送性能的优化</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h2 id="url-简介">URL 简介&lt;/h2>
&lt;p>在阐述地址推送性能的具体优化之前，我们有必要先了解一下与之息息相关的内容 &amp;mdash; URL。&lt;/p>
&lt;h3 id="定义">定义&lt;/h3>
&lt;p>在不谈及 dubbo 时，我们大多数人对 URL 这个概念并不会感到陌生。统一资源定位器 (&lt;a href="https://www.ietf.org/rfc/rfc1738.txt">RFC1738&lt;/a>――Uniform Resource Locators (URL))应该是最广为人知的一个 RFC 规范，它的定义也非常简单。&lt;/p>
&lt;blockquote>
&lt;p>因特网上的可用资源可以用简单字符串来表示，该文档就是描述了这种字符串的语法和语 义。而这些字符串则被称为：“统一资源定位器”（URL）&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>一个标准的 URL 格式&lt;/strong>至多可以包含如下的几个部分&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>protocol://username:password@host:port/path?key=value&amp;amp;key=value
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>一些典型 URL&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>http://www.facebook.com/friends?param1=value1&amp;amp;amp;param2=value2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>https://username:password@10.20.130.230:8080/list?version=1.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ftp://username:password@192.168.1.7:21/1/read.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当然，也有一些&lt;strong>不太符合常规的 URL&lt;/strong>，也被归类到了 URL 之中&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>192.168.1.3:20880
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = null, url host = 192.168.1.3, port = 20880, url path = null
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file:///home/user1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = file, url host = null, url path = home/user1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file://home/user1/router.js?type=script&amp;lt;br&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = file, url host = home, url path = user1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file:///D:/1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = file, url host = null, url path = D:/1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file:/D:/1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>同上 file:///D:/1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/home/user1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = null, url host = null, url path = home/user1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>home/user1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = null, url host = home, url path = user1/router.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="dubbo-中的-url">Dubbo 中的 URL&lt;/h3>
&lt;p>在 dubbo 中，也使用了类似的 URL，主要用于在各个扩展点之间传递数据，组成此 URL 对象的具体参数如下:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>protocol：一般是 dubbo 中的各种协议 如：dubbo thrift http zk&lt;/p>
&lt;/li>
&lt;li>
&lt;p>username/password：用户名/密码&lt;/p>
&lt;/li>
&lt;li>
&lt;p>host/port：主机/端口&lt;/p>
&lt;/li>
&lt;li>
&lt;p>path：接口名称&lt;/p>
&lt;/li>
&lt;li>
&lt;p>parameters：参数键值对&lt;/p>
&lt;p>&lt;strong>一些典型的 Dubbo URL&lt;/strong>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>描述一个 dubbo 协议的服务
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&amp;amp;dubbo=2.0.2&amp;amp;interface=org.apache.dubbo.registry.RegistryService&amp;amp;pid=1214&amp;amp;qos.port=33333&amp;amp;timestamp=1545721981946
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>描述一个 zookeeper 注册中心
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>consumer://30.5.120.217/org.apache.dubbo.demo.DemoService?application=demo-consumer&amp;amp;category=consumers&amp;amp;check=false&amp;amp;dubbo=2.0.2&amp;amp;interface=org.apache.dubbo.demo.DemoService&amp;amp;methods=sayHello&amp;amp;pid=1209&amp;amp;qos.port=33333&amp;amp;side=consumer&amp;amp;timestamp=1545721827784
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>描述一个消费者
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以说，任意的一个领域中的一个实现都可以认为是一类 URL，dubbo 使用 URL 来统一描述了元数据，配置信息，贯穿在整个框架之中。&lt;/p>
&lt;h2 id="dubbo-27">Dubbo 2.7&lt;/h2>
&lt;h3 id="url-结构">URL 结构&lt;/h3>
&lt;p>在 Dubbo 2.7 中，URL 的结构非常简单，一个类就涵盖了所有内容，如下图所示。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/url-perf-tuning-1.png" alt="Dubbo2 URL类图.png">&lt;/p>
&lt;h3 id="地址推送模型">地址推送模型&lt;/h3>
&lt;p>接下来我们再来看看 Dubbo 2.7 中的地址推送模型方案，主要性能问题由下列过程引起。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/url-perf-tuning-2.png" alt="Dubbo2 地址推送模型.png">&lt;/p>
&lt;p>上图中主要的流程为
1、用户新增/删除DemoService的某个具体Provider实例（常见于扩容缩容、网络波动等原因）
2、ZooKeeper将DemoService下所有实例推送给Consumer端
3、Consumer端根据Zookeeper推送的数据重新全量生成URL
根据该方案可以看出在Provider实例数量较小时，Consumer端的影响比较小，但当某个接口有大量Provider实例时，便会有大量不必要的URL创建过程。
而Dubbo 3.0中则主要针对上述推送流程进行了一系列的优化，接下来我们便对其进行具体的讲解。&lt;/p>
&lt;h2 id="dubbo-30">Dubbo 3.0&lt;/h2>
&lt;h3 id="url-结构-1">URL 结构&lt;/h3>
&lt;p>当然，地址推送模型的优化依然离不开 URL 的优化，下图是Dubbo 3.0中优化地址推送模型的过程中使用的新的URL结构。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/url-perf-tuning-3.png" alt="Dubbo3 URL类图.png">&lt;/p>
&lt;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 为什么要改为此种数据结构，并且该结构和地址推送模型的优化有何关联性吧！&lt;/p>
&lt;h3 id="地址推送模型的优化">地址推送模型的优化&lt;/h3>
&lt;h4 id="url-结构上的优化">URL 结构上的优化&lt;/h4>
&lt;p>我们在上小节中的类图里看到虽然原来的属性都被移到了 URLAddress 和 URLParam 里，但是 URL 的子类依然多了几个属性，这几个属性自然也是为了优化而新增的，那么这里就讲讲这几个属性的作用。
&lt;strong>ServiceConfigURL&lt;/strong>：这个子类中新增了 attribute 这个属性，这个属性主要是针对 URLParam 的 params 做了冗余，仅仅只是将 value 的类型从 String 改为了 Object，减少了代码中每次获取 parameters 的格式转换消耗。
&lt;strong>ServiceAddressURL&lt;/strong>：这个子类及其对应的其他子类中则新增了 overrideURL 和 consumerURL 属性。其中 consumerURL 是针对 consumer 端的配置信息，overrideURL 则是在 Dubbo Admin 上进行动态配置时写入的值，当我们调用 URL 的 getParameter() 方法时，优先级为 &lt;code>overrideURL &amp;gt; consumerURL &amp;gt; urlParam&lt;/code>。在 Dubbo 2.7 时，动态配置属性会替换 URL 中的属性，及当你有大量 URL 时消耗也是不可忽视的，而此处的 overrideURL 则避免了这种消耗，因为所有 URL 都会共同使用同一个对象。&lt;/p>
&lt;h4 id="多级缓存">多级缓存&lt;/h4>
&lt;p>缓存是 Dubbo 3.0 在 URL 上做的优化的重点，同时这部分也是直接针对地址推送模型所做的优化，那么接下来我们就开始来介绍一下多级缓存的具体实现。
首先，多级缓存主要体现在 CacheableFailbackRegistry 这个类之中，它直接继承于 FailbackRegistry，以 Zookeeper 为例，我们看看 Dubbo 2.7 和 Dubbo 3.0 继承结构的区别。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/url-perf-tuning-4.png" alt="Dubbo3 CacheableFailbackRegistry缓存.png">&lt;/p>
&lt;p>可以看到在 CacheableFailbackRegistry 缓存中，我们新增了 3 个缓存属性 &lt;code>stringAddress&lt;/code>，&lt;code>stringParam&lt;/code> 和 &lt;code>stringUrls&lt;/code>。我们通过下图来描述这 3 个缓存的具体使用场景。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/url-perf-tuning-5.png" alt="多级缓存.png">&lt;/p>
&lt;p>在该方案下，我们使用了 3 个纬度的缓存数据(URL 字符串缓存、URL 地址缓存、URL 参数缓存)，这样一来，在大部分情况下都能有效利用到缓存中的数据，减少了 Zookeeper 重复通知的消耗。&lt;/p>
&lt;h4 id="延迟通知">延迟通知&lt;/h4>
&lt;p>除了上面提到的优化之外，其实另外还有两个小小的优化。
第一个是解析 URL 时可以直接使用编码后的 URL 字符串字节进行解析，而在 Dubbo 2.7 中，所有编码后的 URL 字符串都需要经过解码才可以正常解析为 URL 对象。该方式也直接减少了 URL 解码过程的开销。
第二个则是 URL 变更后的通知机制增加了延迟，下图以Zookeeper为例讲解了实现细节。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/url-perf-tuning-6.png" alt="延迟通知.png">&lt;/p>
&lt;p>在该方案中，当 Consumer 接收 Zookeeper 的变更通知后会主动休眠一段时间，而这段时间内的变更在休眠结束后只会保留最后一次变更，Consumer 便会使用最后一次变更来进行监听实例的更新，以此方法来减少大量 URL 的创建开销。&lt;/p>
&lt;h4 id="字符串重用">字符串重用&lt;/h4>
&lt;p>在旧版本实现中，不同的 URL 中属性相同的字符串会存储在堆内不同的地址中，如 protocol、path 等，当有大量 provider 的情况下，Consumer 端的堆内会存在大量的重复字符串，导致内存利用率低下，所以此处提供了另一个优化方式，即字符串重用。
而它的实现方式也非常的简单，让我们来看看对应的代码片段。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">URLItemCache&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> PATH_CACHE &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> LRUCache&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>&lt;span style="color:#2aa198">10000&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> PROTOCOL_CACHE &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略无关代码片段
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> String &lt;span style="color:#268bd2">checkProtocol&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String _protocol&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>_protocol &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> _protocol&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String cachedProtocol &lt;span style="color:#719e07">=&lt;/span> PROTOCOL_CACHE&lt;span style="color:#719e07">.&lt;/span>putIfAbsent&lt;span style="color:#719e07">(&lt;/span>_protocol&lt;span style="color:#719e07">,&lt;/span> _protocol&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedProtocol &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> cachedProtocol&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> _protocol&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> String &lt;span style="color:#268bd2">checkPath&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String _path&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>_path &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> _path&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String cachedPath &lt;span style="color:#719e07">=&lt;/span> PATH_CACHE&lt;span style="color:#719e07">.&lt;/span>putIfAbsent&lt;span style="color:#719e07">(&lt;/span>_path&lt;span style="color:#719e07">,&lt;/span> _path&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedPath &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> cachedPath&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> _path&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由如上代码片段可以得知，字符串重用即为简单地使用了 Map 来存储对应的缓存值，当你使用了相同的字符串时，便会从 Map 中获取早已存在的对象返回给调用方，由此便可以减少堆内存中重复的字符串数以达到优化的效果。&lt;/p>
&lt;h3 id="优化结果">优化结果&lt;/h3>
&lt;p>这里优化结果我引用了&lt;a href="https://zhuanlan.zhihu.com/p/345626851">《Dubbo 3.0 前瞻：服务发现支持百万集群，带来可伸缩微服务架构》&lt;/a>这篇文章中的两副图来说明，下图模拟了在&lt;strong>220万&lt;/strong>个 Provider 接口的情况下，接口数据不断变更导致的 Consumer 端的消耗，我们看到整个 Consumer 端几乎被 Full GC 占满了，严重影响了性能。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/url-perf-tuning-7.png" alt="Dubbo2 接口级地址模型.png">&lt;/p>
&lt;p>那么我们再来看看 Dubbo 3.0 中对 URL 进行优化后同一个环境下的压测结果，如下图所示。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/url-perf-tuning-8.png" alt="Dubbo3 接口级地址模型.png">&lt;/p>
&lt;p>我们明显可以看到 Full GC 的频率减少到了只有 3 次，大大提升了性能。当然，该文章中还有其他方面的对比，此处便不一一引用了，感兴趣的读者可以自行去阅读该文章。&lt;/p></description></item><item><title>Blog: 如何通过 Apache ShenYu 网关代理 Dubbo 服务</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/ApacheShenYu-Dubbo-zh.png" alt="">&lt;/p>
&lt;h2 id="1-介绍">1. 介绍&lt;/h2>
&lt;ul>
&lt;li>Apache ShenYu&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/shenyu.png" alt="">&lt;/p>
&lt;p>&lt;a href="https://shenyu.apache.org/zh/docs/index">Apache ShenYu(Incubating)&lt;/a> 是一个异步的，高性能的，跨语言的，响应式的 &lt;code>API&lt;/code> 网关。兼容各种主流框架体系，支持热插拔，用户可以定制化开发，满足用户各种场景的现状和未来需求，经历过大规模场景的锤炼。&lt;/p>
&lt;p>2021年5月，&lt;code>ShenYu&lt;/code>捐献给 &lt;code>Apache&lt;/code> 软件基金会，Apache 基金会全票通过，顺利进入孵化器。&lt;/p>
&lt;ul>
&lt;li>Apache Dubbo&lt;/li>
&lt;/ul>
&lt;p>&lt;code>Apache Dubbo&lt;/code> 是一款微服务开发框架，它提供了 &lt;code>RPC&lt;/code> 通信 与 微服务治理 两大关键能力。这意味着，使用 &lt;code>Dubbo&lt;/code> 开发的微服务，将具备相互之间的远程发现与通信能力， 同时利用 Dubbo 提供的丰富服务治理能力，可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 &lt;code>Dubbo&lt;/code> 是高度可扩展的，用户几乎可以在任意功能点去定制自己的实现，以改变框架的默认行为来满足自己的业务需求。&lt;/p>
&lt;h2 id="2-dubbo快速开始">2. Dubbo快速开始&lt;/h2>
&lt;p>本小节介绍如何将&lt;code>Dubbo&lt;/code>服务接入到&lt;code>ShenYu&lt;/code>网关，您可以直接在工程下找到本小节的&lt;a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">示例代码&lt;/a> 。&lt;/p>
&lt;h3 id="21-启动shenyu-admin">2.1 启动shenyu-admin&lt;/h3>
&lt;p>&lt;code>shenyu-admin&lt;/code>是&lt;code>Apache ShenYu&lt;/code>后台管理系统， 启动的方式有多种，本文通过 &lt;code>[本地部署](https://shenyu.apache.org/zh/docs/deployment/deployment-local)&lt;/code> 的方式启动。启动成功后，需要在基础配置&lt;code>-&amp;gt;&lt;/code>插件管理中，把&lt;code>dubbo&lt;/code> 插件设置为开启，并设置你的注册地址，请确保注册中心已经开启。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-enable-zh.png" alt="">&lt;/p>
&lt;h3 id="22-启动shenyu网关">2.2 启动shenyu网关&lt;/h3>
&lt;p>在这里通过 &lt;a href="https://github.com/apache/incubator-shenyu/tree/master/shenyu-bootstrap">源码&lt;/a> 的方式启动，直接运行&lt;code>shenyu-bootstrap&lt;/code>中的&lt;code>ShenyuBootstrapApplication&lt;/code>。&lt;/p>
&lt;p>在启动前，请确保网关已经引入相关依赖。如果客户端是&lt;code>apache dubbo&lt;/code>，注册中心使用&lt;code>zookeeper&lt;/code>，请参考如下配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> apache shenyu apache dubbo plugin start&lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>shenyu&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>shenyu&lt;span style="color:#719e07">-&lt;/span>spring&lt;span style="color:#719e07">-&lt;/span>boot&lt;span style="color:#719e07">-&lt;/span>starter&lt;span style="color:#719e07">-&lt;/span>plugin&lt;span style="color:#719e07">-&lt;/span>apache&lt;span style="color:#719e07">-&lt;/span>dubbo&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>$&lt;span style="color:#719e07">{&lt;/span>project&lt;span style="color:#719e07">.&lt;/span>version&lt;span style="color:#719e07">}&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>dubbo&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>&lt;span style="color:#2aa198">2.7.5&lt;/span>&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> Dubbo zookeeper registry dependency start &lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>curator&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>curator&lt;span style="color:#719e07">-&lt;/span>client&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>&lt;span style="color:#2aa198">4.0.1&lt;/span>&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>exclusions&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>exclusion&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>log4j&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>log4j&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>exclusion&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>exclusions&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>curator&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>curator&lt;span style="color:#719e07">-&lt;/span>framework&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>&lt;span style="color:#2aa198">4.0.1&lt;/span>&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>curator&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>curator&lt;span style="color:#719e07">-&lt;/span>recipes&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>&lt;span style="color:#2aa198">4.0.1&lt;/span>&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> Dubbo zookeeper registry dependency end &lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> apache dubbo plugin end&lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="23-启动shenyu-examples-dubbo">2.3 启动shenyu-examples-dubbo&lt;/h3>
&lt;p>以官网提供的例子为例 &lt;a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">shenyu-examples-dubbo&lt;/a> 。 假如&lt;code>dubbo&lt;/code>服务定义如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;beans&lt;/span> /* ...... * &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:application&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;test-dubbo-service&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;${dubbo.registry.address}&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:protocol&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> port=&lt;span style="color:#2aa198">&amp;#34;20888&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> timeout=&lt;span style="color:#2aa198">&amp;#34;10000&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.shenyu.examples.dubbo.api.service.DubboTestService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;dubboTestService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/beans&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>声明应用服务名称，注册中心地址，使用&lt;code>dubbo&lt;/code>协议，声明服务接口，对应接口实现类：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * DubboTestServiceImpl.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubboTestService&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboTestServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DubboTestService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@ShenyuDubboClient&lt;/span>&lt;span style="color:#719e07">(&lt;/span>path &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;/findById&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> desc &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Query by Id&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> DubboTest &lt;span style="color:#268bd2">findById&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> String id&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DubboTest&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hello world shenyu Apache, findById&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//......
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在接口实现类中，使用注解&lt;code>@ShenyuDubboClient&lt;/code>向&lt;code>shenyu-admin&lt;/code>注册服务。&lt;/p>
&lt;p>在配置文件&lt;code>application.yml&lt;/code>中的配置信息：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">server&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>: &lt;span style="color:#2aa198">8011&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: &lt;span style="color:#2aa198">0.0.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">servlet&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">context-path&lt;/span>: /
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spring&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">main&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">allow-bean-definition-overriding&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">dubbo&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: zookeeper://localhost:2181 &lt;span style="color:#586e75"># dubbo使用的注册中心&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">shenyu&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">register&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registerType&lt;/span>: http &lt;span style="color:#586e75">#注册方式&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">serverLists&lt;/span>: http://localhost:9095 &lt;span style="color:#586e75">#注册地址&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">props&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">username&lt;/span>: admin
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">password&lt;/span>: &lt;span style="color:#2aa198">123456&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">client&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">dubbo&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">props&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">contextPath&lt;/span>: /dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">appName&lt;/span>: dubbo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在配置文件中，声明&lt;code>dubbo&lt;/code>使用的注册中心地址，&lt;code>dubbo&lt;/code>服务向&lt;code>shenyu-admin&lt;/code>注册，使用的方式是&lt;code>http&lt;/code>，注册地址是&lt;code>http://localhost:9095&lt;/code>。&lt;/p>
&lt;p>关于注册方式的使用，请参考 &lt;code>[应用客户端接入](https://shenyu.apache.org/docs/design/register-center-design/)&lt;/code> 。&lt;/p>
&lt;h3 id="24-调用dubbo服务">2.4 调用dubbo服务&lt;/h3>
&lt;p>&lt;code>shenyu-examples-dubbo&lt;/code>项目成功启动之后会自动把加 &lt;code>@ShenyuDubboClient&lt;/code> 注解的接口方法注册到网关。&lt;/p>
&lt;p>打开 &lt;code>插件列表 -&amp;gt; Proxy -&amp;gt; dubbo&lt;/code> 可以看到插件规则配置列表：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-service-list-zh.png" alt="">&lt;/p>
&lt;p>注册成功的选择器信息：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-selector-zh.png" alt="">&lt;/p>
&lt;p>注册成功的规则信息：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-rule-zh.png" alt="">&lt;/p>
&lt;blockquote>
&lt;p>选择器和规则是 &lt;code>Apache ShenYu&lt;/code> 网关中最灵魂的东西。掌握好它，你可以对任何流量进行管理。对应为选择器与规则里面的匹配条件(conditions)，根据不同的流量筛选规则，我们可以处理各种复杂的场景。流量筛选可以从&lt;code>Header&lt;/code>, &lt;code>URI&lt;/code>, &lt;code>Query&lt;/code>, &lt;code>Cookie&lt;/code> 等等Http请求获取数据。&lt;/p>
&lt;p>然后可以采用 &lt;code>Match&lt;/code>，&lt;code>=&lt;/code>，&lt;code>Regex&lt;/code>，&lt;code>Groovy&lt;/code>，&lt;code>Exclude&lt;/code>等匹配方式，匹配出你所预想的数据。多组匹配添加可以使用&lt;code>And/Or&lt;/code>的匹配策略。&lt;/p>
&lt;p>具体的介绍与使用请看: &lt;code>[选择器与规则管理](https://shenyu.apache.org/zh/docs/user-guide/admin-usage/selector-and-rule)&lt;/code> 。&lt;/p>
&lt;/blockquote>
&lt;p>发起&lt;code>GET&lt;/code>请求，通过&lt;code>ShenYu&lt;/code>网关调用&lt;code>dubbo&lt;/code>服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>GET http://localhost:9195/dubbo/findById?id=100
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Accept: application/json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>成功响应之后，结果如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;#34;name&amp;#34;: &amp;#34;hello world shenyu Apache, findById&amp;#34;,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;#34;id&amp;#34;: &amp;#34;100&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>至此，就成功的通过&lt;code>http&lt;/code>请求访问&lt;code>dubbo&lt;/code>服务了，&lt;code>ShenYu&lt;/code>网关通过&lt;code>shenyu-plugin-dubbo&lt;/code>模块将&lt;code>http&lt;/code>协议转成了&lt;code>dubbo&lt;/code>协议。&lt;/p>
&lt;h2 id="3-深入理解dubbo插件">3. 深入理解Dubbo插件&lt;/h2>
&lt;p>在运行上述&lt;code>demo&lt;/code>的过程中，是否存在一些疑问：&lt;/p>
&lt;ul>
&lt;li>&lt;code>dubbo&lt;/code>服务是如何注册到&lt;code>shenyu-admin&lt;/code>？&lt;/li>
&lt;li>&lt;code>shenyu-admin&lt;/code>是如何将数据同步到&lt;code>ShenYu&lt;/code>网关？&lt;/li>
&lt;li>&lt;code>DubboPlugin&lt;/code>是如何将&lt;code>http&lt;/code>协议转换到到dubbo协议？&lt;/li>
&lt;/ul>
&lt;p>带着这些疑问，来深入理解&lt;code>dubbo&lt;/code>插件。&lt;/p>
&lt;h3 id="31-应用客户端接入">3.1 应用客户端接入&lt;/h3>
&lt;p>应用客户端接入是指将微服务接入到&lt;code>Apache ShenYu&lt;/code>网关，当前支持&lt;code>Http&lt;/code>、 &lt;code>Dubbo&lt;/code>、 &lt;code>Spring Cloud&lt;/code>、 &lt;code>gRPC&lt;/code>、 &lt;code>Motan&lt;/code>、 &lt;code>Sofa&lt;/code>、 &lt;code>Tars&lt;/code>等协议的接入。&lt;/p>
&lt;p>将应用客户端接入到&lt;code>Apache ShenYu&lt;/code>网关是通过注册中心来实现的，涉及到客户端注册和服务端同步数据。注册中心支持&lt;code>Http&lt;/code>、&lt;code>Zookeeper&lt;/code>、&lt;code>Etcd&lt;/code>、&lt;code>Consul&lt;/code>和&lt;code>Nacos&lt;/code>。默认是通过&lt;code>Http&lt;/code>方式注册。&lt;/p>
&lt;p>客户端接入的相关配置请参考 &lt;code>[客户端接入配置](https://shenyu.apache.org/zh/docs/user-guide/register-center-access)&lt;/code> 。&lt;/p>
&lt;h4 id="311-客户端注册">3.1.1 客户端注册&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/register-client.png" alt="">&lt;/p>
&lt;p>在你的微服务配置中声明注册中心客户端类型，如&lt;code>Http&lt;/code>或&lt;code>Zookeeper&lt;/code>。
应用程序启动时使用&lt;code>SPI&lt;/code>方式加载并初始化对应注册中心客户端，通过实现&lt;code>Spring Bean&lt;/code>相关的后置处理器接口，在其中获取需要进行注册的服务接口信息，将获取的信息放入&lt;code>Disruptor&lt;/code>中。&lt;/p>
&lt;p>注册中心客户端从&lt;code>Disruptor&lt;/code>中读取数据，并将接口信息注册到&lt;code>shenyu-admin&lt;/code>，&lt;code>Disruptor&lt;/code>在其中起数据与操作解耦的作用，利于扩展。&lt;/p>
&lt;h4 id="312-服务端注册">3.1.2 服务端注册&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/register-server.png" alt="">&lt;/p>
&lt;p>在&lt;code>shenyu-admin&lt;/code>配置中声明注册中心服务端类型，如&lt;code>Http&lt;/code>或&lt;code>Zookeeper&lt;/code>。当&lt;code>shenyu-admin&lt;/code>启动时，读取配置类型，加载并初始化对应的注册中心服务端，注册中心服务端收到&lt;code>shenyu-client&lt;/code>注册的接口信息后，将其放入&lt;code>Disruptor&lt;/code>中，然后会触发注册处理逻辑，将服务接口信息更新并发布同步事件。&lt;/p>
&lt;p>&lt;code>Disruptor&lt;/code>在其中起到数据与操作解耦，利于扩展。如果注册请求过多，导致注册异常，也有数据缓冲作用。&lt;/p>
&lt;h3 id="32-数据同步原理">3.2 数据同步原理&lt;/h3>
&lt;p>数据同步是指在 &lt;code>shenyu-admin&lt;/code> 后台操作数据以后，使用何种策略将数据同步到 &lt;code>Apache ShenYu&lt;/code> 网关。&lt;code>Apache ShenYu&lt;/code> 网关当前支持&lt;code>ZooKeeper&lt;/code>、&lt;code>WebSocket&lt;/code>、&lt;code>Http长轮询&lt;/code>、&lt;code>Nacos&lt;/code> 、&lt;code>Etcd&lt;/code> 和 &lt;code>Consul&lt;/code> 进行数据同步。默认是通过&lt;code>WebSocket&lt;/code>进行数据同步。&lt;/p>
&lt;p>数据同步的相关配置请参考 &lt;code>[数据同步配置](https://shenyu.apache.org/zh/docs/user-guide/use-data-sync)&lt;/code> 。&lt;/p>
&lt;h4 id="321-数据同步的意义">3.2.1 数据同步的意义&lt;/h4>
&lt;p>网关是流量请求的入口，在微服务架构中承担了非常重要的角色，网关高可用的重要性不言而喻。在使用网关的过程中，为了满足业务诉求，经常需要变更配置，比如流控规则、路由规则等等。因此，网关动态配置是保障网关高可用的重要因素。&lt;/p>
&lt;p>当前数据同步特性如下：&lt;/p>
&lt;ul>
&lt;li>所有的配置都缓存在 &lt;code>Apache ShenYu&lt;/code> 网关内存中，每次请求都使用本地缓存，速度非常快。&lt;/li>
&lt;li>用户可以在 &lt;code>shenyu-admin&lt;/code> 后台任意修改数据，并马上同步到网关内存。&lt;/li>
&lt;li>支持 &lt;code>Apache ShenYu&lt;/code> 的插件、选择器、规则数据、元数据、签名数据等数据同步。&lt;/li>
&lt;li>所有插件的选择器，规则都是动态配置，立即生效，不需要重启服务。&lt;/li>
&lt;li>数据同步方式支持 &lt;code>Zookeeper&lt;/code>、&lt;code>Http 长轮询&lt;/code>、&lt;code>Websocket&lt;/code>、&lt;code>Nacos&lt;/code>、&lt;code>Etcd&lt;/code> 和 &lt;code>Consul&lt;/code>。&lt;/li>
&lt;/ul>
&lt;h4 id="322-数据同步原理分析">3.2.2 数据同步原理分析&lt;/h4>
&lt;p>下图展示了 &lt;code>Apache ShenYu&lt;/code> 数据同步的流程，&lt;code>Apache ShenYu&lt;/code> 网关在启动时，会从配置服务同步配置数据，并且支持推拉模式获取配置变更信息，然后更新本地缓存。管理员可以在管理后台（&lt;code>shenyu-admin&lt;/code>），变更用户权限、规则、插件、流量配置，通过推拉模式将变更信息同步给 &lt;code>Apache ShenYu&lt;/code> 网关，具体是 &lt;code>push&lt;/code> 模式，还是 &lt;code>pull&lt;/code> 模式取决于使用哪种同步方式。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/data-sync.png" alt="">&lt;/p>
&lt;p>在最初的版本中，配置服务依赖 &lt;code>Zookeeper&lt;/code> 实现，管理后台将变更信息 &lt;code>push&lt;/code> 给网关。而现在可以支持 &lt;code>WebSocket&lt;/code>、&lt;code>Http长轮询&lt;/code>、&lt;code>Zookeeper&lt;/code>、&lt;code>Nacos&lt;/code>、&lt;code>Etcd&lt;/code> 和 &lt;code>Consul&lt;/code>，通过在配置文件中设置 &lt;code>shenyu.sync.${strategy}&lt;/code> 指定对应的同步策略，默认使用 &lt;code>webosocket&lt;/code> 同步策略，可以做到秒级数据同步。但是，有一点需要注意的是，&lt;code>Apache ShenYu&lt;/code>网关 和 &lt;code>shenyu-admin&lt;/code> 必须使用相同的同步策略。&lt;/p>
&lt;p>如下图所示，&lt;code>shenyu-admin&lt;/code> 在用户发生配置变更之后，会通过 &lt;code>EventPublisher&lt;/code> 发出配置变更通知，由 &lt;code>EventDispatcher&lt;/code> 处理该变更通知，然后根据配置的同步策略(&lt;code>http、weboscket、zookeeper、naocs、etcd、consul&lt;/code>)，将配置发送给对应的事件处理器。&lt;/p>
&lt;ul>
&lt;li>如果是 &lt;code>websocket&lt;/code> 同步策略，则将变更后的数据主动推送给 &lt;code>shenyu-web&lt;/code>，并且在网关层，会有对应的 &lt;code>WebsocketDataHandler&lt;/code> 处理器来处理 &lt;code>shenyu-admin&lt;/code> 的数据推送。&lt;/li>
&lt;li>如果是 &lt;code>zookeeper&lt;/code> 同步策略，将变更数据更新到 &lt;code>zookeeper&lt;/code>，而 &lt;code>ZookeeperSyncCache&lt;/code> 会监听到 &lt;code>zookeeper&lt;/code> 的数据变更，并予以处理。&lt;/li>
&lt;li>如果是 &lt;code>http&lt;/code> 同步策略，由网关主动发起长轮询请求，默认有 &lt;code>90s&lt;/code> 超时时间，如果 &lt;code>shenyu-admin&lt;/code> 没有数据变更，则会阻塞 &lt;code>http&lt;/code> 请求，如果有数据发生变更则响应变更的数据信息，如果超过 &lt;code>60s&lt;/code> 仍然没有数据变更则响应空数据，网关层接到响应后，继续发起 &lt;code>http&lt;/code> 请求，反复同样的请求。&lt;/li>
&lt;/ul>
&lt;h3 id="33-流程分析">3.3 流程分析&lt;/h3>
&lt;p>流程分析是从源码的角度，展示服务注册流程，数据同步流程和服务调用流程。&lt;/p>
&lt;h4 id="331-服务注册流程">3.3.1 服务注册流程&lt;/h4>
&lt;ul>
&lt;li>读取dubbo服务&lt;/li>
&lt;/ul>
&lt;p>使用注解&lt;code>@ShenyuDubboClient&lt;/code>标记需要注册到网关的&lt;code>dubbo&lt;/code>服务。&lt;/p>
&lt;p>注解扫描通过&lt;code>ApacheDubboServiceBeanListener&lt;/code>完成，它实现了&lt;code>ApplicationListener&amp;lt;ContextRefreshedEvent&amp;gt;&lt;/code>接口，在&lt;code>Spring&lt;/code>容器启动过程中，发生上下文刷新事件时，开始执行事件处理方法&lt;code>onApplicationEvent()&lt;/code>。在重写的方法逻辑中，读取&lt;code>Dubbo&lt;/code>服务&lt;code>ServiceBean&lt;/code>，构建元数据对象和&lt;code>URI&lt;/code>对象，并向&lt;code>shenyu-admin&lt;/code>注册。&lt;/p>
&lt;p>具体的注册逻辑由注册中心实现，请参考 &lt;code>[客户端接入原理](https://shenyu.apache.org/zh/docs/design/register-center-design/)&lt;/code> 。&lt;/p>
&lt;ul>
&lt;li>处理注册信息&lt;/li>
&lt;/ul>
&lt;p>客户端通过注册中心注册的元数据和&lt;code>URI&lt;/code>数据，在&lt;code>shenyu-admin&lt;/code>端进行处理，负责存储到数据库和同步给&lt;code>shenyu&lt;/code>网关。&lt;code>Dubbo&lt;/code>插件的客户端注册处理逻辑在&lt;code>ShenyuClientRegisterDubboServiceImpl&lt;/code>中。继承关系如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/ShenyuClientRegisterDubboServiceImpl.png" alt="">&lt;/p>
&lt;ul>
&lt;li>ShenyuClientRegisterService：客户端注册服务，顶层接口；&lt;/li>
&lt;li>FallbackShenyuClientRegisterService：注册失败，提供重试操作；&lt;/li>
&lt;li>AbstractShenyuClientRegisterServiceImpl：抽象类，实现部分公共注册逻辑；&lt;/li>
&lt;li>ShenyuClientRegisterDubboServiceImpl：实现&lt;code>Dubbo&lt;/code>插件的注册；&lt;/li>
&lt;/ul>
&lt;p>注册信息包括选择器，规则和元数据。&lt;/p>
&lt;p>整体的&lt;code>dubbo&lt;/code>服务注册流程如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-register-zh.png" alt="">&lt;/p>
&lt;h4 id="332-数据同步流程">3.3.2 数据同步流程&lt;/h4>
&lt;ul>
&lt;li>admin更新数据&lt;/li>
&lt;/ul>
&lt;p>假设在在后台管理系统中，新增一条选择器数据，请求会进入&lt;code>SelectorController&lt;/code>类中的&lt;code>createSelector()&lt;/code>方法，它负责数据的校验，添加或更新数据，返回结果信息。&lt;/p>
&lt;p>在&lt;code>SelectorServiceImpl&lt;/code>类中通过&lt;code>createOrUpdate()&lt;/code>方法完成数据的转换，保存到数据库，发布事件，更新&lt;code>upstream&lt;/code>。&lt;/p>
&lt;p>在&lt;code>Service&lt;/code>类完成数据的持久化操作，即保存数据到数据库。发布变更数据通过&lt;code>eventPublisher.publishEvent()&lt;/code>完成，这个&lt;code>eventPublisher&lt;/code>对象是一个&lt;code>ApplicationEventPublisher&lt;/code>类，这个类的全限定名是&lt;code>org.springframework.context.ApplicationEventPublisher&lt;/code>，发布数据的功能正是是通过&lt;code>Spring&lt;/code>相关的功能来完成的。&lt;/p>
&lt;p>当事件发布完成后，会自动进入到&lt;code>DataChangedEventDispatcher&lt;/code>类中的&lt;code>onApplicationEvent()&lt;/code>方法，根据不同数据类型和数据同步方式进行事件处理。&lt;/p>
&lt;ul>
&lt;li>网关数据同步&lt;/li>
&lt;/ul>
&lt;p>网关在启动时，根据指定的数据同步方式加载不同的配置类，初始化数据同步相关类。&lt;/p>
&lt;p>在接收到数据后，进行反序列化操作，读取数据类型和操作类型。不同的数据类型，有不同的数据处理方式，所以有不同的实现类。但是它们之间也有相同的处理逻辑，所以可以通过模板方法设计模式来实现。相同的逻辑放在抽象类&lt;code>AbstractDataHandler&lt;/code>中的&lt;code>handle()&lt;/code>方法中，不同逻辑就交给各自的实现类。&lt;/p>
&lt;p>新增一条选择器数据，是新增操作，会进入到&lt;code>SelectorDataHandler.doUpdate()&lt;/code>具体的数据处理逻辑中。&lt;/p>
&lt;p>在通用插件数据订阅者&lt;code>CommonPluginDataSubscriber&lt;/code>，负责处理所有插件、选择器和规则信息&lt;/p>
&lt;p>将数据保存到网关的内存中，&lt;code>BaseDataCache&lt;/code>是最终缓存数据的类，通过单例模式实现。选择器数据就存到了&lt;code>SELECTOR_MAP&lt;/code>这个&lt;code>Map&lt;/code>中。在后续使用的时候，也是从这里拿数据。&lt;/p>
&lt;p>上述逻辑用流程图表示如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/data-sync-seq-zh.png" alt="">&lt;/p>
&lt;h4 id="333-服务调用流程">3.3.3 服务调用流程&lt;/h4>
&lt;p>在&lt;code>Dubbo&lt;/code>插件体系中，类继承关系如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/ApacheDubboPlugin.png" alt="">&lt;/p>
&lt;blockquote>
&lt;p>ShenyuPlugin：顶层接口，定义接口方法；&lt;/p>
&lt;p>AbstractShenyuPlugin：抽象类，实现插件共有逻辑；&lt;/p>
&lt;p>AbstractDubboPlugin：dubbo插件抽象类，实现&lt;code>dubbo&lt;/code>共有逻辑（ShenYu网关支持ApacheDubbo和AlibabaDubbo）；&lt;/p>
&lt;p>ApacheDubboPlugin：ApacheDubbo插件。&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>org.apache.shenyu.web.handler.ShenyuWebHandler.DefaultShenyuPluginChain#execute()&lt;/li>
&lt;/ul>
&lt;p>通过&lt;code>ShenYu&lt;/code>网关代理后，请求入口是&lt;code>ShenyuWebHandler&lt;/code>，它实现了&lt;code>org.springframework.web.server.WebHandler&lt;/code>接口，通过责任链设计模式将所有插件连接起来。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.base.AbstractShenyuPlugin#execute()&lt;/li>
&lt;/ul>
&lt;p>当请求到网关时，判断某个插件是否执行，是通过指定的匹配逻辑来完成。在&lt;code>execute()&lt;/code>方法中执行选择器和规则的匹配逻辑。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.global.GlobalPlugin#execute()&lt;/li>
&lt;/ul>
&lt;p>最先被执行的是&lt;code>GlobalPlugin&lt;/code> ，它是一个全局插件，在&lt;code>execute()&lt;/code>方法中构建上下文信息。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.base.RpcParamTransformPlugin#execute()&lt;/li>
&lt;/ul>
&lt;p>接着被执行的是&lt;code>RpcParamTransformPlugin&lt;/code> ， 它负责从&lt;code>http&lt;/code>请求中读取参数，保存到&lt;code>exchange&lt;/code>中，传递给&lt;code>rpc&lt;/code>服务。在&lt;code>execute()&lt;/code>方法中，执行该插件的核心逻辑：从&lt;code>exchange&lt;/code>中获取请求信息，根据请求传入的内容形式处理参数。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.dubbo.common.AbstractDubboPlugin&lt;/li>
&lt;/ul>
&lt;p>然后被执行的是&lt;code>DubboPlugin&lt;/code> 。在&lt;code>doExecute()&lt;/code>方法中，主要是检查元数据和参数。在&lt;code>doDubboInvoker()&lt;/code>方法中设置特殊的上下文信息，然后开始&lt;code>dubbo&lt;/code>的泛化调用。&lt;/p>
&lt;p>在&lt;code>genericInvoker()&lt;/code>方法中：&lt;/p>
&lt;ul>
&lt;li>获取&lt;code>ReferenceConfig&lt;/code>对象；&lt;/li>
&lt;li>获取泛化服务&lt;code>GenericService&lt;/code>对象；&lt;/li>
&lt;li>构造请求参数&lt;code>pair&lt;/code>对象；&lt;/li>
&lt;li>发起异步的泛化调用。&lt;/li>
&lt;/ul>
&lt;p>通过泛化调用就可以实现在网关调用&lt;code>dubbo&lt;/code>服务了。&lt;/p>
&lt;p>&lt;code>ReferenceConfig&lt;/code>对象是支持泛化调用的关键对象 ，它的初始化操作是在数据同步的时候完成的。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.response.ResponsePlugin#execute()&lt;/li>
&lt;/ul>
&lt;p>最后被执行的是&lt;code>ResponsePlugin&lt;/code> ，它统一处理网关的响应结果信息。处理类型由&lt;code>MessageWriter&lt;/code>决定，类继承关系如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/MessageWriter.png" alt="">&lt;/p>
&lt;blockquote>
&lt;p>MessageWriter：接口，定义消息处理方法；&lt;/p>
&lt;p>NettyClientMessageWriter：处理&lt;code>Netty&lt;/code>调用结果；&lt;/p>
&lt;p>RPCMessageWriter：处理&lt;code>RPC&lt;/code>调用结果；&lt;/p>
&lt;p>WebClientMessageWriter：处理&lt;code>WebClient&lt;/code>调用结果；&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>Dubbo&lt;/code>服务调用，处理结果是&lt;code>RPCMessageWriter&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>org.apache.shenyu.plugin.response.strategy.RPCMessageWriter#writeWith()&lt;/li>
&lt;/ul>
&lt;p>在&lt;code>writeWith()&lt;/code>方法中处理响应结果，获取结果或处理异常。&lt;/p>
&lt;p>分析至此，关于&lt;code>Dubbo&lt;/code>插件的源码分析就完成了，分析流程图如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/shenyu-dubbo/dubbo-execute-zh.png" alt="">&lt;/p>
&lt;h2 id="4-小结">4. 小结&lt;/h2>
&lt;p>本文从实际案例出发，由浅入深分析了&lt;code>ShenYu&lt;/code>网关对Dubbo服务的代理过程。涉及到的主要知识点如下：&lt;/p>
&lt;ul>
&lt;li>通过责任链设计模式执行插件；&lt;/li>
&lt;li>使用模板方法设计模式实现&lt;code>AbstractShenyuPlugin&lt;/code>，处理通用的操作类型；&lt;/li>
&lt;li>使用单例设计模式实现缓存数据类&lt;code>BaseDataCache&lt;/code>；&lt;/li>
&lt;li>通过&lt;code>springboot starter&lt;/code>即可引入不同的注册中心和数同步方式，扩展性很好；&lt;/li>
&lt;li>通过&lt;code>admin&lt;/code>支持规则热更新，方便流量管控；&lt;/li>
&lt;li>&lt;code>Disruptor&lt;/code>队列是为了数据与操作解耦，以及数据缓冲。&lt;/li>
&lt;/ul></description></item><item><title>Blog: 谈谈Pixiu的Filter</title><link>https://cn.dubbo.apache.org/zh/blog/2022/02/19/%E8%B0%88%E8%B0%88pixiu%E7%9A%84filter/</link><pubDate>Sat, 19 Feb 2022 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2022/02/19/%E8%B0%88%E8%B0%88pixiu%E7%9A%84filter/</guid><description>
&lt;h2 id="filter的生命周期">&lt;strong>Filter的生命周期&lt;/strong>&lt;/h2>
&lt;p>Pixiu作为一个面向云原生的gateway，通过简单的配置即可代理Http to Dubbo 2、Tripe甚至是Spring Cloud的请求。那Filter是怎样运行的呢？&lt;/p>
&lt;p>首先&lt;strong>Filter Plugin&lt;/strong>向&lt;strong>Filter Manager&lt;/strong>注册自己**，&lt;strong>然后&lt;/strong>Filter Manager&lt;strong>根据配置创建好&lt;/strong>Filter Factory&lt;strong>并持有它们，等待请求来临时，&lt;strong>Manager&lt;/strong>创建一个一次性的用于此次请求的Filter Chain，然后利用&lt;/strong>Factory&lt;strong>创建好&lt;/strong>Decode/Encode Filter&lt;strong>并把它们加入链中，然后按照顺序去运行Decode Filter，然后去请求&lt;/strong>Upstream**，拿到Response再反向运行Encode Filter，让Filter可以访问到Response。&lt;/p>
&lt;p>几个关键的概念：&lt;/p>
&lt;p>&lt;strong>Filter Manager&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>Filter的Manger。。。&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// FilterManager manage filters
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> FilterManager &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filters &lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]HttpFilterFactory
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filtersArray []&lt;span style="color:#719e07">*&lt;/span>HttpFilterFactory
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Filter Plugin&lt;/strong>：定义了Filter的（唯一的）名字和描述如何去创建一个Filter Factory。&lt;/p>
&lt;blockquote>
&lt;p>其实结合Filter Factory的定义，可以认为Plugin是Filter Factory的Factory&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// HttpFilterPlugin describe plugin
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>HttpFilterPlugin &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Kind returns the unique kind name to represent itself.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Kind&lt;/span>() &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// CreateFilterFactory return the filter factory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">CreateFilterFactory&lt;/span>() (HttpFilterFactory, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Filter Factory&lt;/strong>：定义了Filter自身的配置，并且在请求来临时创建真实的Filter并把它添加到FilterChain中&lt;/p>
&lt;blockquote>
&lt;ul>
&lt;li>Config() 的目的是能让Filter Manager能够有机会把配置交给Factory（此时golang泛型还没有落地）&lt;/li>
&lt;li>Apply() 在配置被注入到Factory后，有机会对config做一些检查和提前做一些初始化的工作&lt;/li>
&lt;li>PrepareFilterChain() 创建Filter并加入Filter Chain&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// HttpFilterFactory describe http filter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>HttpFilterFactory &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Config Expose the config so that Filter Manger can inject it, so it must be a pointer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Config&lt;/span>() &lt;span style="color:#268bd2">interface&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Apply After the config is injected, check it or make it to default
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Apply&lt;/span>() &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// PrepareFilterChain create filter and append it to FilterChain
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// Be Careful !!! Do not pass the Factory&amp;#39;s config pointer to the Filter instance,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// Factory&amp;#39;s config may be updated by FilterManager
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">PrepareFilterChain&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>http.HttpContext, chain FilterChain) &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Decode/Encode Filter：&lt;strong>Filter分为两个部分，&lt;strong>Decode&lt;/strong>在实际请求&lt;/strong>Upstream&lt;/strong>之前，所以可以做一些鉴权、限流，把请求在gateway层拦截掉。&lt;strong>Eecode&lt;/strong>则运行在获得&lt;strong>Upstream&lt;/strong>的Response之后，所以可以对返回Log甚至修改Response。&lt;/p>
&lt;blockquote>
&lt;p>一个Filter可以即是Decode Filter，又是Encode Filter，没有限制！&lt;/p>
&lt;p>假设有A、B、C三个Filter，都是Decode/Encode Filter，如果配置的顺序是A、B、C，那么运行将会是下面这样&lt;/p>
&lt;p>在Decode阶段 A-&amp;gt;B-&amp;gt;C，而在Encode阶段，顺序将会反过来！C-&amp;gt;B-&amp;gt;A&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// decode filters will be invoked in the config order: A、B、C, and decode filters will be
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// invoked in the reverse order: C、B、A
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>HttpDecodeFilter &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">Decode&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>http.HttpContext) FilterStatus
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// HttpEncodeFilter after invoke upstream,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// decode filters will be invoked in the reverse order
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>HttpEncodeFilter &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">Encode&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>http.HttpContext) FilterStatus
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>更详细的，每个Decode/Encode Filter可以返回一个FilterStatus来决定继续还是就在这里停下！比如JWT鉴权，token无效时就要及时把401返回给Downstream。当然Decode Filter发出的停止命令只会终止Decode阶段，至于为什么？想想如何做一个Access Log Filter，能在请求失败时也把失败的结果记录下吧来！&lt;/p>
&lt;h2 id="怎样编写一个自定义filter">&lt;strong>怎样编写一个自定义Filter&lt;/strong>&lt;/h2>
&lt;p>我们来尝试写一个简单的Filter，这个Filter将会有简单的配置，在Decode阶段把请求的Body Log出来，并翻转后作为Mock的返回值。最后在Encode阶段根据配置把返回值Log出来。&lt;/p>
&lt;p>1.首先创建一个Filter&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> DemoFilter &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logPrefix &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilter) &lt;span style="color:#268bd2">Decode&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>contexthttp.HttpContext) filter.FilterStatus {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> body, _ &lt;span style="color:#719e07">:=&lt;/span> ioutil.&lt;span style="color:#268bd2">ReadAll&lt;/span>(ctx.Request.Body)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;request body: %s&amp;#34;&lt;/span>, body)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//reverse res str
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> runes &lt;span style="color:#719e07">:=&lt;/span> []&lt;span style="color:#b58900">rune&lt;/span>(&lt;span style="color:#b58900">string&lt;/span>(body))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> i &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>; i &amp;lt; &lt;span style="color:#b58900">len&lt;/span>(runes)&lt;span style="color:#719e07">/&lt;/span>&lt;span style="color:#2aa198">2&lt;/span>; i &lt;span style="color:#719e07">+=&lt;/span> &lt;span style="color:#2aa198">1&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> runes[i], runes[&lt;span style="color:#b58900">len&lt;/span>(runes)&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">-&lt;/span>i] = runes[&lt;span style="color:#b58900">len&lt;/span>(runes)&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">-&lt;/span>i], runes[i]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverse &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">string&lt;/span>(runes)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//mock response
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ctx.&lt;span style="color:#268bd2">SendLocalReply&lt;/span>(&lt;span style="color:#2aa198">200&lt;/span>, []&lt;span style="color:#b58900">byte&lt;/span>(reverse))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> filter.Stop
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilter) &lt;span style="color:#268bd2">Encode&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>contexthttp.HttpContext) filter.FilterStatus {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res &lt;span style="color:#719e07">:=&lt;/span> ctx.SourceResp.(&lt;span style="color:#dc322f">string&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;%s: %s&amp;#34;&lt;/span>, f.logPrefix, res)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> filter.Continue
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>2.创建Filter Factory&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DemoFilterFactory &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> conf &lt;span style="color:#719e07">*&lt;/span>Config
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Config describe the config of Filter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Config &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LogPrefix &lt;span style="color:#dc322f">string&lt;/span> &lt;span style="color:#2aa198">`yaml:&amp;#34;logPrefix,omitempty&amp;#34;`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilterFactory) &lt;span style="color:#268bd2">PrepareFilterChain&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>contexthttp.HttpContext, chain filter.FilterChain) &lt;span style="color:#dc322f">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> demo &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>DemoFilter{logPrefix: f.conf.LogPrefix}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> chain.&lt;span style="color:#268bd2">AppendDecodeFilters&lt;/span>(demo)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> chain.&lt;span style="color:#268bd2">AppendEncodeFilters&lt;/span>(demo)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilterFactory) &lt;span style="color:#268bd2">Config&lt;/span>() &lt;span style="color:#268bd2">interface&lt;/span>{} {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> f.conf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilterFactory) &lt;span style="color:#268bd2">Apply&lt;/span>() &lt;span style="color:#dc322f">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>3.创建Filter Plugin，并注册自己&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//important
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filter.&lt;span style="color:#268bd2">RegisterHttpFilter&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>Plugin{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> Plugin &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>Plugin) &lt;span style="color:#268bd2">Kind&lt;/span>() &lt;span style="color:#dc322f">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dgp.filters.demo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>Plugin) &lt;span style="color:#268bd2">CreateFilterFactory&lt;/span>() (filter.HttpFilterFactory, &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>DemoFilterFactory{conf: &lt;span style="color:#719e07">&amp;amp;&lt;/span>Config{}}, &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>4.配置文件中配置此Filter，并启动Pixiu&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">static_resources&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">listeners&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;net/http&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol_type&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;HTTP&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">socket_address&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;0.0.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>: &lt;span style="color:#2aa198">8888&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filter_chains&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filters&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: dgp.filter.httpconnectionmanager
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">route_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">routes&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">match&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">prefix&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">http_filters&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: dgp.filters.demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">config&lt;/span>:
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>5.访问并查看日志与结果&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl localhost:8888/demo -d &lt;span style="color:#2aa198">&amp;#34;eiv al tse’c&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>c’est la vie%
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>日志&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>2022-02-19T20:20:11.900+0800 INFO demo/demo.go:62 request body: eiv al tse’c
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2022-02-19T20:20:11.900+0800 INFO demo/demo.go:71 : eiv al tse’c
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: 从原理到操作，让你在 Apache APISIX 中代理 Dubbo 服务更便捷</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>&lt;a href="https://cn.dubbo.apache.org/zh/">Apache Dubbo&lt;/a> 是由阿里巴巴开源并捐赠给 Apache 的微服务开发框架，它提供了 RPC 通信与微服务治理两大关键能力。不仅经过了阿里电商场景中海量流量的验证，也在国内的技术公司中被广泛落地。&lt;/p>
&lt;p>在实际应用场景中，Apache Dubbo 一般会作为后端系统间 RPC 调用的实现框架，当需要提供 HTTP 接口给到前端时，会通过一个「胶水层」将 Dubbo Service 包装成 HTTP 接口，再交付到前端系统。&lt;/p>
&lt;p>&lt;a href="https://apisix.apache.org/">Apache APISIX&lt;/a> 是 Apache 软件基金会的顶级开源项目，也是当前最活跃的开源网关项目。作为一个动态、实时、高性能的开源 API 网关，Apache APISIX 提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。&lt;/p>
&lt;p>得益于 Apache Dubbo 的应用场景优势，Apache APISIX 基于开源项目 tengine/mod_dubbo 模块为 Apache Dubbo 服务配备了HTTP 网关能力。通过 dubbo-proxy 插件，可以轻松地将 Dubbo Service 发布为 HTTP 服务。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/apisix-plugin/1.png" alt="架构图">&lt;/p>
&lt;h2 id="如何使用">如何使用&lt;/h2>
&lt;h3 id="入门篇安装使用">入门篇：安装使用&lt;/h3>
&lt;blockquote>
&lt;p>这里我们建议使用 Apache APISIX 2.11 版本镜像进行安装。该版本的 APISIX-Base 中已默认编译了 Dubbo 模块，可直接使用 &lt;code>dubbo-proxy&lt;/code> 插件。&lt;/p>
&lt;/blockquote>
&lt;p>在接下来的操作中，我们将使用 &lt;a href="https://github.com/apache/dubbo-samples">&lt;code>dubbo-samples&lt;/code>&lt;/a> 项目进行部分展示。该项目是一些使用 Apache Dubbo 实现的 Demo 应用，本文中我们采用其中的一个子模块作为 Dubbo Provider。&lt;/p>
&lt;p>在进入正式操作前，我们先简单看下 Dubbo 接口的定义、配置以及相关实现。&lt;/p>
&lt;h4 id="接口实现一览">接口实现一览&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * standard samples dubbo infterace demo
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param context pass http infos
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return Map&amp;lt;String, Object&amp;gt;&amp;lt;/&amp;gt; pass to response http
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> **/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">apisixDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> httpRequestContext&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如上所示，Dubbo 接口的定义是固定的。即方法参数中 &lt;code>Map&lt;/code> 表示 APISIX 传递给 Dubbo Provider 关于 HTTP request 的一些信息（如：header、body&amp;hellip;)。而方法返回值的 &lt;code>Map&lt;/code> 表示 Dubbo Provider 传递给 APISIX 要如何返回 HTTP response 的一些信息。&lt;/p>
&lt;p>接口信息配置好之后可通过 XML 配置方式发布 DemoService。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- service implementation, as same as regular local bean --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.provider.DemoServiceImpl&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- declare the service interface to be exported --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.apisix.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过上述配置后，Consumer 可通过 &lt;code>org.apache.dubbo.samples.apisix.DemoService&lt;/code> 访问其中的&lt;code>apisixDubbo&lt;/code> 方法。具体接口实现如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">apisixDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> httpRequestContext&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> httpRequestContext&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Key = &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, Value = &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> ret &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;body&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo success\n&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// http response body
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ret&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;status&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;200&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// http response status
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ret&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;test&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;123&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// http response header
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ret&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上述代码中，&lt;code>DemoServiceImpl&lt;/code> 会打印接收到的 &lt;code>httpRequestContext&lt;/code>，并通过返回包含有指定 Key 的 Map 对象去描述该 Dubbo 请求的 HTTP 响应。&lt;/p>
&lt;h4 id="操作步骤">操作步骤&lt;/h4>
&lt;ol>
&lt;li>启动 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-tengine#install-dubbo">&lt;code>dubbo-samples&lt;/code>&lt;/a>。&lt;/li>
&lt;li>在 &lt;code>config.yaml&lt;/code> 文件中进行 &lt;code>dubbo-proxy&lt;/code> 插件启用。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Add this in config.yaml&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">plugins&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - ... &lt;span style="color:#586e75"># plugin you need&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - dubbo-proxy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>创建指向 Dubbo Provider 的 Upstream。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H &lt;span style="color:#2aa198">&amp;#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&amp;#39;&lt;/span> -X PUT -d &lt;span style="color:#2aa198">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;nodes&amp;#34;: {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;127.0.0.1:20880&amp;#34;: 1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> },
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;type&amp;#34;: &amp;#34;roundrobin&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>为 DemoService 暴露一个 HTTP 路由。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl http://127.0.0.1:9180/apisix/admin/routes/1 -H &lt;span style="color:#2aa198">&amp;#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&amp;#39;&lt;/span> -X PUT -d &lt;span style="color:#2aa198">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;host&amp;#34;: &amp;#34;example.org&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;uris&amp;#34;: [
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;/demo&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> ],
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;plugins&amp;#34;: {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;dubbo-proxy&amp;#34;: {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;service_name&amp;#34;: &amp;#34;org.apache.dubbo.samples.apisix.DemoService&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;service_version&amp;#34;: &amp;#34;0.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;method&amp;#34;: &amp;#34;apisixDubbo&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> },
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;upstream_id&amp;#34;: 1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="5">
&lt;li>使用 curl 命令请求 Apache APISIX，并查看返回结果。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl http://127.0.0.1:9080/demo -H &lt;span style="color:#2aa198">&amp;#34;Host: example.org&amp;#34;&lt;/span> -X POST --data &lt;span style="color:#2aa198">&amp;#39;{&amp;#34;name&amp;#34;: &amp;#34;hello&amp;#34;}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; HTTP/1.1 &lt;span style="color:#2aa198">200&lt;/span> OK
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Date: Sun, &lt;span style="color:#2aa198">26&lt;/span> Dec &lt;span style="color:#2aa198">2021&lt;/span> 11:33:27 GMT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Content-Type: text/plain; &lt;span style="color:#268bd2">charset&lt;/span>&lt;span style="color:#719e07">=&lt;/span>utf-8
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Content-Length: &lt;span style="color:#2aa198">14&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Connection: keep-alive
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; test: &lt;span style="color:#2aa198">123&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt; Server: APISIX/2.11.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo success
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>:::note 说明
上述代码返回中包含了 &lt;code>test: 123&lt;/code> Header，以及 &lt;code>dubbo success&lt;/code> 字符串作为 Body 体。这与我们在 &lt;code>DemoServiceImpl&lt;/code> 编码的预期效果一致。
:::&lt;/p>
&lt;ol start="6">
&lt;li>查看 Dubbo Provider 的日志。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>Key = content-length, Value = 17
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = host, Value = example.org
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = content-type, Value = application/x-www-form-urlencoded
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = body, Value = [B@70754265
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = accept, Value = */*
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Key = user-agent, Value = curl/7.80.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>:::note 说明
通过 &lt;code>httpRequestContext&lt;/code> 可以拿到 HTTP 请求的 Header 和 Body。其中 Header 会作为 Map 元素，而 Body 中 Key 值是固定的字符串&amp;quot;body&amp;quot;，Value 则代表 Byte 数组。
:::&lt;/p>
&lt;h3 id="进阶篇复杂场景示例">进阶篇：复杂场景示例&lt;/h3>
&lt;p>在上述的简单用例中可以看出，我们确实通过 Apache APISIX 将 Dubbo Service 发布为一个 HTTP 服务，但是在使用过程中的限制也非常明显。比如：接口的参数和返回值都必须要是 &lt;code>Map&amp;lt;String, Object&amp;gt;&lt;/code>。&lt;/p>
&lt;p>那么，如果项目中出现已经定义好、但又不符合上述限制的接口，该如何通过 Apache APISIX 来暴露 HTTP 服务呢？&lt;/p>
&lt;h4 id="操作步骤-1">操作步骤&lt;/h4>
&lt;p>针对上述场景，我们可以通过 HTTP Request Body 描述要调用的 Service 和 Method 以及对应参数，再利用 Java 的反射机制实现目标方法的调用。最后将返回值序列化为 JSON，并写入到 HTTP Response Body 中。&lt;/p>
&lt;p>这样就可以将 Apache APISIX 的 「HTTP to Dubbo」 能力进一步加强，并应用到所有已存在的 Dubbo Service 中。具体操作可参考下方：&lt;/p>
&lt;ol>
&lt;li>为已有项目增加一个 Dubbo Service 用来统一处理 HTTP to Dubbo 的转化。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboInvocationParameter&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String type&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String value&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboInvocation&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String method&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DubboInvocationParameter&lt;span style="color:#719e07">[]&lt;/span> parameters&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">HTTP2DubboService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> context&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Component&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">HTTP2DubboServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> HTTP2DubboService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Autowired&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> ApplicationContext appContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> context&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboInvocation invocation &lt;span style="color:#719e07">=&lt;/span> JSONObject&lt;span style="color:#719e07">.&lt;/span>parseObject&lt;span style="color:#719e07">((&lt;/span>&lt;span style="color:#dc322f">byte&lt;/span>&lt;span style="color:#719e07">[])&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;body&amp;#34;&lt;/span>&lt;span style="color:#719e07">),&lt;/span> DubboInvocation&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object&lt;span style="color:#719e07">[]&lt;/span> args &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[&lt;/span>invocation&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">().&lt;/span>size&lt;span style="color:#719e07">()];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> args&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboInvocationParameter parameter &lt;span style="color:#719e07">=&lt;/span> invocation&lt;span style="color:#719e07">.&lt;/span>getParameters&lt;span style="color:#719e07">().&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>i&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> args&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> JSONObject&lt;span style="color:#719e07">.&lt;/span>parseObject&lt;span style="color:#719e07">(&lt;/span>parameter&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">(),&lt;/span> Class&lt;span style="color:#719e07">.&lt;/span>forName&lt;span style="color:#719e07">(&lt;/span>parameter&lt;span style="color:#719e07">.&lt;/span>getType&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object svc &lt;span style="color:#719e07">=&lt;/span> appContext&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">.&lt;/span>forName&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">.&lt;/span>getService&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object result &lt;span style="color:#719e07">=&lt;/span> svc&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">().&lt;/span>getMethod&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">.&lt;/span>getMethod&lt;span style="color:#719e07">()).&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>args&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> httpResponse &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpResponse&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;status&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">200&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpResponse&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;body&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> JSONObject&lt;span style="color:#719e07">.&lt;/span>toJSONString&lt;span style="color:#719e07">(&lt;/span>result&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> httpResponse&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>通过如下命令请求来发起相关调用。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl http://127.0.0.1:9080/demo -H &lt;span style="color:#2aa198">&amp;#34;Host: example.org&amp;#34;&lt;/span> -X POST --data &lt;span style="color:#2aa198">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;service&amp;#34;: &amp;#34;org.apache.dubbo.samples.apisix.DemoService&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;method&amp;#34;: &amp;#34;createUser&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;parameters&amp;#34;: [
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;type&amp;#34;: &amp;#34;org.apache.dubbo.samples.apisix.User&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;value&amp;#34;: &amp;#34;{&amp;#39;&lt;/span>name&lt;span style="color:#2aa198">&amp;#39;: &amp;#39;&lt;/span>hello&lt;span style="color:#2aa198">&amp;#39;}&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> ]
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="总结">总结&lt;/h2>
&lt;p>本文为大家介绍了如何借助 Apache APISIX 实现 Dubbo Service 的代理，通过引入 &lt;code>dubbo-proxy&lt;/code> 插件便可为 Dubbo 框架的后端系统构建更简单更高效的流量链路。&lt;/p>
&lt;p>希望通过上述操作步骤和用例场景分享，能为大家在相关场景的使用提供借鉴思路。更多关于 &lt;code>dubbo-proxy&lt;/code> 插件的介绍与使用可参考&lt;a href="https://apisix.apache.org/docs/apisix/plugins/dubbo-proxy/">官方文档&lt;/a>。&lt;/p></description></item><item><title>Blog: 2.7.14 发版公告</title><link>https://cn.dubbo.apache.org/zh/blog/2021/09/20/2.7.14-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</link><pubDate>Mon, 20 Sep 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/09/20/2.7.14-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</guid><description>
&lt;h2 id="变动项">变动项&lt;/h2>
&lt;ul>
&lt;li>为 ServiceDiscovery 增加动态配置中心的覆盖规则。（#8389）&lt;/li>
&lt;li>修复当 mock 参数中包含 &amp;lsquo;:&amp;rsquo; 或者 &amp;lsquo;=&amp;rsquo; 符号时无法正常使用的问题。（#8379）&lt;/li>
&lt;li>修复 zone 参数对 ZoneAwareClusterInvoker 配置无法生效的问题。（#8521）&lt;/li>
&lt;li>为序列化白名单检查增加开关，默认为 true。（#8537）&lt;/li>
&lt;li>修复当请求超时时序列化检查的空指针异常。（#8587）&lt;/li>
&lt;li>修复 NetUtils.ignoreNetworkInterface 无法处理网卡中包含 &amp;lsquo;(&amp;rsquo; 符号的问题。（#8629）&lt;/li>
&lt;li>统一获取本地地址的方式。（#8679）&lt;/li>
&lt;li>修复当重试参数为0，依旧会重试1次的问题。（#8743）&lt;/li>
&lt;li>当清除未使用的 invoker 时，立即关闭客户端。（#8756）&lt;/li>
&lt;li>修复 destroy 方法以及 doOverrideIfNecessary 中的异常。（#8683）&lt;/li>
&lt;li>DefaultFuture.closeChannel 根据日志级别选择是否打印请求的详细数据。（#8778）&lt;/li>
&lt;li>使用 MapUtils 替换 AttachmentsAdapter（#8772）&lt;/li>
&lt;/ul>
&lt;h2 id="maven依赖变化">Maven依赖变化&lt;/h2>
&lt;ul>
&lt;li>netty4: 4.1.51.Final -&amp;gt; 4.1.66.Final&lt;/li>
&lt;li>netty4_ssl: 2.0.39.Final -&amp;gt; 2.0.40.Final&lt;/li>
&lt;li>http_client: 4.5.3 -&amp;gt; 4.5.13&lt;/li>
&lt;li>jetty: 9.4.11.v20180605 -&amp;gt; 9.4.43.v20210629&lt;/li>
&lt;li>apollo_client: 1.1.1 -&amp;gt; 1.8.0&lt;/li>
&lt;li>tomcat_embed: 8.5.31-&amp;gt; 9.0.48&lt;/li>
&lt;li>commons_io: 2.6 -&amp;gt; 2.7&lt;/li>
&lt;li>curator: 5.0.0 -&amp;gt; 5.1.0&lt;/li>
&lt;li>hessian_lite: 3.2.8 -&amp;gt; 3.2.11&lt;/li>
&lt;/ul></description></item><item><title>Blog: 3.0.2.1 发版公告</title><link>https://cn.dubbo.apache.org/zh/blog/2021/08/23/3.0.2.1-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</link><pubDate>Mon, 23 Aug 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/08/23/3.0.2.1-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</guid><description>
&lt;p>这是 3.0.2 的错误修正版本。
除了以下更改外，与版本 3.0.2 完全相同。&lt;/p>
&lt;h2 id="bug-修复">Bug 修复&lt;/h2>
&lt;ul>
&lt;li>修复 nacos group 在消费者端不生效的问题 (#8533)&lt;/li>
&lt;li>修复请求超时时序列化检查的 NPE (#8547)&lt;/li>
&lt;li>兼容使用 dubbo-all 时未导入 farbic-io 包的问题 (#8546)&lt;/li>
&lt;/ul></description></item><item><title>Blog: 3.0.2 发版公告</title><link>https://cn.dubbo.apache.org/zh/blog/2021/08/18/3.0.2-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</link><pubDate>Wed, 18 Aug 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/08/18/3.0.2-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</guid><description>
&lt;h2 id="新特性">新特性&lt;/h2>
&lt;ul>
&lt;li>支持通过 @DubboService 注解暴露泛化服务&lt;/li>
&lt;li>元数据中心xml格式的配置支持 protocol 和 port 属性&lt;/li>
&lt;li>兼容 curator5 以上的版本&lt;/li>
&lt;li>点对点调用支持*通配符进行匹配, 一个提供者地址可对应多个接口&lt;/li>
&lt;li>为应用级别的服务发现增加动态配置去进行规则覆盖&lt;/li>
&lt;li>对提供者测的动态配置覆盖规则提供开关，可以使提供者无视动态配置，不重新暴露&lt;/li>
&lt;li>支持 native image&lt;/li>
&lt;li>提供取消执行 shutdown hook 的开关&lt;/li>
&lt;li>支持 Kubernetes Mesh 的服务治理规则&lt;/li>
&lt;li>Netty 连接支持 SSL 配置&lt;/li>
&lt;/ul>
&lt;h2 id="bug-修复">Bug 修复&lt;/h2>
&lt;ul>
&lt;li>DubboBootStrap start 重复调用后，动态配置被覆盖&lt;/li>
&lt;li>动态配置规则被删除后，依然生效&lt;/li>
&lt;li>triple 协议在暴露时会抛出空指针异常&lt;/li>
&lt;li>ConfigCenterConfig.setAddress 方法会覆盖掉 username 属性&lt;/li>
&lt;li>DefaultFuture.closeChannel 会销毁掉消费端测的线程池&lt;/li>
&lt;li>TripleClientHandler.writeRequest 抛出空指针异常&lt;/li>
&lt;li>解析3.0迁移规则异常时会抛出空指针异常&lt;/li>
&lt;li>Activated Extensions 的顺序性问题&lt;/li>
&lt;li>URLAddress.parse 方法在解析 ipv6 地址时存在问题&lt;/li>
&lt;li>用户自定义的参数在 properties 配置中不生效&lt;/li>
&lt;li>同时使用 API 模式和 Spring 模式配置时属性，Config Id 存在覆盖问题&lt;/li>
&lt;li>应用级别服务发现在启动时不生效&lt;/li>
&lt;li>Nacos 注册中心无法动态感知提供者的数量变化&lt;/li>
&lt;li>${dubbo.application} 在 xml 文件中无法被 spring 的 placeholder 规则给解析替换&lt;/li>
&lt;li>获取实例参数的顺序问题，先去获取了实例级别参数，再去获取接口级别参数&lt;/li>
&lt;li>当 DubboConfigBeanInitializer 不存在时，注册应用启动监听器会抛出异常&lt;/li>
&lt;li>Mock 时 参数中包含 &amp;lsquo;:&amp;rsquo; 或者 &amp;lsquo;=&amp;rsquo; 字符时，不生效&lt;/li>
&lt;li>删除 Mesh 规则时空指针异常&lt;/li>
&lt;/ul>
&lt;h2 id="优化">优化&lt;/h2>
&lt;ul>
&lt;li>抓住 RemovalTask 的异常，保证信号量能够释放&lt;/li>
&lt;li>通过 唯一 service name 检查 ReferenceConfig/ServiceConfig 是否重复&lt;/li>
&lt;li>优化生成随机数的性能&lt;/li>
&lt;li>如果用户使用接口级别去做服务发现，不发布应用与接口的映射数据到元数据中心&lt;/li>
&lt;li>使用 StringBuilder#append(Char) 提升性能&lt;/li>
&lt;li>保证 GRPC 编译的类文件中接口的顺序&lt;/li>
&lt;li>优化 reference bean 的占位符解析&lt;/li>
&lt;li>MergeableClusterInvoker 中使用 CompletableFuture#get(long, TimeUnit) 去提升性能&lt;/li>
&lt;li>内置服务 MetadataService 不延迟暴露&lt;/li>
&lt;li>优化 ConfigBean 和 Bootstrap 的启动逻辑&lt;/li>
&lt;li>优化 Config 检查是否重复的逻辑&lt;/li>
&lt;li>使用 Ring 数据结构去进行注册通知&lt;/li>
&lt;li>优化动态配置的初始化逻辑&lt;/li>
&lt;li>ConfigManager 使用 ConcurrentHashMap 去移除锁逻辑，提升 equals 和 toString 性能&lt;/li>
&lt;li>优化 MetadataInfo equals 方法 以及 Instance Listener&lt;/li>
&lt;li>优化异步 export/refer 逻辑&lt;/li>
&lt;li>使用 TreeSet 数据结构保证应用级别服务发现时应用名称的顺序一致&lt;/li>
&lt;li>RegistryNotifier 的第一个十次通知不延迟&lt;/li>
&lt;li>dubbo-compile 编译使用新的格式生成 stub&lt;/li>
&lt;li>Mesh 服务治理规则在动态配置中心的分组和其他规则分组统一，由 DEFAULT_GROUP 改成 dubbo&lt;/li>
&lt;li>使用 nacos 用作注册中心时，可以在注册中心地址中使用参数来改变在 nacos 中的分组&lt;/li>
&lt;li>计算 ServiceInfo 的 reversion 时，移除运行时参数，避免生成多个 reversion&lt;/li>
&lt;li>Nacos 注册中心抛出异常时，将异常封装称 RpcException 抛出&lt;/li>
&lt;li>禁止动态配置中心对一些权限参数进行动态修改&lt;/li>
&lt;li>优化 Config Bean 的初始化流程，并兼容 spring 3.x/4.1.x&lt;/li>
&lt;li>Bootstrap.start 方法可重入，暴露或引用新的服务&lt;/li>
&lt;li>将 org.apache.dubbo 包中的类默认添加到白名单中&lt;/li>
&lt;li>保证生成的 Config 的 Id 唯一，并检查 Config 是否之前存在&lt;/li>
&lt;li>Javaassist 兼容改变 override 声明字段&lt;/li>
&lt;li>重构解码时的检查逻辑，当找不到 path, version 对应的提供者是，抛出异常&lt;/li>
&lt;li>当 ApplicationModel 为 null 时，兼容 adaptive extensions&lt;/li>
&lt;/ul>
&lt;h2 id="代码质量提升">代码质量提升&lt;/h2>
&lt;p>感谢以下提高 Apache Dubbo 的稳定性的贡献。&lt;/p>
&lt;p>&lt;a href="https://github.com/apache/dubbo/pull/8111">#8111&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8147">#8147&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8164">#8164&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8177">#8177&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8180">#8180&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8161">#8161&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8183">#8183&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8205">#8205&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8215">#8215&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8173">#8173&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8219">#8219&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8228">#8228&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8232">#8232&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8230">#8230&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8236">#8236&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8260">#8260&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8262">#8262&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8252">#8252&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8246">#8246&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8208">#8208&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8278">#8278&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8267">#8267&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8277">#8277&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8291">#8291&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8296">#8296&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8302">#8302&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8175">#8175&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8319">#8319&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8309">#8309&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8336">#8336&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8332">#8332&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8328">#8328&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8355">#8355&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8396">#8396&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8401">#8401&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8395">#8395&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8415">#8415&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8406">#8406&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8411">#8411&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8418">#8418&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8439">#8439&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8404">#8404&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8443">#8443&lt;/a>&lt;/p>
&lt;h2 id="maven依赖变化">Maven依赖变化&lt;/h2>
&lt;ul>
&lt;li>移除依赖: org.eclipse.collections:eclipse-collections&lt;/li>
&lt;li>移除依赖: com.google.guava:guava&lt;/li>
&lt;li>jetty: 9.4.11.v20180605 -&amp;gt; 9.4.43.v20210629&lt;/li>
&lt;li>apollo client: 1.1.1 -&amp;gt; 1.8.0&lt;/li>
&lt;li>snakeyaml: 1.20 -&amp;gt; 1.29&lt;/li>
&lt;li>tomcat embed: 8.5.31 -&amp;gt; 8.5.69&lt;/li>
&lt;li>nacos client: 2.0.0 -&amp;gt; 2.0.2&lt;/li>
&lt;li>swagger: 1.5.19 -&amp;gt; 1.5.24&lt;/li>
&lt;li>hessian_lite: 3.2.8 -&amp;gt; 3.2.11&lt;/li>
&lt;/ul></description></item><item><title>Blog: 3.0.1 发版公告</title><link>https://cn.dubbo.apache.org/zh/blog/2021/07/02/3.0.1-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</link><pubDate>Fri, 02 Jul 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/07/02/3.0.1-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</guid><description>
&lt;h2 id="优化">优化&lt;/h2>
&lt;ul>
&lt;li>重构服务自省映射关系，支持地址迁移时直接配置上游应用名&lt;/li>
&lt;li>为 Spring 扫描添加缓存&lt;/li>
&lt;li>优化配置覆盖逻辑&lt;/li>
&lt;li>支持 Servlet 环境下控制 Dubbo 生命周期&lt;/li>
&lt;li>添加 ServiceListener 用于监听 ServiceConfig&lt;/li>
&lt;li>优化方法回调参数设置&lt;/li>
&lt;/ul>
&lt;h2 id="bug-修复">Bug 修复&lt;/h2>
&lt;ul>
&lt;li>启用强制校验&lt;/li>
&lt;li>更正多注册中心情况下，一个注册中心启动时无地址就销毁的逻辑&lt;/li>
&lt;li>移除冗余的日志输出&lt;/li>
&lt;li>忽略无效的 MetadataReportConfig&lt;/li>
&lt;li>修复消费端启动时 NPE 的情况&lt;/li>
&lt;li>修复若干和低版本兼容问题&lt;/li>
&lt;li>修复若干应用级服务发现逻辑存在的问题&lt;/li>
&lt;li>优化地址迁移规则，支持应用级地址重订阅&lt;/li>
&lt;li>修复 MetadataInfo 存在 NPE 的情况&lt;/li>
&lt;li>修复应用级注册到注册中心的实例信息被错误覆盖的问题&lt;/li>
&lt;/ul>
&lt;h2 id="代码质量提升">代码质量提升&lt;/h2>
&lt;p>感谢以下提高 Apache Dubbo 的稳定性的贡献。&lt;/p>
&lt;p>&lt;a href="https://github.com/apache/dubbo/pull/8043">#8043&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8044">#8044&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8048">#8048&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8071">#8071&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8119">#8119&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8132">#8132&lt;/a>&lt;/p></description></item><item><title>Blog: Dubbo3 应用级服务发现</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;h2 id="1-服务发现service-discovery-概述">1 服务发现（Service Discovery） 概述&lt;/h2>
&lt;p>从 Internet 刚开始兴起，如何动态感知后端服务的地址变化就是一个必须要面对的问题，为此人们定义了 DNS 协议，基于此协议，调用方只需要记住由固定字符串组成的域名，就能轻松完成对后端服务的访问，而不用担心流量最终会访问到哪些机器 IP，因为有代理组件会基于 DNS 地址解析后的地址列表，将流量透明的、均匀的分发到不同的后端机器上。&lt;/p>
&lt;p>在使用微服务构建复杂的分布式系统时，如何感知 backend 服务实例的动态上下线，也是微服务框架最需要关心并解决的问题之一。业界将这个问题称之为 - 微服务的地址发现（Service Discovery），业界比较有代表性的微服务框架如 SpringCloud、Dubbo 等都抽象了强大的动态地址发现能力，并且为了满足微服务业务场景的需求，绝大多数框架的地址发现都是基于自己设计的一套机制来实现，因此在能力、灵活性上都要比传统 DNS 丰富得多。如 SpringCloud 中常用的 Eureka， Dubbo 中常用的 Zookeeper、Nacos 等，这些注册中心实现不止能够传递地址（IP + Port），还包括一些微服务的 Metadata 信息，如实例序列化类型、实例方法列表、各个方法级的定制化配置等。&lt;/p>
&lt;p>下图是微服务中 Service Discovery 的基本工作原理图，微服务体系中的实例大概可分为三种角色：服务提供者（Provider）、服务消费者（Consumer）和注册中心（Registry）。而不同框架实现间最主要的区别就体现在注册中心数据的组织：地址如何组织、以什么粒度组织、除地址外还同步哪些数据？&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-1.png" alt="img1">&lt;/p>
&lt;p>我们今天这篇文章就是围绕这三个角色展开，重点看下 Dubbo 中对于服务发现方案的设计，包括之前老的服务发现方案的优势和缺点，以及 Dubbo 3.0 中正在设计、开发中的全新的&lt;strong>面向应用粒度的地址发现方案&lt;/strong>，我们期待这个新的方案能做到：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>支持几十万/上百万级集群实例的地址发现&lt;/strong>&lt;/li>
&lt;li>&lt;strong>与不同的微服务体系（如 Spring Cloud）实现在地址发现层面的互通&lt;/strong>&lt;/li>
&lt;/ul>
&lt;h2 id="2-dubbo-地址发现机制解析">2 Dubbo 地址发现机制解析&lt;/h2>
&lt;p>我们先以一个 DEMO 应用为例，来快速的看一下 Dubbo “接口粒度”服务发现与“应用粒度”服务发现体现出来的区别。这里我们重点关注 Provider 实例是如何向注册中心注册的，并且，为了体现注册中心数据量变化，我们观察的是两个 Provider 实例的场景。&lt;/p>
&lt;p>&lt;strong>应用 DEMO 提供的服务列表如下：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.GreetingService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;greetingService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们示例注册中心实现采用的是 Zookeeper ，启动 192.168.0.103 和 192.168.0.104 两个实例后，以下是两种模式下注册中心的实际数据&lt;/p>
&lt;p>&lt;strong>1. “接口粒度” 服务发现&lt;/strong>&lt;/p>
&lt;p>192.168.0.103 实例注册的数据&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>dubbo://192.168.0.103:20880/org.apache.dubbo.samples.basic.api.DemoService?anyhost=true&amp;amp;application=demo-provider&amp;amp;default=true&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=org.apache.dubbo.samples.basic.api.DemoService&amp;amp;methods=testVoid,sayHello&amp;amp;pid=995&amp;amp;release=2.7.7&amp;amp;side=provider&amp;amp;timestamp=1596988171266
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo://192.168.0.103:20880/org.apache.dubbo.samples.basic.api.GreetingService?anyhost=true&amp;amp;application=demo-provider&amp;amp;default=true&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=org.apache.dubbo.samples.basic.api.GreetingService&amp;amp;methods=greeting&amp;amp;pid=995&amp;amp;release=2.7.7&amp;amp;side=provider&amp;amp;timestamp=1596988170816
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>192.168.0.104 实例注册的数据&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>dubbo://192.168.0.104:20880/org.apache.dubbo.samples.basic.api.DemoService?anyhost=true&amp;amp;application=demo-provider&amp;amp;default=true&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=org.apache.dubbo.samples.basic.api.DemoService&amp;amp;methods=testVoid,sayHello&amp;amp;pid=995&amp;amp;release=2.7.7&amp;amp;side=provider&amp;amp;timestamp=1596988171266
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo://192.168.0.104:20880/org.apache.dubbo.samples.basic.api.GreetingService?anyhost=true&amp;amp;application=demo-provider&amp;amp;default=true&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=org.apache.dubbo.samples.basic.api.GreetingService&amp;amp;methods=greeting&amp;amp;pid=995&amp;amp;release=2.7.7&amp;amp;side=provider&amp;amp;timestamp=1596988170816
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>2. “应用粒度” 服务发现&lt;/strong>&lt;/p>
&lt;p>192.168.0.103 与 192.168.0.104 两个实例共享一份注册中心数据，如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;demo-provider&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;id&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;192.168.0.103:20880&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;address&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;192.168.0.103&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;port&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">20880&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;metadata&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dubbo.endpoints&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;[{\&amp;#34;port\&amp;#34;:20880,\&amp;#34;protocol\&amp;#34;:\&amp;#34;dubbo\&amp;#34;}]&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dubbo.metadata.storage-type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;local&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dubbo.revision&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;6785535733750099598&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;time&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">1583461240877&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;demo-provider&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;id&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;192.168.0.104:20880&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;address&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;192.168.0.104&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;port&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">20880&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;metadata&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dubbo.endpoints&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;[{\&amp;#34;port\&amp;#34;:20880,\&amp;#34;protocol\&amp;#34;:\&amp;#34;dubbo\&amp;#34;}]&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dubbo.metadata.storage-type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;local&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;dubbo.revision&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;7829635812370099387&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;time&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">1583461240877&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对比以上两种不同粒度的服务发现模式，从 “接口粒度” 升级到 “应用粒度” 后我们可以总结出最大的区别是：注册中心数据量不再与接口数成正比，不论应用提供有多少接口，注册中心只有一条实例数据。&lt;/p>
&lt;p>那么接下来我们详细看下这个变化给 Dubbo 带来了哪些好处。&lt;/p>
&lt;h2 id="3-dubbo-应用级服务发现的意义">3 Dubbo 应用级服务发现的意义&lt;/h2>
&lt;p>我们先说结论，应用级服务发现给 Dubbo 带来以下优势：&lt;/p>
&lt;ol>
&lt;li>与业界主流微服务模型对齐，比如 SpringCloud、Kubernetes Native Service等。&lt;/li>
&lt;li>提升性能与可伸缩性。注册中心数据的重新组织（减少），能最大幅度的减轻注册中心的存储、推送压力，进而减少 Dubbo Consumer 侧的地址计算压力；集群规模也开始变得可预测、可评估（与 RPC 接口数量无关，只与实例部署规模相关）。&lt;/li>
&lt;/ol>
&lt;h3 id="31-对齐主流微服务模型">3.1 对齐主流微服务模型&lt;/h3>
&lt;p>自动、透明的实例地址发现（负载均衡）是所有微服务框架需要解决的事情，这能让后端的部署结构对上游微服务透明，上游服务只需要从收到的地址列表中选取一个，发起调用就可以了。要实现以上目标，涉及两个关键点的自动同步：&lt;/p>
&lt;ul>
&lt;li>实例地址，服务消费方需要知道地址以建立连接&lt;/li>
&lt;li>RPC 方法定义，服务消费方需要知道 RPC 服务的具体定义，不论服务类型是 rest 或 rmi 等。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-2.png" alt="img2">&lt;/p>
&lt;p>对于 RPC 实例间借助注册中心的数据同步，REST 定义了一套非常有意思的成熟度模型，感兴趣的朋友可以参考这里的链接 &lt;a href="https://www.martinfowler.com/articles/richardsonMaturityModel.html">https://www.martinfowler.com/articles/richardsonMaturityModel.html&lt;/a>， 按照文章中的 4 级成熟度定义，Dubbo 当前基于接口粒度的模型可以对应到 L4 级别。&lt;/p>
&lt;p>接下来，我们看看 Dubbo、SpringCloud 以及 Kubernetes 分别是怎么围绕自动化的实例地址发现这个目标设计的。&lt;/p>
&lt;p>&lt;strong>1. Spring Cloud&lt;/strong>&lt;/p>
&lt;p>Spring Cloud 通过注册中心只同步了应用与实例地址，消费方可以基于实例地址与服务提供方建立连接，但是消费方对于如何发起 http 调用（SpringCloud 基于 rest 通信）一无所知，比如对方有哪些 http endpoint，需要传入哪些参数等。&lt;/p>
&lt;p>RPC 服务这部分信息目前都是通过线下约定或离线的管理系统来协商的。这种架构的优缺点总结如下。
优势：部署结构清晰、地址推送量小；
缺点：地址订阅需要指定应用名， provider 应用变更（拆分）需消费端感知；RPC 调用无法全自动同步。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-3.png" alt="img3">&lt;/p>
&lt;p>&lt;strong>2. Dubbo&lt;/strong>&lt;/p>
&lt;p>Dubbo 通过注册中心同时同步了实例地址和 RPC 方法，因此其能实现 RPC 过程的自动同步，面向 RPC 编程、面向 RPC 治理，对后端应用的拆分消费端无感知，其缺点则是地址推送数量变大，和 RPC 方法成正比。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-4.png" alt="img4">&lt;/p>
&lt;p>&lt;strong>3. Dubbo + Kubernetes&lt;/strong>&lt;/p>
&lt;p>Dubbo 要支持 Kubernetes native service，相比之前自建注册中心的服务发现体系来说，在工作机制上主要有两点变化：&lt;/p>
&lt;ul>
&lt;li>服务注册由平台接管，provider 不再需要关心服务注册&lt;/li>
&lt;li>consumer 端服务发现将是 Dubbo 关注的重点，通过对接平台层的 API-Server、DNS 等，Dubbo client 可以通过一个 &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/">Service Name&lt;/a>（通常对应到 Application Name）查询到一组 Endpoints（一组运行 provider 的 pod），通过将 Endpoints 映射到 Dubbo 内部地址列表，以驱动 Dubbo 内置的负载均衡机制工作。&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>Kubernetes Service 作为一个抽象概念，怎么映射到 Dubbo 是一个值得讨论的点&lt;/p>
&lt;ul>
&lt;li>Service Name - &amp;gt; Application Name，Dubbo 应用和 Kubernetes 服务一一对应，对于微服务运维和建设环节透明，与开发阶段解耦。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: provider-app-name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: provider-app-name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">protocol&lt;/span>: TCP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">targetPort&lt;/span>: &lt;span style="color:#2aa198">9376&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Service Name - &amp;gt; Dubbo RPC Service，Kubernetes 要维护调度的服务与应用内建 RPC 服务绑定，维护的服务数量变多。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: rpc-service-1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: provider-app-name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>: &lt;span style="color:#586e75">##&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: rpc-service-2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: provider-app-name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>: &lt;span style="color:#586e75">##&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: rpc-service-N
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: provider-app-name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>: &lt;span style="color:#586e75">##&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/blockquote>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-5.png" alt="img5">&lt;/p>
&lt;p>结合以上几种不同微服务框架模型的分析，我们可以发现，Dubbo 与 SpringCloud、Kubernetes 等不同产品在微服务的抽象定义上还是存在很大不同的。SpringCloud 和 Kubernetes 在微服务的模型抽象上还是比较接近的，两者基本都只关心实例地址的同步，如果我们去关心其他的一些服务框架产品，会发现它们绝大多数也是这么设计的；&lt;/p>
&lt;blockquote>
&lt;p>即 REST 成熟度模型中的 L3 级别。&lt;/p>
&lt;/blockquote>
&lt;p>对比起来 Dubbo 则相对是比较特殊的存在，更多的是从 RPC 服务的粒度去设计的。&lt;/p>
&lt;blockquote>
&lt;p>对应 REST 成熟度模型中的 L4 级别。&lt;/p>
&lt;/blockquote>
&lt;p>如我们上面针对每种模型做了详细的分析，每种模型都有其优势和不足。而我们最初决定 Dubbo 要做出改变，往其他的微服务发现模型上的对齐，是我们最早在确定 Dubbo 的云原生方案时，我们发现要让 Dubbo 去支持 Kubernetes Native Service，模型对齐是一个基础条件；另一点是来自用户侧对 Dubbo 场景化的一些工程实践的需求，得益于 Dubbo 对多注册、多协议能力的支持，使得 Dubbo 联通不同的微服务体系成为可能，而服务发现模型的不一致成为其中的一个障碍，这部分的场景描述请参见以下文章：&lt;a href="https://developer.aliyun.com/article/740260">Dubbo 如何成为连接异构微服务体系的最佳服务开发框架&lt;/a>&lt;/p>
&lt;h3 id="32-更大规模的微服务集群---解决性能瓶颈">3.2 更大规模的微服务集群 - 解决性能瓶颈&lt;/h3>
&lt;p>这部分涉及到和注册中心、配置中心的交互，关于不同模型下注册中心数据的变化，之前原理部分我们简单分析过。为更直观的对比服务模型变更带来的推送效率提升，我们来通过一个示例看一下不同模型注册中心的对比：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-6.png" alt="img6">&lt;/p>
&lt;p>图中左边是微服务框架的一个典型工作流程，Provider 和 Consumer 通过注册中心实现自动化的地址通知。其中，Provider 实例的信息如图中表格所示：
应用 DEMO 包含三个接口 DemoService 1 2 3，当前实例的 ip 地址为 10.210.134.30。&lt;/p>
&lt;ul>
&lt;li>对于 Spring Cloud 和 Kubernetes 模型，注册中心只会存储一条 &lt;code>DEMO - 10.210.134.30+metadata&lt;/code> 的数据；&lt;/li>
&lt;li>对于老的 Dubbo 模型，注册中心存储了三条接口粒度的数据，分别对应三个接口 DemoService 1 2 3，并且很多的址数据都是重复的；&lt;/li>
&lt;/ul>
&lt;p>可以总结出，基于应用粒度的模型所存储和推送的数据量是和应用、实例数成正比的，只有当我们的应用数增多或应用的实例数增长时，地址推送压力才会上涨。
而对于基于接口粒度的模型，数据量是和接口数量正相关的，鉴于一个应用通常发布多个接口的现状，这个数量级本身比应用粒度是要乘以倍数的；另外一个关键点在于，接口粒度导致的集群规模评估的不透明，相对于实例、应用增长都通常是在运维侧的规划之中，接口的定义更多的是业务侧的内部行为，往往可以绕过评估给集群带来压力。&lt;/p>
&lt;p>以 Consumer 端服务订阅举例，根据我对社区部分 Dubbo 中大规模头部用户的粗略统计，根据受统计公司的实际场景，一个 Consumer 应用要消费（订阅）的 Provier 应用数量往往要超过 10 个，而具体到其要消费（订阅）的的接口数量则通常要达到 30 个，平均情况下 Consumer 订阅的 3 个接口来自同一个 Provider 应用，如此计算下来，如果以应用粒度为地址通知和选址基本单位，则平均地址推送和计算量将下降 60% 还要多，
而在极端情况下，也就是当 Consumer 端消费的接口更多的来自同一个应用时，这个地址推送与内存消耗的占用将会进一步得到降低，甚至可以超过 80% 以上。&lt;/p>
&lt;p>一个典型的极端场景即是 Dubbo 体系中的网关型应用，有些网关应用消费（订阅）达 100+ 应用，而消费（订阅）的服务有 1000+ ，平均有 10 个接口来自同一个应用，如果我们把地址推送和计算的粒度改为应用，则地址推送量从原来的 n * 1000 变为 n * 100，地址数量降低可达近 90%。&lt;/p>
&lt;h2 id="4-应用级服务发现工作原理">4 应用级服务发现工作原理&lt;/h2>
&lt;h3 id="41-设计原则">4.1 设计原则&lt;/h3>
&lt;p>上面一节我们从&lt;strong>服务模型&lt;/strong>及&lt;strong>支撑大规模集群&lt;/strong>的角度分别给出了 Dubbo 往应用级服务发现靠拢的好处和原因，但这么做的同时接口粒度的服务治理能力还是要继续保留，这是 Dubbo 框架编程模型易用性、服务治理能力优势的基础。
以下是我认为我们做服务模型迁移仍要坚持的设计原则&lt;/p>
&lt;ul>
&lt;li>新的服务发现模型要实现对原有 Dubbo 消费端开发者的无感知迁移，即 Dubbo 继续面向 RPC 服务编程、面向 RPC 服务治理，做到对用户侧完全无感知。&lt;/li>
&lt;li>建立 Consumer 与 Provider 间的自动化 RPC 服务元数据协调机制，解决传统微服务模型无法同步 RPC 级接口配置的缺点。&lt;/li>
&lt;/ul>
&lt;h3 id="42-基本原理详解">4.2 基本原理详解&lt;/h3>
&lt;p>应用级服务发现作为一种新的服务发现机制，和以前 Dubbo 基于 RPC 服务粒度的服务发现在核心流程上基本上是一致的：即服务提供者往注册中心注册地址信息，服务消费者从注册中心拉取&amp;amp;订阅地址信息。&lt;/p>
&lt;p>这里主要的不同有以下两点：&lt;/p>
&lt;h4 id="421-注册中心数据以应用---实例列表格式组织不再包含-rpc-服务信息">4.2.1 注册中心数据以“应用 - 实例列表”格式组织，不再包含 RPC 服务信息&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-7.png" alt="img7">&lt;/p>
&lt;p>以下是每个 Instance metadata 的示例数据，总的原则是 metadata 只包含当前 instance 节点相关的信息，不涉及 RPC 服务粒度的信息。&lt;/p>
&lt;p>总体信息概括如下：实例地址、实例各种环境标、metadata service 元数据、其他少量必要属性。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;provider-app-name&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;id&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;192.168.0.102:20880&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;address&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;192.168.0.102&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;port&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">20880&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;sslPort&amp;#34;&lt;/span>: &lt;span style="color:#cb4b16">null&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;payload&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;id&amp;#34;&lt;/span>: &lt;span style="color:#cb4b16">null&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;provider-app-name&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;metadata&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;metadataService&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;{\&amp;#34;dubbo\&amp;#34;:{\&amp;#34;version\&amp;#34;:\&amp;#34;1.0.0\&amp;#34;,\&amp;#34;dubbo\&amp;#34;:\&amp;#34;2.0.2\&amp;#34;,\&amp;#34;release\&amp;#34;:\&amp;#34;2.7.5\&amp;#34;,\&amp;#34;port\&amp;#34;:\&amp;#34;20881\&amp;#34;}}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;endpoints&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;[{\&amp;#34;port\&amp;#34;:20880,\&amp;#34;protocol\&amp;#34;:\&amp;#34;dubbo\&amp;#34;}]&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;storage-type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;local&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;revision&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;6785535733750099598&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;registrationTimeUTC&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">1583461240877&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;serviceType&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;DYNAMIC&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;uriSpec&amp;#34;&lt;/span>: &lt;span style="color:#cb4b16">null&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="422-client--server-自行协商-rpc-方法信息">4.2.2 Client – Server 自行协商 RPC 方法信息&lt;/h4>
&lt;p>在注册中心不再同步 RPC 服务信息后，服务自省在服务消费端和提供端之间建立了一条内置的 RPC 服务信息协商机制，这也是“服务自省”这个名字的由来。服务端实例会暴露一个预定义的 MetadataService RPC 服务，消费端通过调用 MetadataService 获取每个实例 RPC 方法相关的配置信息。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-8.png" alt="img8">&lt;/p>
&lt;p>当前 MetadataService 返回的数据格式如下，&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>[
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo://192.168.0.102:20880/org.apache.dubbo.demo.DemoService?anyhost=true&amp;amp;application=demo-provider&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=org.apache.dubbo.demo.DemoService&amp;amp;methods=sayHello&amp;amp;pid=9585&amp;amp;release=2.7.5&amp;amp;side=provider&amp;amp;timestamp=1583469714314&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo://192.168.0.102:20880/org.apache.dubbo.demo.HelloService?anyhost=true&amp;amp;application=demo-provider&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=org.apache.dubbo.demo.DemoService&amp;amp;methods=sayHello&amp;amp;pid=9585&amp;amp;release=2.7.5&amp;amp;side=provider&amp;amp;timestamp=1583469714314&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo://192.168.0.102:20880/org.apache.dubbo.demo.WorldService?anyhost=true&amp;amp;application=demo-provider&amp;amp;deprecated=false&amp;amp;dubbo=2.0.2&amp;amp;dynamic=true&amp;amp;generic=false&amp;amp;interface=org.apache.dubbo.demo.DemoService&amp;amp;methods=sayHello&amp;amp;pid=9585&amp;amp;release=2.7.5&amp;amp;side=provider&amp;amp;timestamp=1583469714314&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>熟悉 Dubbo 基于 RPC 服务粒度的服务发现模型的开发者应该能看出来，服务自省机制机制将以前注册中心传递的 URL 一拆为二：&lt;/p>
&lt;ul>
&lt;li>一部分和实例相关的数据继续保留在注册中心，如 ip、port、机器标识等。&lt;/li>
&lt;li>另一部分和 RPC 方法相关的数据从注册中心移除，转而通过 MetadataService 暴露给消费端。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>理想情况下是能达到数据按照实例、RPC 服务严格区分开来，但明显可以看到以上实现版本还存在一些数据冗余，有些数据也还未合理划分。尤其是 MetadataService 部分，其返回的数据还只是简单的 URL 列表组装，这些 URL其实是包含了全量的数据。&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>以下是服务自省的一个完整工作流程图，详细描述了服务注册、服务发现、MetadataService、RPC 调用间的协作流程。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-9.png" alt="img9">&lt;/p>
&lt;ul>
&lt;li>服务提供者启动，首先解析应用定义的“普通服务”并依次注册为 RPC 服务，紧接着注册内建的 MetadataService 服务，最后打开 TCP 监听端口。&lt;/li>
&lt;li>启动完成后，将实例信息注册到注册中心（仅限 ip、port 等实例相关数据），提供者启动完成。&lt;/li>
&lt;li>服务消费者启动，首先依据其要“消费的 provider 应用名”到注册中心查询地址列表，并完成订阅（以实现后续地址变更自动通知）。&lt;/li>
&lt;li>消费端拿到地址列表后，紧接着对 MetadataService 发起调用，返回结果中包含了所有应用定义的“普通服务”及其相关配置信息。&lt;/li>
&lt;li>至此，消费者可以接收外部流量，并对提供者发起 Dubbo RPC 调用&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>在以上流程中，我们只考虑了一切顺利的情况，但在更详细的设计或编码实现中，我们还需要严格约定一些异常场景下的框架行为。比如，如果消费者 MetadataService 调用失败，则在重试直到成功之前，消费者将不可以接收外部流量。&lt;/p>
&lt;/blockquote>
&lt;h3 id="43-服务自省中的关键机制">4.3 服务自省中的关键机制&lt;/h3>
&lt;h4 id="431-元数据同步机制">4.3.1 元数据同步机制&lt;/h4>
&lt;p>Client 与 Server 间在收到地址推送后的配置同步是服务自省的关键环节，目前针对元数据同步有两种具体的可选方案，分别是：&lt;/p>
&lt;ul>
&lt;li>内建 MetadataService。&lt;/li>
&lt;li>独立的元数据中心，通过中心化的元数据集群协调数据。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>1. 内建 MetadataService&lt;/strong>
MetadataService 通过标准的 Dubbo 协议暴露，根据查询条件，会将内存中符合条件的“普通服务”配置返回给消费者。这一步发生在消费端选址和调用前。&lt;/p>
&lt;p>&lt;strong>2. 元数据中心&lt;/strong>
复用 2.7 版本中引入的元数据中心，provider 实例启动后，会尝试将内部的 RPC 服务组织成元数据的格式同步到元数据中心，而 consumer 则在每次收到注册中心推送更新后，主动查询元数据中心。&lt;/p>
&lt;blockquote>
&lt;p>注意 consumer 端查询元数据中心的时机，是等到注册中心的地址更新通知之后。也就是通过注册中心下发的数据，我们能明确的知道何时某个实例的元数据被更新了，此时才需要去查元数据中心。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-10.png" alt="img10">&lt;/p>
&lt;h4 id="432-rpc-服务-----应用映射关系">4.3.2 RPC 服务 &amp;lt; - &amp;gt; 应用映射关系&lt;/h4>
&lt;p>回顾上文讲到的注册中心关于“应用 - 实例列表”结构的数据组织形式，这个变动目前对开发者并不是完全透明的，业务开发侧会感知到查询/订阅地址列表的机制的变化。具体来说，相比以往我们基于 RPC 服务来检索地址，现在 consumer 需要通过指定 provider 应用名才能实现地址查询或订阅。&lt;/p>
&lt;p>老的 Consumer 开发与配置示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 框架直接通过 RPC Service 1/2/N 去注册中心查询或订阅地址列表 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;RPC Service 1&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;RPC Service 2&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;RPC Service N&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>新的 Consumer 开发与配置示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 框架需要通过额外的 provided-by=&amp;#34;provider-app-x&amp;#34; 才能在注册中心查询或订阅到地址列表 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181?registry-type=service&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;RPC Service 1&amp;#34;&lt;/span> provided-by=&lt;span style="color:#2aa198">&amp;#34;provider-app-x&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;RPC Service 2&amp;#34;&lt;/span> provided-by=&lt;span style="color:#2aa198">&amp;#34;provider-app-x&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;RPC Service N&amp;#34;&lt;/span> provided-by=&lt;span style="color:#2aa198">&amp;#34;provider-app-y&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>以上指定 provider 应用名的方式是 Spring Cloud 当前的做法，需要 consumer 端的开发者显示指定其要消费的 provider 应用。&lt;/p>
&lt;p>以上问题的根源在于注册中心不知道任何 RPC 服务相关的信息，因此只能通过应用名来查询。&lt;/p>
&lt;p>为了使整个开发流程对老的 Dubbo 用户更透明，同时避免指定 provider 对可扩展性带来的影响（参见下方说明），我们设计了一套 &lt;code>RPC 服务到应用名&lt;/code>的映射关系，以尝试在 consumer 端自动完成 RPC 服务到 provider 应用名的转换。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/service-discovery-11.png" alt="img11">&lt;/p>
&lt;blockquote>
&lt;p>Dubbo 之所以选择建立一套“接口-应用”的映射关系，主要是考虑到 service - app 映射关系的不确定性。一个典型的场景即是应用/服务拆分，如上面提到的配置&lt;code>&amp;lt;dubbo:reference interface=&amp;quot;RPC Service 2&amp;quot; provided-by=&amp;quot;provider-app-x&amp;quot; /&amp;gt;&lt;/code>，PC Service 2 是定义于 provider-app-x 中的一个服务，未来它随时可能会被开发者分拆到另外一个新的应用如 provider-app-x-1 中，这个拆分要被所有的 PC Service 2 消费方感知到，并对应用进行修改升级，如改为&lt;code>&amp;lt;dubbo:reference interface=&amp;quot;RPC Service 2&amp;quot; provided-by=&amp;quot;provider-app-x-1&amp;quot; /&amp;gt;&lt;/code>，这样的升级成本不可否认还是挺高的。
到底是 Dubbo 框架帮助开发者透明的解决这个问题，还是交由开发者自己去解决，当然这只是个策略选择问题，并且 Dubbo 2.7.5+ 版本目前是都提供了的。其实我个人更倾向于交由业务开发者通过组织上的约束来做，这样也可进一步降低 Dubbo 框架的复杂度，提升运行态的稳定性。&lt;/p>
&lt;/blockquote>
&lt;h2 id="5-总结与展望">5 总结与展望&lt;/h2>
&lt;p>应用级服务发现机制是 Dubbo 面向云原生走出的重要一步，它帮 Dubbo 打通了与其他微服务体系之间在地址发现层面的鸿沟，也成为 Dubbo 适配 Kubernetes Native Service 等基础设施的基础。我们期望 Dubbo 在新模型基础上，能继续保留在编程易用性、服务治理能力等方面强大的优势。但是我们也应该看到应用粒度的模型一方面带来了新的复杂性，需要我们继续去优化与增强；另一方面，除了地址存储与推送之外，应用粒度在帮助 Dubbo 选址层面也有进一步挖掘的潜力。&lt;/p></description></item><item><title>Blog: dubbo-go 白话文</title><link>https://cn.dubbo.apache.org/zh/blog/2021/02/20/dubbo-go-%E7%99%BD%E8%AF%9D%E6%96%87/</link><pubDate>Sat, 20 Feb 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/02/20/dubbo-go-%E7%99%BD%E8%AF%9D%E6%96%87/</guid><description>
&lt;h2 id="一前言">一、前言&lt;/h2>
&lt;blockquote>
&lt;p>本文基于 dubbogo &lt;a href="https://github.com/apache/dubbo-go/releases/tag/v1.5.4">1.5.4&lt;/a> 版本&lt;/p>
&lt;/blockquote>
&lt;p>最近开始参与 dubbogo 的一些开发测试，之前都是直接拿 &lt;a href="https://github.com/apache/dubbo-go-samples">samples&lt;/a> 的例子验证功能，而这次为了复现一个功能问题，打算从零开始搭建一个 dubbo-go 和 dubbo 调用的工程，踩到了一些新人使用 dubbogo 的坑，把这个过程记录下供大家参考。&lt;/p>
&lt;p>通过本文你可以了解到：&lt;/p>
&lt;ul>
&lt;li>如何常规配置 dubbogo 消费方去调用 dubbo 和 dubbogo 服务提供方&lt;/li>
&lt;li>通过一个实际的 BUG 介绍解决问题的思路&lt;/li>
&lt;/ul>
&lt;h2 id="二解决问题">二、解决问题&lt;/h2>
&lt;h3 id="21-准备-dubbo-服务提供者">2.1 准备 dubbo 服务提供者&lt;/h3>
&lt;h4 id="211-基本定义">2.1.1 基本定义&lt;/h4>
&lt;p>定义 &lt;code>DemoService&lt;/code> 接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>User user&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>User user&lt;span style="color:#719e07">,&lt;/span> String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>定义 &lt;code>User&lt;/code> 对象：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">User&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Serializable &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String name&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> age&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">......&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="212-启动-dubbo-服务提供者">2.1.2 启动 dubbo 服务提供者&lt;/h4>
&lt;p>用的 &lt;a href="https://cn.dubbo.apache.org/zh/docsv2.7/user/configuration/api/">dubbo 官方示例代码&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 服务实现
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> DemoService demoService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DemoServiceImpl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 当前应用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationConfig application &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> application&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;demoProvider&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 连接注册中心配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RegistryConfig registry &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry&lt;span style="color:#719e07">.&lt;/span>setUsername&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry&lt;span style="color:#719e07">.&lt;/span>setPassword&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 服务提供者协议配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ProtocolConfig protocol &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">12345&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol&lt;span style="color:#719e07">.&lt;/span>setThreads&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">200&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注意：ServiceConfig为重对象，内部封装了与注册中心的连接，以及开启服务端口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 服务提供者暴露服务配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span> &lt;span style="color:#586e75">// 此实例很重，封装了与注册中心的连接，请自行缓存，否则可能造成内存和连接泄漏
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>application&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setRegistry&lt;span style="color:#719e07">(&lt;/span>registry&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// 多个注册中心可以用setRegistries()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>protocol&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// 多个协议可以用setProtocols()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>demoService&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setVersion&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setGroup&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service&lt;span style="color:#719e07">.&lt;/span>setTimeout&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">60&lt;/span> &lt;span style="color:#719e07">*&lt;/span> &lt;span style="color:#2aa198">1000&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 暴露及注册服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>查看 zookeeper 看是否注册成功：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">$ls&lt;/span> /dubbo/com.funnycode.DemoService/providers
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>dubbo%3A%2F%2F127.0.0.1%3A12345%2Fcom.funnycode.DemoService%3Fanyhost%3Dtrue%26application%3DdemoProvider%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Dtc%26interface%3Dcom.funnycode.DemoService%26methods%3DsayHello%26pid%3D18167%26release%3D2.7.7%26revision%3D1.0.0%26side%3Dprovider%26threads%3D200%26timestamp%3D1606896020691%26version%3D1.0.0&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如上的输出表示服务提供方已经启动。&lt;/p>
&lt;h3 id="22-准备-dubbogo-服务消费者">2.2 准备 dubbogo 服务消费者&lt;/h3>
&lt;h4 id="221-基本定义">2.2.1 基本定义&lt;/h4>
&lt;p>定义 &lt;code>User&lt;/code> 对象：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> User &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Age &lt;span style="color:#dc322f">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (User) &lt;span style="color:#268bd2">JavaClassName&lt;/span>() &lt;span style="color:#dc322f">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.funnycode.User&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>定义 &lt;code>DemoProvider&lt;/code> 接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> DemoProvider &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SayHello &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, name &lt;span style="color:#dc322f">string&lt;/span>) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) &lt;span style="color:#2aa198">`dubbo:&amp;#34;sayHello&amp;#34;`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SayHello2 &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, user User) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) &lt;span style="color:#2aa198">`dubbo:&amp;#34;sayHello&amp;#34;`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SayHello3 &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, user User, name &lt;span style="color:#dc322f">string&lt;/span>) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) &lt;span style="color:#2aa198">`dubbo:&amp;#34;sayHello&amp;#34;`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>DemoProvider) &lt;span style="color:#268bd2">Reference&lt;/span>() &lt;span style="color:#dc322f">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;DemoProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="222-启动-dubbogo-消费者">2.2.2 启动 dubbogo 消费者&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> gxlog.&lt;span style="color:#268bd2">CInfo&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;\n\n\nstart to test dubbo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res, err &lt;span style="color:#719e07">:=&lt;/span> demoProvider.&lt;span style="color:#268bd2">SayHello&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), &lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> gxlog.&lt;span style="color:#268bd2">CInfo&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;response result: %v\n&amp;#34;&lt;/span>, res)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> user &lt;span style="color:#719e07">:=&lt;/span> User{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name: &lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Age: &lt;span style="color:#2aa198">18&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res, err = demoProvider.&lt;span style="color:#268bd2">SayHello2&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> gxlog.&lt;span style="color:#268bd2">CInfo&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;response result: %v\n&amp;#34;&lt;/span>, res)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res, err = demoProvider.&lt;span style="color:#268bd2">SayHello3&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), user, &lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> gxlog.&lt;span style="color:#268bd2">CInfo&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;response result: %v\n&amp;#34;&lt;/span>, res)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">initSignal&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="23-请求结果分析">2.3 请求结果分析&lt;/h3>
&lt;h4 id="231-直接调用">2.3.1 直接调用&lt;/h4>
&lt;blockquote>
&lt;p>确认问题的存在&lt;/p>
&lt;/blockquote>
&lt;p>第一个接口的参数是字符串，可以正常返回 &lt;code>[2020-12-03/18:59:12 main.main: client.go: 29] response result: Hello tc&lt;/code>
第二、三两个接口存在 &lt;code>User&lt;/code> 对象，无法调用成功。错误信息如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>2020-12-02T17:10:47.739+0800 INFO getty/listener.go:87 session&lt;span style="color:#719e07">{&lt;/span>session session-closed, Read Bytes: 924, Write Bytes: 199, Read Pkgs: 0, Write Pkgs: 1&lt;span style="color:#719e07">}&lt;/span> got error&lt;span style="color:#719e07">{&lt;/span>java exception:Fail to decode request due to: java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, sayHello
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode&lt;span style="color:#719e07">(&lt;/span>DecodeableRpcInvocation.java:134&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode&lt;span style="color:#719e07">(&lt;/span>DecodeableRpcInvocation.java:80&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.transport.DecodeHandler.decode&lt;span style="color:#719e07">(&lt;/span>DecodeHandler.java:57&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.transport.DecodeHandler.received&lt;span style="color:#719e07">(&lt;/span>DecodeHandler.java:44&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run&lt;span style="color:#719e07">(&lt;/span>ChannelEventRunnable.java:57&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at java.util.concurrent.ThreadPoolExecutor.runWorker&lt;span style="color:#719e07">(&lt;/span>ThreadPoolExecutor.java:1149&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at java.util.concurrent.ThreadPoolExecutor&lt;span style="color:#268bd2">$Worker&lt;/span>.run&lt;span style="color:#719e07">(&lt;/span>ThreadPoolExecutor.java:624&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at java.lang.Thread.run&lt;span style="color:#719e07">(&lt;/span>Thread.java:748&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>, will be closed.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>错误正如 &lt;a href="https://github.com/apache/dubbo-go/issues/900">issue&lt;/a> 中描述的一模一样，因为错误信息返回到了消费端，可以看到 Java 那边的错误堆栈信息，所以直接去看 &lt;code>DecodeableRpcInvocation.decode#134&lt;/code>。&lt;/p>
&lt;h4 id="232-断点查看">2.3.2 断点查看&lt;/h4>
&lt;p>代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 反序列化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DecodeableRpcInvocation&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> RpcInvocation &lt;span style="color:#268bd2">implements&lt;/span> Codec&lt;span style="color:#719e07">,&lt;/span> Decodeable &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Object &lt;span style="color:#268bd2">decode&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Channel channel&lt;span style="color:#719e07">,&lt;/span> InputStream input&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">......&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serviceDescriptor &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 方法描述里面根据方法名查找
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MethodDescriptor methodDescriptor &lt;span style="color:#719e07">=&lt;/span> serviceDescriptor&lt;span style="color:#719e07">.&lt;/span>getMethod&lt;span style="color:#719e07">(&lt;/span>getMethodName&lt;span style="color:#719e07">(),&lt;/span> desc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>methodDescriptor &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pts &lt;span style="color:#719e07">=&lt;/span> methodDescriptor&lt;span style="color:#719e07">.&lt;/span>getParameterClasses&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>setReturnTypes&lt;span style="color:#719e07">(&lt;/span>methodDescriptor&lt;span style="color:#719e07">.&lt;/span>getReturnTypes&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 表示没有找到方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>pts &lt;span style="color:#719e07">==&lt;/span> DubboCodec&lt;span style="color:#719e07">.&lt;/span>EMPTY_CLASS_ARRAY&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>RpcUtils&lt;span style="color:#719e07">.&lt;/span>isGenericCall&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">,&lt;/span> getMethodName&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>RpcUtils&lt;span style="color:#719e07">.&lt;/span>isEcho&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">,&lt;/span> getMethodName&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Service not found:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> path &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> getMethodName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pts &lt;span style="color:#719e07">=&lt;/span> ReflectUtils&lt;span style="color:#719e07">.&lt;/span>desc2classArray&lt;span style="color:#719e07">(&lt;/span>desc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">......&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>查看 &lt;code>MethodDescriptor&lt;/code>，即找方法是否存在，存在的话就会设置好 &lt;code>ParameterClasses&lt;/code>&lt;/li>
&lt;li>如果上面没找到，&lt;code>pts == DubboCodec.EMPTY_CLASS_ARRAY&lt;/code> 就会满足条件，进而判断是否是泛化调用或者是 echo 调用，如果都不是则报服务找不到方法错误&lt;/li>
&lt;li>desc 是 &lt;code>Ljava/lang/Object&lt;/code> ，很明显并没有参数是 Object 的方法，所以必然是会报错的&lt;/li>
&lt;/ul>
&lt;p>补充说明：&lt;/p>
&lt;p>&lt;strong>方法查询&lt;/strong>&lt;/p>
&lt;p>代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> MethodDescriptor &lt;span style="color:#268bd2">getMethod&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String methodName&lt;span style="color:#719e07">,&lt;/span> String params&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> MethodDescriptor&lt;span style="color:#719e07">&amp;gt;&lt;/span> methods &lt;span style="color:#719e07">=&lt;/span> descToMethods&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>methodName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmptyMap&lt;span style="color:#719e07">(&lt;/span>methods&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> methods&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>params&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>优点：&lt;/p>
&lt;p>比之前的版本加了方法的元信息缓存起来，不使用反射可以提高效率，可以理解用空间换时间。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/from-scratch/dfsa01.jpg" alt="dfsa01.jpg">&lt;/p>
&lt;h3 id="24-解决问题">2.4 解决问题&lt;/h3>
&lt;blockquote>
&lt;p>因为直接撸代码并 hold 不住，所以通过比较来查看问题所在。&lt;/p>
&lt;/blockquote>
&lt;h4 id="241-启动-dubbo-服务消费者">2.4.1 启动 dubbo 服务消费者&lt;/h4>
&lt;p>通过 api 模式启动，参考官方例子。启动这个是为了查看 Java 版本的传输内容。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> InterruptedException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 当前应用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationConfig application &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> application&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;demoProvider2&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 连接注册中心配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RegistryConfig registry &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry&lt;span style="color:#719e07">.&lt;/span>setUsername&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry&lt;span style="color:#719e07">.&lt;/span>setPassword&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注意：ReferenceConfig为重对象，内部封装了与注册中心的连接，以及与服务提供方的连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 引用远程服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span> &lt;span style="color:#586e75">// 此实例很重，封装了与注册中心的连接以及与提供者的连接，请自行缓存，否则可能造成内存和连接泄漏
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> reference&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>application&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setRegistry&lt;span style="color:#719e07">(&lt;/span>registry&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// 多个注册中心可以用setRegistries()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> reference&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setVersion&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setGroup&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setCheck&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setTimeout&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1000&lt;/span> &lt;span style="color:#719e07">*&lt;/span> &lt;span style="color:#2aa198">60&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 和本地bean一样使用xxxService
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> DemoService demoService &lt;span style="color:#719e07">=&lt;/span> reference&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// 注意：此代理对象内部封装了所有通讯细节，对象较重，请缓存复用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>demoService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> User&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">18&lt;/span>&lt;span style="color:#719e07">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MINUTES&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">10&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/from-scratch/dfsa02.png" alt="dfsa02.png">&lt;/p>
&lt;p>desc 肉眼可见的是 &lt;code>Lcom/funnycode/User&lt;/code>，这个就是正确的对象了。&lt;/p>
&lt;h4 id="242-查找-dubbogo-为什么不对">2.4.2 查找 dubbogo 为什么不对&lt;/h4>
&lt;p>代码位置：&lt;/p>
&lt;p>&lt;code>protocol/dubbo/impl/hessian.go:120#marshalRequest&lt;/code>&lt;/p>
&lt;p>代码实现：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">marshalRequest&lt;/span>(encoder &lt;span style="color:#719e07">*&lt;/span>hessian.Encoder, p DubboPackage) ([]&lt;span style="color:#dc322f">byte&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service &lt;span style="color:#719e07">:=&lt;/span> p.Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> request &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">EnsureRequestPayload&lt;/span>(p.Body)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> encoder.&lt;span style="color:#268bd2">Encode&lt;/span>(DEFAULT_DUBBO_PROTOCOL_VERSION)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> encoder.&lt;span style="color:#268bd2">Encode&lt;/span>(service.Path)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> encoder.&lt;span style="color:#268bd2">Encode&lt;/span>(service.Version)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> encoder.&lt;span style="color:#268bd2">Encode&lt;/span>(service.Method)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> args, ok &lt;span style="color:#719e07">:=&lt;/span> request.Params.([]&lt;span style="color:#268bd2">interface&lt;/span>{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> !ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;request args are: %+v&amp;#34;&lt;/span>, request.Params)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>, perrors.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;@params is not of type: []interface{}&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> types, err &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">getArgsTypeList&lt;/span>(args)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>, perrors.&lt;span style="color:#268bd2">Wrapf&lt;/span>(err, &lt;span style="color:#2aa198">&amp;#34; PackRequest(args:%+v)&amp;#34;&lt;/span>, args)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> encoder.&lt;span style="color:#268bd2">Encode&lt;/span>(types)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> _, v &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">range&lt;/span> args {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> encoder.&lt;span style="color:#268bd2">Encode&lt;/span>(v)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">......&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>断点可以发现，types 返回的时候就已经是 &lt;code>Object&lt;/code> 了，没有返回 &lt;code>User&lt;/code>，那么继续跟进去查看代码。&lt;/p>
&lt;ul>
&lt;li>&lt;code>protocol/dubbo/impl/hessian.go:394#getArgsTypeList&lt;/code>&lt;/li>
&lt;li>&lt;code>protocol/dubbo/impl/hessian.go:418#getArgType&lt;/code>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">getArgType&lt;/span>(v &lt;span style="color:#268bd2">interface&lt;/span>{}) &lt;span style="color:#dc322f">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 常见的类型处理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">......&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t &lt;span style="color:#719e07">:=&lt;/span> reflect.&lt;span style="color:#268bd2">TypeOf&lt;/span>(v)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> reflect.Ptr &lt;span style="color:#719e07">==&lt;/span> t.&lt;span style="color:#268bd2">Kind&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t = reflect.&lt;span style="color:#268bd2">TypeOf&lt;/span>(reflect.&lt;span style="color:#268bd2">ValueOf&lt;/span>(v).&lt;span style="color:#268bd2">Elem&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">switch&lt;/span> t.&lt;span style="color:#268bd2">Kind&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> reflect.Struct:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;java.lang.Object&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">......&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>很明显当发现是 &lt;code>reflect.Struct&lt;/code> 的时候就返回了 &lt;code>java.lang.Object&lt;/code>，所以参数就变成了 &lt;code>Object&lt;/code>，那么因为 Java 代码那边依赖这个类型所以就调用失败了。&lt;/p>
&lt;h4 id="243-其它版本验证">2.4.3 其它版本验证&lt;/h4>
&lt;p>因为反馈是 2.7.7 出错，所以先考虑到在之前的版本是否功能正常，于是把服务提供者切换到 dubbo 2.7.3，发现调用仍然有错误，如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>2020-12-02T21:52:25.945+0800 INFO getty/listener.go:85 session&lt;span style="color:#719e07">{&lt;/span>session session-closed, Read Bytes: 4586, Write Bytes: 232, Read Pkgs: 0, Write Pkgs: 1&lt;span style="color:#719e07">}&lt;/span> got error&lt;span style="color:#719e07">{&lt;/span>java exception:org.apache.dubbo.rpc.RpcException: Failed to invoke remote proxy method sayHello to registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application&lt;span style="color:#719e07">=&lt;/span>demoProvider&amp;amp;&lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.0.2&amp;amp;&lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbo%3A%2F%2F192.168.0.113%3A12345%2Fcom.funnycode.DemoService%3Fanyhost%3Dtrue%26application%3DdemoProvider%26bind.ip%3D192.168.0.113%26bind.port%3D12345%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Dtc%26interface%3Dcom.funnycode.DemoService%26methods%3DsayHello%26pid%3D23889%26register%3Dtrue%26release%3D2.7.3%26revision%3D1.0.0%26side%3Dprovider%26threads%3D200%26timeout%3D60000%26timestamp%3D1606916702204%26version%3D1.0.0&amp;amp;&lt;span style="color:#268bd2">pid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>23889&amp;amp;&lt;span style="color:#268bd2">registry&lt;/span>&lt;span style="color:#719e07">=&lt;/span>zookeeper&amp;amp;&lt;span style="color:#268bd2">release&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.7.3&amp;amp;&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1606916702193, cause: Not found method &lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span> in class com.funnycode.DemoServiceImpl.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>org.apache.dubbo.rpc.RpcException: Failed to invoke remote proxy method sayHello to registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application&lt;span style="color:#719e07">=&lt;/span>demoProvider&amp;amp;&lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.0.2&amp;amp;&lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbo%3A%2F%2F192.168.0.113%3A12345%2Fcom.funnycode.DemoService%3Fanyhost%3Dtrue%26application%3DdemoProvider%26bind.ip%3D192.168.0.113%26bind.port%3D12345%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Dtc%26interface%3Dcom.funnycode.DemoService%26methods%3DsayHello%26pid%3D23889%26register%3Dtrue%26release%3D2.7.3%26revision%3D1.0.0%26side%3Dprovider%26threads%3D200%26timeout%3D60000%26timestamp%3D1606916702204%26version%3D1.0.0&amp;amp;&lt;span style="color:#268bd2">pid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>23889&amp;amp;&lt;span style="color:#268bd2">registry&lt;/span>&lt;span style="color:#719e07">=&lt;/span>zookeeper&amp;amp;&lt;span style="color:#268bd2">release&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.7.3&amp;amp;&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1606916702193, cause: Not found method &lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span> in class com.funnycode.DemoServiceImpl.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke&lt;span style="color:#719e07">(&lt;/span>AbstractProxyInvoker.java:107&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke&lt;span style="color:#719e07">(&lt;/span>DelegateProviderMetaDataInvoker.java:56&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke&lt;span style="color:#719e07">(&lt;/span>InvokerWrapper.java:56&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.filter.ExceptionFilter.invoke&lt;span style="color:#719e07">(&lt;/span>ExceptionFilter.java:55&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:82&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.monitor.support.MonitorFilter.invoke&lt;span style="color:#719e07">(&lt;/span>MonitorFilter.java:92&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:82&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.filter.TimeoutFilter.invoke&lt;span style="color:#719e07">(&lt;/span>TimeoutFilter.java:48&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:82&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke&lt;span style="color:#719e07">(&lt;/span>TraceFilter.java:81&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:82&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.filter.ContextFilter.invoke&lt;span style="color:#719e07">(&lt;/span>ContextFilter.java:96&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:82&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.filter.GenericFilter.invoke&lt;span style="color:#719e07">(&lt;/span>GenericFilter.java:148&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:82&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.filter.ClassLoaderFilter.invoke&lt;span style="color:#719e07">(&lt;/span>ClassLoaderFilter.java:38&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:82&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.filter.EchoFilter.invoke&lt;span style="color:#719e07">(&lt;/span>EchoFilter.java:41&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:82&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$CallbackRegistrationInvoker&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:157&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol&lt;span style="color:#268bd2">$1&lt;/span>.reply&lt;span style="color:#719e07">(&lt;/span>DubboProtocol.java:152&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest&lt;span style="color:#719e07">(&lt;/span>HeaderExchangeHandler.java:102&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received&lt;span style="color:#719e07">(&lt;/span>HeaderExchangeHandler.java:193&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.transport.DecodeHandler.received&lt;span style="color:#719e07">(&lt;/span>DecodeHandler.java:51&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run&lt;span style="color:#719e07">(&lt;/span>ChannelEventRunnable.java:57&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at java.util.concurrent.ThreadPoolExecutor.runWorker&lt;span style="color:#719e07">(&lt;/span>ThreadPoolExecutor.java:1149&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at java.util.concurrent.ThreadPoolExecutor&lt;span style="color:#268bd2">$Worker&lt;/span>.run&lt;span style="color:#719e07">(&lt;/span>ThreadPoolExecutor.java:624&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at java.lang.Thread.run&lt;span style="color:#719e07">(&lt;/span>Thread.java:748&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Caused by: org.apache.dubbo.common.bytecode.NoSuchMethodException: Not found method &lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span> in class com.funnycode.DemoServiceImpl.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.common.bytecode.Wrapper1.invokeMethod&lt;span style="color:#719e07">(&lt;/span>Wrapper1.java&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory&lt;span style="color:#268bd2">$1&lt;/span>.doInvoke&lt;span style="color:#719e07">(&lt;/span>JavassistProxyFactory.java:47&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke&lt;span style="color:#719e07">(&lt;/span>AbstractProxyInvoker.java:84&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ... &lt;span style="color:#2aa198">27&lt;/span> more
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>, will be closed.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>虽然和 2.7.7 的代码是不一样的，但是通过错误也能看出来是在代理增强类里面方法找不到，大概率是反射找不到方法，所以归根结底也是参数的问题。&lt;/p>
&lt;h4 id="244-修复问题">2.4.4 修复问题&lt;/h4>
&lt;p>修复相对简单，就是拿到 &lt;code>struct&lt;/code> 定义的 &lt;code>JavaClassName&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">case&lt;/span> reflect.Struct:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> v, ok &lt;span style="color:#719e07">:=&lt;/span> v.(hessian.POJO)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> v.&lt;span style="color:#268bd2">JavaClassName&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;java.lang.Object&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="243-验证结果">2.4.3 验证结果&lt;/h4>
&lt;p>再次执行消费者，运行（提供方 2.7.7 和 2.7.3）正常，输出如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>2020-12-03/20:04:06 main.main: client.go: 29&lt;span style="color:#719e07">]&lt;/span> response result: Hello tc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>2020-12-03/20:04:09 main.main: client.go: 41&lt;span style="color:#719e07">]&lt;/span> response result: Hello tc You are &lt;span style="color:#2aa198">18&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>2020-12-03/20:04:09 main.main: client.go: 48&lt;span style="color:#719e07">]&lt;/span> response result: Hello tc You are &lt;span style="color:#2aa198">18&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="三细节叨叨">三、细节叨叨&lt;/h2>
&lt;h3 id="31-如何配置-dubbgo-消费者">3.1 如何配置 dubbgo 消费者&lt;/h3>
&lt;p>细心的你是否已经发现，在我 dubbogo 的消费端接口叫 &lt;code>DemoProvider&lt;/code>，然后发现提供者叫 &lt;code>DemoService&lt;/code>，这个又是如何正常运行的？&lt;/p>
&lt;p>实际上和 &lt;code>client.yml&lt;/code> 中配置项 &lt;code>references&lt;/code> 有关，在配置文件详细说明了 &lt;code>interface&lt;/code>，&lt;code>version&lt;/code>，&lt;code>group&lt;/code> 等，你还可以通过 methods 配置方法的超时时间等信息。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">references&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;DemoProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 可以指定多个registry，使用逗号隔开;不指定默认向所有注册中心注册&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;zk1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.funnycode.DemoService&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">version&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">group&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;SayHello&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">3&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ......
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="32-全局的-group-和-version-怎么配置">3.2 全局的 group 和 version 怎么配置&lt;/h3>
&lt;p>配置文件如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># application config&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">application&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">organization&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbogoproxy.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;Demo Micro Service&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">module&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbogoproxy tc client&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">version&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">group&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">owner&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;ZX&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">environment&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dev&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">references&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;DemoProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 可以指定多个registry，使用逗号隔开;不指定默认向所有注册中心注册&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;zk1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.funnycode.DemoService&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># version: &amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># group: &amp;#34;tc&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;SayHello&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>从使用的习惯来讲，肯定是 &lt;code>application&lt;/code> 表示了全局的配置，但是我发现启动的时候在 &lt;code>application&lt;/code> 配置的 &lt;code>version&lt;/code> 和 &lt;code>group&lt;/code> 并不会赋值给接口，启动会报服务提供方找不到，如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>2020-12-03T20:15:42.208+0800 DEBUG zookeeper/registry.go:237 Create a zookeeper node:/dubbo/com.funnycode.DemoService/consumers/consumer%3A%2F%2F30.11.176.107%2FDemoProvider%3Fapp.version%3D1.0.0%26application%3DDemo+Micro+Service%26async%3Dfalse%26bean.name%3DDemoProvider%26cluster%3Dfailover%26environment%3Ddev%26generic%3Dfalse%26group%3D%26interface%3Dcom.funnycode.DemoService%26ip%3D30.11.176.107%26loadbalance%3D%26methods.SayHello.loadbalance%3D%26methods.SayHello.retries%3D3%26methods.SayHello.sticky%3Dfalse%26module%3Ddubbogoproxy+tc+client%26name%3DDemo+Micro+Service%26organization%3Ddubbogoproxy.com%26owner%3DZX%26pid%3D38692%26protocol%3Ddubbo%26provided-by%3D%26reference.filter%3Dcshutdown%26registry.role%3D0%26release%3Ddubbo-golang-1.3.0%26retries%3D%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1606997742%26version%3D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>version&lt;/code> 和 &lt;code>group&lt;/code> 都是空。必须把 &lt;code>DemoProvider&lt;/code> 下的 &lt;code>version&lt;/code> 和 &lt;code>group&lt;/code> 注释打开。&lt;/p>
&lt;h3 id="33-怎么指定调用的方法名">3.3 怎么指定调用的方法名&lt;/h3>
&lt;h4 id="331-go-调用-java">3.3.1 go 调用 java&lt;/h4>
&lt;p>dubbogo 调用 dubbo，因为 go 是大写的方法名，java 里面是小写的方法名，所以会出现如下错误：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>2020-12-02T17:10:47.739+0800 INFO getty/listener.go:87 session&lt;span style="color:#719e07">{&lt;/span>session session-closed, Read Bytes: 924, Write Bytes: 199, Read Pkgs: 0, Write Pkgs: 1&lt;span style="color:#719e07">}&lt;/span> got error&lt;span style="color:#719e07">{&lt;/span>java exception:Fail to decode request due to: java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, SayHello
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>java.lang.IllegalArgumentException: Service not found:com.funnycode.DemoService, SayHello
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode&lt;span style="color:#719e07">(&lt;/span>DecodeableRpcInvocation.java:134&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode&lt;span style="color:#719e07">(&lt;/span>DecodeableRpcInvocation.java:80&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.transport.DecodeHandler.decode&lt;span style="color:#719e07">(&lt;/span>DecodeHandler.java:57&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.transport.DecodeHandler.received&lt;span style="color:#719e07">(&lt;/span>DecodeHandler.java:44&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run&lt;span style="color:#719e07">(&lt;/span>ChannelEventRunnable.java:57&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at java.util.concurrent.ThreadPoolExecutor.runWorker&lt;span style="color:#719e07">(&lt;/span>ThreadPoolExecutor.java:1149&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at java.util.concurrent.ThreadPoolExecutor&lt;span style="color:#268bd2">$Worker&lt;/span>.run&lt;span style="color:#719e07">(&lt;/span>ThreadPoolExecutor.java:624&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at java.lang.Thread.run&lt;span style="color:#719e07">(&lt;/span>Thread.java:748&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>, will be closed.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>细心的读者可能已经注意到了，我在消费端的接口声明是有个 &lt;code>dubbo:&amp;quot;sayHello&amp;quot;&lt;/code> 的，表示方法名是 sayHello，这样在服务提供方就可以得到 sayHello 这个方法名。&lt;/p>
&lt;p>还有我声明的三个方法都指明它们的方法名叫 &lt;code>dubbo:&amp;quot;sayHello&amp;quot;&lt;/code>，这是因为 Java 可以方法名字一样进行重载，而 go 是不能方法名重复的。&lt;/p>
&lt;h4 id="332-go-调用-go">3.3.2 go 调用 go&lt;/h4>
&lt;blockquote>
&lt;p>直接贴能跑通的代码&lt;/p>
&lt;/blockquote>
&lt;p>我的提供者接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> DemoProvider &lt;span style="color:#268bd2">struct&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>DemoProvider) &lt;span style="color:#268bd2">SayHello&lt;/span>(ctx context.Context, name &lt;span style="color:#dc322f">string&lt;/span>) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name, &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>DemoProvider) &lt;span style="color:#268bd2">SayHello4&lt;/span>(ctx context.Context, user &lt;span style="color:#719e07">*&lt;/span>User) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> user.Name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; You are &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> strconv.&lt;span style="color:#268bd2">Itoa&lt;/span>(user.Age), &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>DemoProvider) &lt;span style="color:#268bd2">SayHello5&lt;/span>(ctx context.Context, user &lt;span style="color:#719e07">*&lt;/span>User, name &lt;span style="color:#dc322f">string&lt;/span>) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; You are &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> strconv.&lt;span style="color:#268bd2">Itoa&lt;/span>(user.Age), &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>DemoProvider) &lt;span style="color:#268bd2">Reference&lt;/span>() &lt;span style="color:#dc322f">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;DemoProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>DemoProvider) &lt;span style="color:#268bd2">MethodMapper&lt;/span>() &lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]&lt;span style="color:#dc322f">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]&lt;span style="color:#dc322f">string&lt;/span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;SayHello&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我的消费者接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> DemoProvider &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 调用 java 和 go
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> SayHello &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, name &lt;span style="color:#dc322f">string&lt;/span>) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) &lt;span style="color:#2aa198">`dubbo:&amp;#34;sayHello&amp;#34;`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 只调用 java
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> SayHello2 &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, user &lt;span style="color:#719e07">*&lt;/span>User) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) &lt;span style="color:#2aa198">`dubbo:&amp;#34;sayHello&amp;#34;`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SayHello3 &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, user &lt;span style="color:#719e07">*&lt;/span>User, name &lt;span style="color:#dc322f">string&lt;/span>) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>) &lt;span style="color:#2aa198">`dubbo:&amp;#34;sayHello&amp;#34;`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 只调用 go
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> SayHello4 &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, user &lt;span style="color:#719e07">*&lt;/span>User) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SayHello5 &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, user &lt;span style="color:#719e07">*&lt;/span>User, name &lt;span style="color:#dc322f">string&lt;/span>) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启动服务消费者：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> gxlog.&lt;span style="color:#268bd2">CInfo&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;\n\n\nstart to test dubbo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res, err &lt;span style="color:#719e07">:=&lt;/span> demoProvider.&lt;span style="color:#268bd2">SayHello&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), &lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> gxlog.&lt;span style="color:#268bd2">CInfo&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;response result: %v\n&amp;#34;&lt;/span>, res)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> user &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>User{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name: &lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Age: &lt;span style="color:#2aa198">18&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res, err = demoProvider.&lt;span style="color:#268bd2">SayHello4&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> gxlog.&lt;span style="color:#268bd2">CInfo&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;response result: %v\n&amp;#34;&lt;/span>, res)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res, err = demoProvider.&lt;span style="color:#268bd2">SayHello5&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), user, &lt;span style="color:#2aa198">&amp;#34;tc&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> gxlog.&lt;span style="color:#268bd2">CInfo&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;response result: %v\n&amp;#34;&lt;/span>, res)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">initSignal&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里需要注意 &lt;code>MethodMapper&lt;/code> 方法，有时候需要在这个方法中配置方法名的映射关系，否则还是会出现找不到方法的错误。&lt;/p>
&lt;p>比如因为配置 &lt;code>dubbo:&amp;quot;sayHello&amp;quot;&lt;/code> ，所以在 go 里面请求 &lt;code>SayHello&lt;/code> 变成了 &lt;code>sayHello&lt;/code>，那么服务提供方通过 &lt;code>MethodMapper&lt;/code> 方法配置后使得提供方也是 &lt;code>sayHello&lt;/code>，这样 go 和 java 下暴露的都是小写的 &lt;code>sayHello&lt;/code>。&lt;/p>
&lt;h3 id="34-为什么会用-hessian2">3.4 为什么会用 hessian2&lt;/h3>
&lt;p>老司机都懂，在 dubbo 中 SPI 机制的默认值就是 hessian2&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SPI&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;hessian2&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">Serialization&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>而在 dubbo-go 中：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">NewDubboCodec&lt;/span>(reader &lt;span style="color:#719e07">*&lt;/span>bufio.Reader) &lt;span style="color:#719e07">*&lt;/span>ProtocolCodec {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s, _ &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">GetSerializerById&lt;/span>(constant.S_Hessian2)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>ProtocolCodec{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reader: reader,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pkgType: &lt;span style="color:#2aa198">0&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bodyLen: &lt;span style="color:#2aa198">0&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> headerRead: &lt;span style="color:#cb4b16">false&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serializer: s.(Serializer),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="35-hessian序列化源码">3.5 hessian序列化源码&lt;/h3>
&lt;blockquote>
&lt;p>可以自行断点查看，两边基本上一样，我也是通过两边比出来的，RpcInvocation.getParameterTypesDesc() 就是方法的参数&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>go 代码 &lt;code>protocol/dubbo/impl/hessian.go:120#marshalRequest&lt;/code>&lt;/li>
&lt;li>java 代码 &lt;code>org.apache.dubbo.rpc.protocol.dubbo.DubboCodec#encodeRequestData(org.apache.dubbo.remoting.Channel, org.apache.dubbo.common.serialize.ObjectOutput, java.lang.Object, java.lang.String)&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="36-dubbogo-服务提供者的方法对象需要是指针对象">3.6 dubbogo 服务提供者的方法对象需要是指针对象&lt;/h3>
&lt;p>之前的例子都是 copy 的，这次是纯手打的，才发现了这个问题。&lt;/p>
&lt;p>如果你的提供类似：&lt;code>func (p *DemoProvider) SayHello4(ctx context.Context, user User) (string, error)&lt;/code>，那么会出现如下错误：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>2020-12-03T12:42:32.834+0800 ERROR getty/listener.go:280 OnMessage panic: reflect: Call using *main.User as &lt;span style="color:#b58900">type&lt;/span> main.User
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>github.com/apache/dubbo-go/remoting/getty.&lt;span style="color:#719e07">(&lt;/span>*RpcServerHandler&lt;span style="color:#719e07">)&lt;/span>.OnMessage.func1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>参数里面的 &lt;code>User&lt;/code> 需要改成 &lt;code>*User&lt;/code>。&lt;/p>
&lt;h3 id="37-dubbogo-服务消费者的方法对象可以是非指针对象">3.7 dubbogo 服务消费者的方法对象可以是非指针对象&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>SayHello4 &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, user &lt;span style="color:#719e07">*&lt;/span>User) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// or
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>SayHello4 &lt;span style="color:#268bd2">func&lt;/span>(ctx context.Context, user User) (&lt;span style="color:#dc322f">string&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>因为在参数序列化的时候会对指针做操作：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>t &lt;span style="color:#719e07">:=&lt;/span> reflect.&lt;span style="color:#268bd2">TypeOf&lt;/span>(v)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">if&lt;/span> reflect.Ptr &lt;span style="color:#719e07">==&lt;/span> t.&lt;span style="color:#268bd2">Kind&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t = reflect.&lt;span style="color:#268bd2">TypeOf&lt;/span>(reflect.&lt;span style="color:#268bd2">ValueOf&lt;/span>(v).&lt;span style="color:#268bd2">Elem&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a href="https://github.com/apache/dubbo-go/blob/v1.5.4/protocol/dubbo/impl/hessian.go#L486">完整代码&lt;/a>&lt;/p>
&lt;h3 id="38-配置文件说明">3.8 配置文件说明&lt;/h3>
&lt;p>dubbogo 主要有三个配置文件：&lt;/p>
&lt;ul>
&lt;li>server.yaml 服务提供方的配置文件&lt;/li>
&lt;li>client.yaml 服务消费方的配置文件&lt;/li>
&lt;li>log.yaml 日志文件&lt;/li>
&lt;/ul>
&lt;p>如果你什么都不配置，会出现：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>2021/01/11 15:31:41 &lt;span style="color:#719e07">[&lt;/span>InitLog&lt;span style="color:#719e07">]&lt;/span> warn: log configure file name is nil
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2021/01/11 15:31:41 &lt;span style="color:#719e07">[&lt;/span>consumerInit&lt;span style="color:#719e07">]&lt;/span> application configure&lt;span style="color:#719e07">(&lt;/span>consumer&lt;span style="color:#719e07">)&lt;/span> file name is nil
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2021/01/11 15:31:41 &lt;span style="color:#719e07">[&lt;/span>providerInit&lt;span style="color:#719e07">]&lt;/span> application configure&lt;span style="color:#719e07">(&lt;/span>provider&lt;span style="color:#719e07">)&lt;/span> file name is nil
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样是没法正常使用的。如果你是服务提供方，必须要配置 server.yaml 文件，如果你是服务消费方，必须要配置 client.yaml，实际我们的应用应该既是消费者又是提供者，所以往往两个文件都是需要配置的。&lt;/p>
&lt;p>服务提供方正常启动是会有如下输出的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>2021-01-11T15:36:55.003+0800 INFO protocol/protocol.go:205 The cached exporter keys is dubbo://:20000/DemoProvider?accesslog&lt;span style="color:#719e07">=&lt;/span>&amp;amp;app.version&lt;span style="color:#719e07">=&lt;/span>1.0.0&amp;amp;&lt;span style="color:#268bd2">application&lt;/span>&lt;span style="color:#719e07">=&lt;/span>Demo+Micro+Service&amp;amp;&lt;span style="color:#268bd2">auth&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&amp;amp;bean.name&lt;span style="color:#719e07">=&lt;/span>DemoProvider&amp;amp;&lt;span style="color:#268bd2">cluster&lt;/span>&lt;span style="color:#719e07">=&lt;/span>failover&amp;amp;&lt;span style="color:#268bd2">environment&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dev&amp;amp;execute.limit&lt;span style="color:#719e07">=&lt;/span>&amp;amp;execute.limit.rejected.handler&lt;span style="color:#719e07">=&lt;/span>&amp;amp;&lt;span style="color:#268bd2">group&lt;/span>&lt;span style="color:#719e07">=&lt;/span>tc&amp;amp;&lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.funnycode.DemoService&amp;amp;&lt;span style="color:#268bd2">loadbalance&lt;/span>&lt;span style="color:#719e07">=&lt;/span>random&amp;amp;methods.SayHello.loadbalance&lt;span style="color:#719e07">=&lt;/span>random&amp;amp;methods.SayHello.retries&lt;span style="color:#719e07">=&lt;/span>3&amp;amp;methods.SayHello.tps.limit.interval&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello.tps.limit.rate&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello.tps.limit.strategy&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello.weight&lt;span style="color:#719e07">=&lt;/span>0&amp;amp;methods.SayHello4.loadbalance&lt;span style="color:#719e07">=&lt;/span>random&amp;amp;methods.SayHello4.retries&lt;span style="color:#719e07">=&lt;/span>3&amp;amp;methods.SayHello4.tps.limit.interval&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello4.tps.limit.rate&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello4.tps.limit.strategy&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello4.weight&lt;span style="color:#719e07">=&lt;/span>0&amp;amp;methods.SayHello5.loadbalance&lt;span style="color:#719e07">=&lt;/span>random&amp;amp;methods.SayHello5.retries&lt;span style="color:#719e07">=&lt;/span>3&amp;amp;methods.SayHello5.tps.limit.interval&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello5.tps.limit.rate&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello5.tps.limit.strategy&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello5.weight&lt;span style="color:#719e07">=&lt;/span>0&amp;amp;&lt;span style="color:#268bd2">module&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbogoproxy+tc+client&amp;amp;&lt;span style="color:#268bd2">name&lt;/span>&lt;span style="color:#719e07">=&lt;/span>Demo+Micro+Service&amp;amp;&lt;span style="color:#268bd2">organization&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbogoproxy.com&amp;amp;&lt;span style="color:#268bd2">owner&lt;/span>&lt;span style="color:#719e07">=&lt;/span>ZX&amp;amp;param.sign&lt;span style="color:#719e07">=&lt;/span>&amp;amp;registry.role&lt;span style="color:#719e07">=&lt;/span>3&amp;amp;&lt;span style="color:#268bd2">release&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbo-golang-1.3.0&amp;amp;&lt;span style="color:#268bd2">retries&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&amp;amp;&lt;span style="color:#268bd2">serialization&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&amp;amp;service.filter&lt;span style="color:#719e07">=&lt;/span>echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&amp;amp;&lt;span style="color:#268bd2">side&lt;/span>&lt;span style="color:#719e07">=&lt;/span>provider&amp;amp;ssl-enabled&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1610350614&amp;amp;tps.limit.interval&lt;span style="color:#719e07">=&lt;/span>&amp;amp;tps.limit.rate&lt;span style="color:#719e07">=&lt;/span>&amp;amp;tps.limit.rejected.handler&lt;span style="color:#719e07">=&lt;/span>&amp;amp;tps.limit.strategy&lt;span style="color:#719e07">=&lt;/span>&amp;amp;tps.limiter&lt;span style="color:#719e07">=&lt;/span>&amp;amp;&lt;span style="color:#268bd2">version&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1.0.0&amp;amp;&lt;span style="color:#268bd2">warmup&lt;/span>&lt;span style="color:#719e07">=&lt;/span>100!
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2021-01-11T15:36:55.003+0800 INFO dubbo/dubbo_protocol.go:86 Export service: dubbo://:20000/DemoProvider?accesslog&lt;span style="color:#719e07">=&lt;/span>&amp;amp;app.version&lt;span style="color:#719e07">=&lt;/span>1.0.0&amp;amp;&lt;span style="color:#268bd2">application&lt;/span>&lt;span style="color:#719e07">=&lt;/span>Demo+Micro+Service&amp;amp;&lt;span style="color:#268bd2">auth&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&amp;amp;bean.name&lt;span style="color:#719e07">=&lt;/span>DemoProvider&amp;amp;&lt;span style="color:#268bd2">cluster&lt;/span>&lt;span style="color:#719e07">=&lt;/span>failover&amp;amp;&lt;span style="color:#268bd2">environment&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dev&amp;amp;execute.limit&lt;span style="color:#719e07">=&lt;/span>&amp;amp;execute.limit.rejected.handler&lt;span style="color:#719e07">=&lt;/span>&amp;amp;&lt;span style="color:#268bd2">group&lt;/span>&lt;span style="color:#719e07">=&lt;/span>tc&amp;amp;&lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.funnycode.DemoService&amp;amp;&lt;span style="color:#268bd2">loadbalance&lt;/span>&lt;span style="color:#719e07">=&lt;/span>random&amp;amp;methods.SayHello.loadbalance&lt;span style="color:#719e07">=&lt;/span>random&amp;amp;methods.SayHello.retries&lt;span style="color:#719e07">=&lt;/span>3&amp;amp;methods.SayHello.tps.limit.interval&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello.tps.limit.rate&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello.tps.limit.strategy&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello.weight&lt;span style="color:#719e07">=&lt;/span>0&amp;amp;methods.SayHello4.loadbalance&lt;span style="color:#719e07">=&lt;/span>random&amp;amp;methods.SayHello4.retries&lt;span style="color:#719e07">=&lt;/span>3&amp;amp;methods.SayHello4.tps.limit.interval&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello4.tps.limit.rate&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello4.tps.limit.strategy&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello4.weight&lt;span style="color:#719e07">=&lt;/span>0&amp;amp;methods.SayHello5.loadbalance&lt;span style="color:#719e07">=&lt;/span>random&amp;amp;methods.SayHello5.retries&lt;span style="color:#719e07">=&lt;/span>3&amp;amp;methods.SayHello5.tps.limit.interval&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello5.tps.limit.rate&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello5.tps.limit.strategy&lt;span style="color:#719e07">=&lt;/span>&amp;amp;methods.SayHello5.weight&lt;span style="color:#719e07">=&lt;/span>0&amp;amp;&lt;span style="color:#268bd2">module&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbogoproxy+tc+client&amp;amp;&lt;span style="color:#268bd2">name&lt;/span>&lt;span style="color:#719e07">=&lt;/span>Demo+Micro+Service&amp;amp;&lt;span style="color:#268bd2">organization&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbogoproxy.com&amp;amp;&lt;span style="color:#268bd2">owner&lt;/span>&lt;span style="color:#719e07">=&lt;/span>ZX&amp;amp;param.sign&lt;span style="color:#719e07">=&lt;/span>&amp;amp;registry.role&lt;span style="color:#719e07">=&lt;/span>3&amp;amp;&lt;span style="color:#268bd2">release&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbo-golang-1.3.0&amp;amp;&lt;span style="color:#268bd2">retries&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&amp;amp;&lt;span style="color:#268bd2">serialization&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&amp;amp;service.filter&lt;span style="color:#719e07">=&lt;/span>echo%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&amp;amp;&lt;span style="color:#268bd2">side&lt;/span>&lt;span style="color:#719e07">=&lt;/span>provider&amp;amp;ssl-enabled&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1610350614&amp;amp;tps.limit.interval&lt;span style="color:#719e07">=&lt;/span>&amp;amp;tps.limit.rate&lt;span style="color:#719e07">=&lt;/span>&amp;amp;tps.limit.rejected.handler&lt;span style="color:#719e07">=&lt;/span>&amp;amp;tps.limit.strategy&lt;span style="color:#719e07">=&lt;/span>&amp;amp;tps.limiter&lt;span style="color:#719e07">=&lt;/span>&amp;amp;&lt;span style="color:#268bd2">version&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1.0.0&amp;amp;&lt;span style="color:#268bd2">warmup&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">100&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="39-复现代码">3.9 复现代码&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://github.com/cityiron/java_study/tree/master/dubbo2.7.7/dg-issue900">https://github.com/cityiron/java_study/tree/master/dubbo2.7.7/dg-issue900&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/cityiron/golang_study/tree/master/dubbogo/1.5.4/arg-bug">https://github.com/cityiron/golang_study/tree/master/dubbogo/1.5.4/arg-bug&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="四参考">四、参考&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://cn.dubbo.apache.org/zh/docsv2.7/user/configuration/api/">https://dubbo.apache.org/zh/docsv2.7/user/configuration/api/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/apache/dubbo-go/issues/257">https://github.com/apache/dubbo-go/issues/257&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>篇幅有限，就介绍到这里。欢迎有兴趣的同学来参与 &lt;a href="https://github.com/apache/dubbo-go/tree/3.0">dubbogo3.0&lt;/a> 的建设，感谢阅读。&lt;/p></description></item><item><title>Blog: dubbo-go源码笔记（二）客户端调用过程</title><link>https://cn.dubbo.apache.org/zh/blog/2021/01/15/dubbo-go%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%BA%8C%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B0%83%E7%94%A8%E8%BF%87%E7%A8%8B/</link><pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/01/15/dubbo-go%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%BA%8C%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B0%83%E7%94%A8%E8%BF%87%E7%A8%8B/</guid><description>
&lt;h2 id="前言">前言&lt;/h2>
&lt;p>有了上一篇文章&lt;a href="https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%B8%80server-%E7%AB%AF%E5%BC%80%E5%90%AF%E6%9C%8D%E5%8A%A1%E8%BF%87%E7%A8%8B/" title="">《dubbo-go 源码笔记（一）Server服务暴露过程详解》&lt;/a> 的铺垫，可以大致上类比客户端服务类似于服务端启动过程。其中最大的区别是服务端通过zk注册服务，发布自己的ivkURL并订阅事件开启监听；而服务端应该是通过zk注册组件，&lt;strong>拿到需要调用的serviceURL&lt;/strong>，&lt;strong>更新invoker&lt;/strong>并&lt;strong>重写用户的RPCService&lt;/strong>，从而实现对远程过程调用细节的封装。&lt;/p>
&lt;h2 id="1-配置文件和客户端源码">1. 配置文件和客户端源码&lt;/h2>
&lt;h4 id="11-client配置文件">1.1 client配置文件&lt;/h4>
&lt;p>helloworld提供的demo：profiles/client.yaml&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">registries &lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;demoZk&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">timeout &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;127.0.0.1:2181&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">username&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">password&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">references&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;UserProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 可以指定多个registry，使用逗号隔开;不指定默认向所有注册中心注册&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;demoZk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.ikurento.user.UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods &lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;GetUser&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可看到配置文件与之前讨论过的server端非常类似，其refrences部分字段就是对当前服务要主调的服务的配置，其中详细说明了调用协议、注册协议、接口id、调用方法、集群策略等，这些配置都会在之后与注册组件交互，重写ivk、调用的过程中使用到。&lt;/p>
&lt;h4 id="12-客户端使用框架源码">1.2 客户端使用框架源码&lt;/h4>
&lt;p>user.go&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">SetConsumerService&lt;/span>(userProvider)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hessian.&lt;span style="color:#268bd2">RegisterPOJO&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>User{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>main.go&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hessian.&lt;span style="color:#268bd2">RegisterPOJO&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>User{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> time.&lt;span style="color:#268bd2">Sleep&lt;/span>(&lt;span style="color:#2aa198">3e9&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">println&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;\n\n\nstart to test dubbo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> user &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>User{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#719e07">:=&lt;/span> userProvider.&lt;span style="color:#268bd2">GetUser&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), []&lt;span style="color:#268bd2">interface&lt;/span>{}{&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>}, user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">println&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;response result: %v\n&amp;#34;&lt;/span>, user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">initSignal&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>官网提供的helloworld demo的源码。可看到与服务端类似，在user.go内注册了rpc-service，以及需要rpc传输的结构体user。&lt;/p>
&lt;p>在main函数中，同样调用了config.Load()函数，之后就可以直接通过实现好的rpc-service：userProvider 直接调用对应的功能函数，即可实现rpc调用。&lt;/p>
&lt;p>可以猜到，从hessian注册结构、SetConsumerService，到调用函数.GetUser()期间，用户定义的rpc-service也就是userProvider对应的函数被重写，重写后的GetUser函数已经包含了实现了远程调用逻辑的invoker。&lt;/p>
&lt;p>接下来，就要通过阅读源码，看看dubbo-go是如何做到的。&lt;/p>
&lt;h2 id="2-实现远程过程调用">2. 实现远程过程调用&lt;/h2>
&lt;h4 id="21-加载配置文件">2.1 加载配置文件&lt;/h4>
&lt;p>config/config_loader.go :Load()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Load Dubbo Init
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">Load&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init router
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">initRouter&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init the global event dispatcher
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> extension.&lt;span style="color:#268bd2">SetAndInitGlobalDispatcher&lt;/span>(&lt;span style="color:#268bd2">GetBaseConfig&lt;/span>().EventDispatcherType)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// start the metadata report if config set
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">startMetadataReport&lt;/span>(&lt;span style="color:#268bd2">GetApplicationConfig&lt;/span>().MetadataType, &lt;span style="color:#268bd2">GetBaseConfig&lt;/span>().MetadataReportConfig); err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;Provider starts metadata report error, and the error is {%#v}&amp;#34;&lt;/span>, err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// reference config
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">loadConsumerConfig&lt;/span>()
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在main函数中调用的config.Load()函数，进而调用了loadConsumerConfig，类似于之前讲到的server端配置读入函数。&lt;/p>
&lt;p>在loadConsumerConfig函数中，进行了三步操作：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code2/p1.png" alt="">&lt;/p>
&lt;ol>
&lt;li>检查配置文件并将配置写入内存&lt;/li>
&lt;li>&lt;strong>在for循环内部&lt;/strong>，依次引用（refer）并且实例化（implement）每个被调reference。&lt;/li>
&lt;li>等待三秒钟所有invoker就绪&lt;/li>
&lt;/ol>
&lt;p>其中重要的就是for循环里面的引用和实例化，两步操作，会在接下来展开讨论。&lt;/p>
&lt;p>至此，配置已经被写入了框架。&lt;/p>
&lt;h4 id="22-获取远程service-url实现可供调用的invoker">2.2 获取远程Service URL，实现可供调用的invoker&lt;/h4>
&lt;p>上述的ref.Refer完成的就是这部分的操作。&lt;/p>
&lt;p>图（一）&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code2/p2.png" alt="">&lt;/p>
&lt;h5 id="221-构造注册url">2.2.1 构造注册url&lt;/h5>
&lt;p>和server端类似，存在注册url和服务url，dubbo习惯将服务url作为注册url的sub。&lt;/p>
&lt;p>config/reference_config.go: Refer()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">/&lt;/span> Refer &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (c &lt;span style="color:#719e07">*&lt;/span>ReferenceConfig) &lt;span style="color:#268bd2">Refer&lt;/span>(_ &lt;span style="color:#268bd2">interface&lt;/span>{}) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//（一）配置url参数(serviceUrl)，将会作为sub
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cfgURL &lt;span style="color:#719e07">:=&lt;/span> common.&lt;span style="color:#268bd2">NewURLWithOptions&lt;/span>(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> common.&lt;span style="color:#268bd2">WithPath&lt;/span>(c.id),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> common.&lt;span style="color:#268bd2">WithProtocol&lt;/span>(c.Protocol),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> common.&lt;span style="color:#268bd2">WithParams&lt;/span>(c.&lt;span style="color:#268bd2">getUrlMap&lt;/span>()),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> common.&lt;span style="color:#268bd2">WithParamsValue&lt;/span>(constant.BEAN_NAME_KEY, c.id),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// （二）注册地址可以通过url格式给定，也可以通过配置格式给定
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 这一步的意义就是配置-&amp;gt;提取信息生成URL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> c.Url &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span> {&lt;span style="color:#586e75">// 用户给定url信息，可以是点对点的地址，也可以是注册中心的地址
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 1. user specified URL, could be peer-to-peer address, or register center&amp;#39;s address.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> urlStrings &lt;span style="color:#719e07">:=&lt;/span> gxstrings.&lt;span style="color:#268bd2">RegSplit&lt;/span>(c.Url, &lt;span style="color:#2aa198">&amp;#34;\\s*[;]+\\s*&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> _, urlStr &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">range&lt;/span> urlStrings {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceUrl, err &lt;span style="color:#719e07">:=&lt;/span> common.&lt;span style="color:#268bd2">NewURL&lt;/span>(urlStr)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {&lt;span style="color:#586e75">// 配置读入注册中心的信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// assemble SubURL from register center&amp;#39;s configuration mode
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 这是注册url，protocol = registry,包含了zk的用户名、密码、ip等等
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> c.urls = &lt;span style="color:#268bd2">loadRegistries&lt;/span>(c.Registry, consumerConfig.Registries, common.CONSUMER)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// set url to regUrls
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> _, regUrl &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">range&lt;/span> c.urls {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> regUrl.SubURL = cfgURL&lt;span style="color:#586e75">// regUrl的subURl存当前配置url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//至此，无论通过什么形式，已经拿到了全部的regURL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// （三）获取registryProtocol实例，调用其Refer方法，传入新构建好的regURL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#b58900">len&lt;/span>(c.urls) &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 这一步访问到registry/protocol/protocol.go registryProtocol.Refer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 这里是registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> c.invoker = extension.&lt;span style="color:#268bd2">GetProtocol&lt;/span>(c.urls[&lt;span style="color:#2aa198">0&lt;/span>].Protocol).&lt;span style="color:#268bd2">Refer&lt;/span>(&lt;span style="color:#719e07">*&lt;/span>c.urls[&lt;span style="color:#2aa198">0&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 如果有多个注册中心，即有多个invoker,则采取集群策略
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invokers &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">make&lt;/span>([]protocol.Invoker, &lt;span style="color:#2aa198">0&lt;/span>, &lt;span style="color:#b58900">len&lt;/span>(c.urls))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个函数中，已经处理完从Register配置到RegisterURL的转换,即图（一）中部分：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code2/p3.png" alt="">&lt;/p>
&lt;p>接下来，已经拿到的url将被传递给RegistryProtocol，进一步refer。&lt;/p>
&lt;h5 id="222-registryprotocol获取到zkregistry实例进一步refer">2.2.2 registryProtocol获取到zkRegistry实例，进一步Refer&lt;/h5>
&lt;p>registry/protocol/protocol.go: Refer&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Refer provider service from registry center
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 拿到的是配置文件registries的url，他能够生成一个invoker = 指向目的addr，以供客户端直接调用。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (proto &lt;span style="color:#719e07">*&lt;/span>registryProtocol) &lt;span style="color:#268bd2">Refer&lt;/span>(url common.URL) protocol.Invoker {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">var&lt;/span> registryUrl = url
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 这里拿到的是referenceConfig，serviceUrl里面包含了Reference的所有信息，包含interfaceName、method等等
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">var&lt;/span> serviceUrl = registryUrl.SubURL
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> registryUrl.Protocol &lt;span style="color:#719e07">==&lt;/span> constant.REGISTRY_PROTOCOL {&lt;span style="color:#586e75">// registryUrl.Proto = &amp;#34;registry&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> protocol &lt;span style="color:#719e07">:=&lt;/span> registryUrl.&lt;span style="color:#268bd2">GetParam&lt;/span>(constant.REGISTRY_KEY, &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryUrl.Protocol = protocol&lt;span style="color:#586e75">//替换成了具体的值，比如&amp;#34;zookeeper&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 接口对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">var&lt;/span> reg registry.Registry
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// （一）实例化接口对象，缓存策略
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> regI, loaded &lt;span style="color:#719e07">:=&lt;/span> proto.registries.&lt;span style="color:#268bd2">Load&lt;/span>(registryUrl.&lt;span style="color:#268bd2">Key&lt;/span>()); !loaded {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 缓存中不存在当前registry，新建一个reg
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> reg = &lt;span style="color:#268bd2">getRegistry&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>registryUrl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 缓存起来
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> proto.registries.&lt;span style="color:#268bd2">Store&lt;/span>(registryUrl.&lt;span style="color:#268bd2">Key&lt;/span>(), reg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reg = regI.(registry.Registry)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 到这里，获取到了reg实例 zookeeper的registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//（二）根据Register的实例zkRegistry和传入的regURL新建一个directory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 这一步存在复杂的异步逻辑，从注册中心拿到了目的service的真实addr，获取了invoker并放入directory，
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 这一步将在下面详细给出步骤
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// new registry directory for store service url from registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> directory, err &lt;span style="color:#719e07">:=&lt;/span> extension.&lt;span style="color:#268bd2">GetDefaultRegistryDirectory&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>registryUrl, reg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;consumer service %v create registry directory error, error message is %s, and will return nil invoker!&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceUrl.&lt;span style="color:#268bd2">String&lt;/span>(), err.&lt;span style="color:#268bd2">Error&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// （三）DoRegister 在zk上注册当前client service
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> err = reg.&lt;span style="color:#268bd2">Register&lt;/span>(&lt;span style="color:#719e07">*&lt;/span>serviceUrl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;consumer service %v register registry %v error, error message is %s&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceUrl.&lt;span style="color:#268bd2">String&lt;/span>(), registryUrl.&lt;span style="color:#268bd2">String&lt;/span>(), err.&lt;span style="color:#268bd2">Error&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// （四）new cluster invoker，将directory写入集群，获得具有集群策略的invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cluster &lt;span style="color:#719e07">:=&lt;/span> extension.&lt;span style="color:#268bd2">GetCluster&lt;/span>(serviceUrl.&lt;span style="color:#268bd2">GetParam&lt;/span>(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker &lt;span style="color:#719e07">:=&lt;/span> cluster.&lt;span style="color:#268bd2">Join&lt;/span>(directory)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoker保存
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> proto.invokers = &lt;span style="color:#b58900">append&lt;/span>(proto.invokers, invoker)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可详细阅读上述注释，这个函数完成了从url到invoker的全部过程&lt;/p>
&lt;p>（一）首先获得Registry对象，默认是之前实例化的zkRegistry，和之前server获取Registry的处理很类似。
（二）通过构造一个新的directory，异步拿到之前在zk上注册的server端信息，生成invoker
（三）在zk上注册当前service
（四）集群策略，获得最终invoker&lt;/p>
&lt;p>这一步完成了图（一）中所有余下的绝大多数操作，接下来就需要详细的查看directory的构造过程：&lt;/p>
&lt;h5 id="223-构造directory包含较复杂的异步操作">2.2.3 构造directory（包含较复杂的异步操作）&lt;/h5>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code2/p4.png" alt="">&lt;/p>
&lt;p>图（二）&lt;/p>
&lt;p>上述的 &lt;code>extension.GetDefaultRegistryDirectory(&amp;amp;registryUrl, reg)&lt;/code>函数，本质上调用了已经注册好的&lt;code>NewRegistryDirectory&lt;/code>函数:&lt;/p>
&lt;p>registry/directory/directory.go: NewRegistryDirectory()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// NewRegistryDirectory will create a new RegistryDirectory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 这个函数作为default注册在extension上面
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// url为注册url，reg为zookeeper registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">NewRegistryDirectory&lt;/span>(url &lt;span style="color:#719e07">*&lt;/span>common.URL, registry registry.Registry) (cluster.Directory, &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> url.SubURL &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>, perrors.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;url is invalid, suburl can not be nil&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dir &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>RegistryDirectory{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> BaseDirectory: directory.&lt;span style="color:#268bd2">NewBaseDirectory&lt;/span>(url),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cacheInvokers: []protocol.Invoker{},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cacheInvokersMap: &lt;span style="color:#719e07">&amp;amp;&lt;/span>sync.Map{},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceType: url.SubURL.&lt;span style="color:#268bd2">Service&lt;/span>(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry: registry,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dir.consumerConfigurationListener = &lt;span style="color:#268bd2">newConsumerConfigurationListener&lt;/span>(dir)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">go&lt;/span> dir.&lt;span style="color:#268bd2">subscribe&lt;/span>(url.SubURL)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> dir, &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>首先构造了一个注册directory，开启携程调用其subscribe函数，传入serviceURL。&lt;/p>
&lt;p>这个directory目前包含了对应的zkRegistry，以及传入的URL，他cacheInvokers的部分是空的。&lt;/p>
&lt;p>进入dir.subscribe(url.SubURL)这个异步函数：&lt;/p>
&lt;p>registry/directory/directory.go: subscribe()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// subscribe from registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (dir &lt;span style="color:#719e07">*&lt;/span>RegistryDirectory) &lt;span style="color:#268bd2">subscribe&lt;/span>(url &lt;span style="color:#719e07">*&lt;/span>common.URL) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 增加两个监听，
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> dir.consumerConfigurationListener.&lt;span style="color:#268bd2">addNotifyListener&lt;/span>(dir)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dir.referenceConfigurationListener = &lt;span style="color:#268bd2">newReferenceConfigurationListener&lt;/span>(dir, url)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// subscribe调用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> dir.registry.&lt;span style="color:#268bd2">Subscribe&lt;/span>(url, dir)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>重点来了，他调用了zkRegistry的Subscribe方法,与此同时将自己作为ConfigListener传入&lt;/p>
&lt;blockquote>
&lt;p>我认为这种传入listener的设计模式非常值得学习，而且很有java的味道。&lt;/p>
&lt;p>针对等待zk返回订阅信息这样的异步操作，需要传入一个Listener，这个Listener需要实现Notify方法，进而在作为参数传入内部之后，可以被异步地调用Notify，将内部触发的异步事件“传递出来”，再进一步处理加工。&lt;/p>
&lt;p>层层的Listener事件链，能将传入的原始serviceURL通过zkConn发送给zk服务，获取到服务端注册好的url对应的二进制信息。&lt;/p>
&lt;p>而Notify回调链，则将这串byte[]一步一步解析、加工；以事件的形式向外传递，最终落到directory上的时候，已经是成型的newInvokers了。&lt;/p>
&lt;p>具体细节不再以源码形式展示，可参照上图查阅源码。&lt;/p>
&lt;/blockquote>
&lt;p>至此已经拿到了server端注册好的真实invoker。&lt;/p>
&lt;p>完成了图（一）中的部分：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code2/p5.png" alt="">&lt;/p>
&lt;h5 id="224-构造带有集群策略的clusterinvoker">2.2.4 构造带有集群策略的clusterinvoker&lt;/h5>
&lt;p>经过上述操作，已经拿到了server端Invokers，放入了directory的cacheinvokers数组里面缓存。&lt;/p>
&lt;p>后续的操作对应本文2.2.2的第四步，由directory生成带有特性集群策略的invoker&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// （四）new cluster invoker，将directory写入集群，获得具有集群策略的invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cluster &lt;span style="color:#719e07">:=&lt;/span> extension.&lt;span style="color:#268bd2">GetCluster&lt;/span>(serviceUrl.&lt;span style="color:#268bd2">GetParam&lt;/span>(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker &lt;span style="color:#719e07">:=&lt;/span> cluster.&lt;span style="color:#268bd2">Join&lt;/span>(directory)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">123&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Join函数的实现就是如下函数：&lt;/p>
&lt;p>cluster/cluster_impl/failover_cluster_invokers.go: newFailoverClusterInvoker()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">newFailoverClusterInvoker&lt;/span>(directory cluster.Directory) protocol.Invoker {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>failoverClusterInvoker{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> baseClusterInvoker: &lt;span style="color:#268bd2">newBaseClusterInvoker&lt;/span>(directory),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">12345&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>dubbo-go框架默认选择failover策略，既然返回了一个invoker，我们查看一下failoverClusterInvoker的Invoker方法，看他是如何将集群策略封装到Invoker函数内部的：&lt;/p>
&lt;p>cluster/cluster_impl/failover_cluster_invokers.go: Invoker()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Invoker 函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (invoker &lt;span style="color:#719e07">*&lt;/span>failoverClusterInvoker) &lt;span style="color:#268bd2">Invoke&lt;/span>(ctx context.Context, invocation protocol.Invocation) protocol.Result {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用List方法拿到directory缓存的所有invokers
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invokers &lt;span style="color:#719e07">:=&lt;/span> invoker.directory.&lt;span style="color:#268bd2">List&lt;/span>(invocation)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">:=&lt;/span> invoker.&lt;span style="color:#268bd2">checkInvokers&lt;/span>(invokers, invocation); err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {&lt;span style="color:#586e75">// 检查是否可以实现调用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>protocol.RPCResult{Err: err}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取来自用户方向传入的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> methodName &lt;span style="color:#719e07">:=&lt;/span> invocation.&lt;span style="color:#268bd2">MethodName&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> retries &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">getRetries&lt;/span>(invokers, methodName)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadBalance &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">getLoadBalance&lt;/span>(invokers[&lt;span style="color:#2aa198">0&lt;/span>], invocation)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> i &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>; i &lt;span style="color:#719e07">&amp;lt;=&lt;/span> retries; i&lt;span style="color:#719e07">++&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 重要！这里是集群策略的体现，失败后重试！
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//Reselect before retry to avoid a change of candidate `invokers`.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> i &amp;gt; &lt;span style="color:#2aa198">0&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">:=&lt;/span> invoker.&lt;span style="color:#268bd2">checkWhetherDestroyed&lt;/span>(); err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>protocol.RPCResult{Err: err}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invokers = invoker.directory.&lt;span style="color:#268bd2">List&lt;/span>(invocation)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">:=&lt;/span> invoker.&lt;span style="color:#268bd2">checkInvokers&lt;/span>(invokers, invocation); err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>protocol.RPCResult{Err: err}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 这里是负载均衡策略的体现！选择特定ivk进行调用。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ivk &lt;span style="color:#719e07">:=&lt;/span> invoker.&lt;span style="color:#268bd2">doSelect&lt;/span>(loadBalance, invocation, invokers, invoked)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ivk &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoked = &lt;span style="color:#b58900">append&lt;/span>(invoked, ivk)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//DO INVOKE
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> result = ivk.&lt;span style="color:#268bd2">Invoke&lt;/span>(ctx, invocation)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> result.&lt;span style="color:#268bd2">Error&lt;/span>() &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> providers = &lt;span style="color:#b58900">append&lt;/span>(providers, ivk.&lt;span style="color:#268bd2">GetUrl&lt;/span>().&lt;span style="color:#268bd2">Key&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> result
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>看了很多Invoke函数的实现，所有类似的Invoker函数都包含两个方向，一个是用户方向的invcation，一个是函数方向的底层invokers。&lt;/p>
&lt;p>而集群策略的invoke函数本身作为接线员，把invocation一步步解析，根据调用需求和集群策略，选择特定的invoker来执行&lt;/p>
&lt;p>proxy函数也是这样，一个是用户方向的ins[] reflect.Type, 一个是函数方向的invoker。&lt;/p>
&lt;p>proxy函数负责将ins转换为invocation，调用对应invoker的invoker函数，实现连通。&lt;/p>
&lt;p>而出于这样的设计，可以在一步步Invoker封装的过程中，每个Invoker只关心自己负责操作的部分，从而使整个调用栈解耦。&lt;/p>
&lt;p>妙啊！！！&lt;/p>
&lt;/blockquote>
&lt;p>至此，我们理解了failoverClusterInvoker 的Invoke函数实现，也正是和这个集群策略Invoker被返回，接受来自上方的调用。&lt;/p>
&lt;p>已完成图（一）中的：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code2/p6.png" alt="">&lt;/p>
&lt;h5 id="225-在zookeeper上注册当前client">2.2.5 在zookeeper上注册当前client&lt;/h5>
&lt;p>拿到invokers后，可以回到：&lt;/p>
&lt;p>config/refrence_config.go: Refer()函数了。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#b58900">len&lt;/span>(c.urls) &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 这一步访问到registry/protocol/protocol.go registryProtocol.Refer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> c.invoker = extension.&lt;span style="color:#268bd2">GetProtocol&lt;/span>(c.urls[&lt;span style="color:#2aa198">0&lt;/span>].Protocol).&lt;span style="color:#268bd2">Refer&lt;/span>(&lt;span style="color:#719e07">*&lt;/span>c.urls[&lt;span style="color:#2aa198">0&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// （一）拿到了真实的invokers
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 如果有多个注册中心，即有多个invoker,则采取集群策略
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invokers &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">make&lt;/span>([]protocol.Invoker, &lt;span style="color:#2aa198">0&lt;/span>, &lt;span style="color:#b58900">len&lt;/span>(c.urls))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cluster &lt;span style="color:#719e07">:=&lt;/span> extension.&lt;span style="color:#268bd2">GetCluster&lt;/span>(hitClu)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// If &amp;#39;zone-aware&amp;#39; policy select, the invoker wrap sequence would be:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// ZoneAwareClusterInvoker(StaticDirectory) -&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// FailoverClusterInvoker(RegistryDirectory, routing happens here) -&amp;gt; Invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> c.invoker = cluster.&lt;span style="color:#268bd2">Join&lt;/span>(directory.&lt;span style="color:#268bd2">NewStaticDirectory&lt;/span>(invokers))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// （二）create proxy，为函数配置代理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> c.Async {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> callback &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">GetCallback&lt;/span>(c.id)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> c.pxy = extension.&lt;span style="color:#268bd2">GetProxyFactory&lt;/span>(consumerConfig.ProxyFactory).&lt;span style="color:#268bd2">GetAsyncProxy&lt;/span>(c.invoker, callback, cfgURL)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 这里c.invoker已经是目的addr了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> c.pxy = extension.&lt;span style="color:#268bd2">GetProxyFactory&lt;/span>(consumerConfig.ProxyFactory).&lt;span style="color:#268bd2">GetProxy&lt;/span>(c.invoker, cfgURL)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们有了可以打通的invokers，但还不能直接调用，因为invoker的入参是invocation，而调用函数使用的是具体的参数列表。需要通过一层proxy来规范入参和出参。&lt;/p>
&lt;p>接下来新建一个默认proxy，放置在c.proxy内，以供后续使用&lt;/p>
&lt;p>至此，完成了图（一）中最后的操作&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code2/p7.png" alt="">&lt;/p>
&lt;h3 id="23-将调用逻辑以代理函数的形式写入rpc-service">2.3 将调用逻辑以代理函数的形式写入rpc-service&lt;/h3>
&lt;p>上面完成了config.Refer操作&lt;/p>
&lt;p>回到config/config_loader.go: loadConsumerConfig()&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code2/p8.png" alt="">&lt;/p>
&lt;p>下一个重要的函数是Implement，他完的操作较为简单：旨在使用上面生成的c.proxy代理，链接用户自己定义的rpcService到clusterInvoker的信息传输。&lt;/p>
&lt;p>函数较长，只选取了重要的部分:&lt;/p>
&lt;p>common/proxy/proxy.go: Implement()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Implement
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// proxy implement
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// In consumer, RPCService like:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// type XxxProvider struct {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Yyy func(ctx context.Context, args []interface{}, rsp *Zzz) error
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Implement 实现的过程，就是proxy根据函数名和返回值，通过调用invoker 构造出拥有远程调用逻辑的代理函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 将当前rpc所有可供调用的函数注册到proxy.rpc内
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>Proxy) &lt;span style="color:#268bd2">Implement&lt;/span>(v common.RPCService) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// makeDubboCallProxy 这是一个构造代理函数，这个函数的返回值是func(in []reflect.Value) []reflect.Value 这样一个函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 这个被返回的函数是请求实现的载体，由他来发起调用获取结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> makeDubboCallProxy &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">func&lt;/span>(methodName &lt;span style="color:#dc322f">string&lt;/span>, outs []reflect.Type) &lt;span style="color:#268bd2">func&lt;/span>(in []reflect.Value) []reflect.Value {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#268bd2">func&lt;/span>(in []reflect.Value) []reflect.Value {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 根据methodName和outs的类型，构造这样一个函数，这个函数能将in 输入的value转换为输出的value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 这个函数具体的实现如下：
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 目前拿到了 methodName、所有入参的interface和value，出参数reply
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// （一）根据这些生成一个 rpcinvocation
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> inv = invocation_impl.&lt;span style="color:#268bd2">NewRPCInvocationWithOptions&lt;/span>(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invocation_impl.&lt;span style="color:#268bd2">WithMethodName&lt;/span>(methodName),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invocation_impl.&lt;span style="color:#268bd2">WithArguments&lt;/span>(inIArr),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invocation_impl.&lt;span style="color:#268bd2">WithReply&lt;/span>(reply.&lt;span style="color:#268bd2">Interface&lt;/span>()),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invocation_impl.&lt;span style="color:#268bd2">WithCallBack&lt;/span>(p.callBack),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invocation_impl.&lt;span style="color:#268bd2">WithParameterValues&lt;/span>(inVArr))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> k, value &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">range&lt;/span> p.attachments {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inv.&lt;span style="color:#268bd2">SetAttachments&lt;/span>(k, value)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// add user setAttachment
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> atm &lt;span style="color:#719e07">:=&lt;/span> invCtx.&lt;span style="color:#268bd2">Value&lt;/span>(constant.AttachmentKey) &lt;span style="color:#586e75">// 如果传入的ctx里面有attachment，也要写入inv
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> m, ok &lt;span style="color:#719e07">:=&lt;/span> atm.(&lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]&lt;span style="color:#dc322f">string&lt;/span>); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> k, value &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">range&lt;/span> m {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inv.&lt;span style="color:#268bd2">SetAttachments&lt;/span>(k, value)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 至此构造inv完毕
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// (二）触发Invoker 之前已经将cluster_invoker放入proxy，使用Invoke方法，通过getty远程过程调用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> result &lt;span style="color:#719e07">:=&lt;/span> p.invoke.&lt;span style="color:#268bd2">Invoke&lt;/span>(invCtx, inv)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 如果有attachment，则加入
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#b58900">len&lt;/span>(result.&lt;span style="color:#268bd2">Attachments&lt;/span>()) &amp;gt; &lt;span style="color:#2aa198">0&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invCtx = context.&lt;span style="color:#268bd2">WithValue&lt;/span>(invCtx, constant.AttachmentKey, result.&lt;span style="color:#268bd2">Attachments&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> numField &lt;span style="color:#719e07">:=&lt;/span> valueOfElem.&lt;span style="color:#268bd2">NumField&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> i &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>; i &amp;lt; numField; i&lt;span style="color:#719e07">++&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t &lt;span style="color:#719e07">:=&lt;/span> typeOf.&lt;span style="color:#268bd2">Field&lt;/span>(i)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> methodName &lt;span style="color:#719e07">:=&lt;/span> t.Tag.&lt;span style="color:#268bd2">Get&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> methodName &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> methodName = t.Name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> f &lt;span style="color:#719e07">:=&lt;/span> valueOfElem.&lt;span style="color:#268bd2">Field&lt;/span>(i)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> f.&lt;span style="color:#268bd2">Kind&lt;/span>() &lt;span style="color:#719e07">==&lt;/span> reflect.Func &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> f.&lt;span style="color:#268bd2">IsValid&lt;/span>() &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> f.&lt;span style="color:#268bd2">CanSet&lt;/span>() { &lt;span style="color:#586e75">// 针对于每个函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> outNum &lt;span style="color:#719e07">:=&lt;/span> t.Type.&lt;span style="color:#268bd2">NumOut&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 规定函数输出只能有1/2个
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> outNum &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#2aa198">1&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> outNum &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#2aa198">2&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Warnf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;method %s of mtype %v has wrong number of in out parameters %d; needs exactly 1/2&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t.Name, t.Type.&lt;span style="color:#268bd2">String&lt;/span>(), outNum)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// The latest return type of the method must be error.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 规定最后一个返回值一定是error
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> returnType &lt;span style="color:#719e07">:=&lt;/span> t.Type.&lt;span style="color:#268bd2">Out&lt;/span>(outNum &lt;span style="color:#719e07">-&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>); returnType &lt;span style="color:#719e07">!=&lt;/span> typError {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Warnf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;the latest return type %s of method %q is not error&amp;#34;&lt;/span>, returnType, t.Name)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取到所有的出参类型，放到数组里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">var&lt;/span> funcOuts = &lt;span style="color:#b58900">make&lt;/span>([]reflect.Type, outNum)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> i &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>; i &amp;lt; outNum; i&lt;span style="color:#719e07">++&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> funcOuts[i] = t.Type.&lt;span style="color:#268bd2">Out&lt;/span>(i)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// do method proxy here:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// （三）调用make函数，传入函数名和返回值，获得能调用远程的proxy，将这个proxy替换掉原来的函数位置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> f.&lt;span style="color:#268bd2">Set&lt;/span>(reflect.&lt;span style="color:#268bd2">MakeFunc&lt;/span>(f.&lt;span style="color:#268bd2">Type&lt;/span>(), &lt;span style="color:#268bd2">makeDubboCallProxy&lt;/span>(methodName, funcOuts)))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Debugf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;set method [%s]&amp;#34;&lt;/span>, methodName)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>正如之前所说，proxy的作用是将用户定义的函数参数列表，转化为抽象的invocation传入Invoker，进行调用。&lt;/p>
&lt;p>其中已标明有三处较为重要的地方：&lt;/p>
&lt;ol>
&lt;li>在代理函数中实现由参数列表生成Invocation的逻辑&lt;/li>
&lt;li>在代理函数实现调用Invoker的逻辑&lt;/li>
&lt;li>将代理函数替换为原始rpc-service对应函数
至此，也就解决了一开始的问题：
client.go: main()&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> user &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>User{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#719e07">:=&lt;/span> userProvider.&lt;span style="color:#268bd2">GetUser&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), []&lt;span style="color:#268bd2">interface&lt;/span>{}{&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>}, user)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里直接调用用户定义的rpcService的函数GetUser，这里实际调用的是经过重写入的函数代理，所以就能实现远程调用了。&lt;/p>
&lt;h3 id="3-从client到server的invoker嵌套链--小结">3. 从client到server的invoker嵌套链- 小结&lt;/h3>
&lt;p>在阅读dubbo-go源码的过程中，我能发现一条清晰的invoker-proxy嵌套链，我希望通过图的形式来展现：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code2/p9.png" alt="">&lt;/p>
&lt;blockquote>
&lt;p>作者简介 李志信 (GitHubID LaurenceLiZhixin)，中山大学软件工程专业在校学生，擅长使用 Java/Go 语言，专注于云原生和微服务等技术方向。&lt;/p>
&lt;/blockquote></description></item><item><title>Blog: dubbogo 3.0：牵手 gRPC 走向云原生时代</title><link>https://cn.dubbo.apache.org/zh/blog/2021/01/15/dubbogo-3.0%E7%89%B5%E6%89%8B-grpc-%E8%B5%B0%E5%90%91%E4%BA%91%E5%8E%9F%E7%94%9F%E6%97%B6%E4%BB%A3/</link><pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/01/15/dubbogo-3.0%E7%89%B5%E6%89%8B-grpc-%E8%B5%B0%E5%90%91%E4%BA%91%E5%8E%9F%E7%94%9F%E6%97%B6%E4%BB%A3/</guid><description>
&lt;p>自从 2011 年 Dubbo 开源之后，被大量中小公司采用，一直是国内最受欢迎的 RPC 框架。2014 年，由于阿里内部组织架构调整，Dubbo 暂停维护了一段时间，之后随着 Spring Cloud 的面世，两个体系在融合中一起助推了微服务的火热。&lt;/p>
&lt;p>不过这世界变化快，自从以 docker 为代表的的容器技术和以 K8s 为代表的容器编排技术登上舞台之后，云原生时代到来了。在云原生时代，不可变的基础设施给原有的中间件带来了不可变的中间件基础设施：gRPC 统一了底层通信层；protobuf 统一了序列化协议；以 envoy + istio 为代表的 service mesh 逐渐统一了服务的控制面与数据面。&lt;/p>
&lt;p>dubbogo 的天然使命是：Bridging the gap between Java and Go。保持 Go 应用与 Java 应用互联互通的同时，借助 Go 语言（事实上的第一云原生语言）的优势拥抱云原生时代。dubbogo 社区 2020 年勠力打造三支箭：&lt;/p>
&lt;ul>
&lt;li>已经发布的对齐 dubbo 2.7 的 dubbogo v1.5 版本；&lt;/li>
&lt;li>近期将要发布的 sidecar 形态的 dubbo-go-proxy 项目；&lt;/li>
&lt;li>以及处于进行时的 dubbogo 3.0。&lt;/li>
&lt;/ul>
&lt;p>用一句话概括 dubbogo 3.0 即是：新通信协议、新序列化协议、新应用注册模型以及新的服务治理能力！本文主要着重讨论 dubbogo 3.0 的新通信协议和应用级服务注册发现模型。&lt;/p>
&lt;h2 id="dubbogo-30-vs-grpc">dubbogo 3.0 vs gRPC&lt;/h2>
&lt;p>知己知彼，方能进步。dubbogo 3.0 的通信层改进主要借鉴了 gRPC。&lt;/p>
&lt;p>gRPC 协议，简单来说就是 http2 协议的基础之上，增加了特定的协议 header：“grpc-” 开头的 header 字段，采用特定的打解包工具（protobuf）对数据进行序列化，从而实现 RPC 调用。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/3.0-plan/p1.webp" alt="">&lt;/p>
&lt;p>众所周知，gRPC 几乎没有服务治理能力，而阿里云现有 dubbo 框架兼具 RPC 和服务治理能力，整体实力不逊于 gRPC。但在“大家都用 gRPC” 这样的背景之下，dubbogo 3.0 的新通信协议就必须&lt;strong>完美兼容 gRPC&lt;/strong>，对开发者已部署的服务完全兼容，并在此基础之上延续已有 dubbo 协议和服务治理能力，进而推出一系列新策略：比如 mesh 支持、应用级服务注册等。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/3.0-plan/p2.webp" alt="">&lt;/p>
&lt;h2 id="dubbogo-30-vs-dubbogo-15">dubbogo 3.0 vs dubbogo 1.5&lt;/h2>
&lt;p>目前已有的 dubbo 2.7 协议已经尽可能实现了 gRPC 的支持。开发者可以通过 protoc-gen-dubbo 工具将 pb IDL 协议转换为框架支持的 stub，再借助底层 gRPC conn 的 RPC 过程，将已有的服务治理能力自上而下传递给 gRPC，因此实现了 gRPC 服务的支持。&lt;/p>
&lt;p>dubbo-go v1.5.x 也支持 gRPC 的 Stream 调用。和 unary RPC 类似，通过产生框架支持的 stub，在底层 gRPC stream 调用的基础之上，将流式 RPC 的能力和并入框架。但由于 dubbo v2.7.x / dubbo-go v1.5.x 本身并不支持流式调用，所以没有对 gRPC stream 调用的进行上层服务治理支持。&lt;/p>
&lt;p>开发者所面临的问题就是：我们在使用 dubbo-go2.7 进行 grpc 协议传输的时候，或多或少不是那么放心。&lt;/p>
&lt;p>而即将推出的 dubbo-go 3.0 协议将从根源解决这个问题。&lt;/p>
&lt;h2 id="协议兼容的三种层次">协议兼容的三种层次&lt;/h2>
&lt;p>笔者认为，一款服务框架对于第三方协议的支持可分为三个程度：应用层次、协议层次、传输层次。&lt;/p>
&lt;p>一款框架如果在一个协议的 sdk 之上封装接口，可以认为它处于应用层次支持，这样的框架需要遵循下层 sdk 的接口，可扩展性较差。&lt;/p>
&lt;p>处于协议层次的框架，从配置层到服务治理层均由本框架提供，而在此之下的协议层到网络传输层均使用某个固定的通信协议，这样的框架可以解决服务治理的问题，但框架本身无法与第三方协议完全适配，如果不适配就会出现对第三方协议支持的削弱，比如上面说到的 dubbo-go 1.5 对 stream rpc 支持的缺陷。&lt;/p>
&lt;p>如果想进一步支持更多的第三方协议，需要从传输层下手，真正了解第三方协议的具体字段、所依赖的底层协议（比如 HTTP2）的帧模型和数据流，再开发出与第三方协议完全一致的数据交互模块，作为本框架的底层。这样做的好处是最大程度赋予了协议的可扩展性，可以在兼容已有协议的基础之上，可选地增加开发者需要的字段，从而实现已有协议无法实现的功能，就比如 dubbogo 3.0 将支持的反压策略。&lt;/p>
&lt;h2 id="基于-http2-的通信流程">基于 HTTP2 的通信流程&lt;/h2>
&lt;p>gRPC 一次基于 HTTP2 的 unary rpc 调用传输主要流程如下：&lt;/p>
&lt;ul>
&lt;li>client 发送 Magic 信息：
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n；&lt;/li>
&lt;li>server 收到并检查是否正确；&lt;/li>
&lt;li>client 和 server 互相发送 setting 帧，收到后发送 ACK 确认；&lt;/li>
&lt;li>client 发送 Header 帧，包含 gRPC 协议字段，以 End Headers 作为 Header 结束标志；&lt;/li>
&lt;li>client 紧接着发送 Data 帧，包含 RPC 调用的 request 信息，以 End Stream 作为 Data 结束标志；&lt;/li>
&lt;li>server 调用函数获得结果；&lt;/li>
&lt;li>server 发送 Header 帧，包含 gRPC 协议字段，以 End Headers 作为 Header 结束标志；&lt;/li>
&lt;li>server 紧接着发送 Data 帧，包含 RPC 调用回传的 response 信息；&lt;/li>
&lt;li>server 紧接着再次发送 Header 帧，包含 RPC 状态和 message 信息，以 End Stream 作为本次 RPC 调用结束标志。&lt;/li>
&lt;/ul>
&lt;p>其中包含 gRPC 调用信息的 HTTP2 Header 帧如下图：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/3.0-plan/p3.webp" alt="">&lt;/p>
&lt;p>另外，在 gRPC 的 stream 调用中，可在 server 端回传的过程中发送多次 Data，调用结束后再发送 Header 终止 RPC 过程，并汇报状态信息。&lt;/p>
&lt;p>dubbogo 3.0 的通信层将在 HTTP2 通信协议之上采用同样的通信流程，以保证与 gRPC 的底层通信沟通能力。&lt;/p>
&lt;h2 id="dubbogo-30-预期通信架构">dubbogo 3.0 预期通信架构&lt;/h2>
&lt;p>除了通信协议采用 HTTP2 外，dubbogo 3.0 将采用基于 google protobuf 的 triple 协议【下面称为 dubbo3 协议】作为 dubbogo 3.0 的序列化协议，为 dubbo 将来支持更多的编程语言打下通信协议层面的基础。&lt;/p>
&lt;p>目前设计的 dubbogo 3.0 传输模型如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/3.0-plan/p4.webp" alt="">&lt;/p>
&lt;ul>
&lt;li>为保证同时支持 unary RPC 和 stream RPC，在 server 端和 client 端增加数据流结构，以异步调用的形式完成数据传递；&lt;/li>
&lt;li>继续支持原有的 TCP 通信能力；&lt;/li>
&lt;li>在 HTTP2 的通信协议之上支持 dubbo3 协议，decode 过程兼容 gRPC 使用的 protobuf，保证与 gRPC 服务打通。&lt;/li>
&lt;/ul>
&lt;h2 id="应用级服务注册发现">应用级服务注册发现&lt;/h2>
&lt;h4 id="1-应用级服务注册发现介绍">1. 应用级服务注册发现介绍&lt;/h4>
&lt;p>dubbogo 3.0 使用的新一代服务注册发现体系，将摒弃旧版的“接口级注册发现”，使用“应用级别注册发现”。&lt;/p>
&lt;p>简单地说，接口级别注册发现，在注册中心中以 RPC 服务为 key，以实例列表作为 value 来组织数据的，而我们新引入的“应用粒度的服务发现”，它以应用名（Application）作为 key，以这个应用部署的一组实例（Instance）列表作为 value。这带来两点不同：&lt;/p>
&lt;ul>
&lt;li>数据映射关系变了，从 RPC Service -&amp;gt; Instance 变为 Application -&amp;gt; Instance；&lt;/li>
&lt;li>数据变少了，注册中心没有了 RPC Service 及其相关配置信息。&lt;/li>
&lt;/ul>
&lt;p>可以认为，基于应用粒度的模型所存储和推送的数据量是和应用、实例数成正比的，只有当我们的应用数增多或应用的实例数增长时，地址推送压力才会上涨。&lt;/p>
&lt;p>而对于基于接口粒度的模型，数据量是和接口数量正相关的，鉴于一个应用通常发布多个接口的现状，其数量级一般是比应用粒度的数十倍。另外一个关键点在于，接口的定义更多的是业务侧的内部行为，接口粒度导致的集群规模评估的不透明，而实例、应用增长都通常是在运维侧的规划之中，可控性较好。&lt;/p>
&lt;p>工商银行曾经对这两个模型进行生产测算：应用级服务注册模型可以让注册中心上的数据量变成原来的 1.68%，新模型可以让 zookeeper 轻松支撑 10 万级别的服务量和 10 万级别的节点量。&lt;/p>
&lt;h4 id="2-元数据中心同步机制的引入">2. 元数据中心同步机制的引入&lt;/h4>
&lt;p>数据中心的数据量变少所造成的结果，是 RPC 服务相关的数据在注册中心消失了，只有 application - instance 这两个层级的数据。为了保证这部分缺少的 RPC 服务数据仍然能被 Consumer 端正确的感知，我们在 Consumer 和 Provider 间建立了一条单独的通信通道，目前针对元数据同步有两种具体的可选方案，分别是：&lt;/p>
&lt;ul>
&lt;li>内建 MetadataService；&lt;/li>
&lt;li>独立的元数据中心，通过中细化的元数据集群协调数据。&lt;/li>
&lt;/ul>
&lt;h4 id="3-兼容旧版本-dubbo-go">3. 兼容旧版本 dubbo-go&lt;/h4>
&lt;p>为了使整个开发流程对老的 dubbo-go 用户更透明，同时避免指定 provider 对可扩展性带来的影响），我们设计了一套 RPC服务到应用名的映射关系，以尝试在 consumer 自动完成 RPC 服务到 provider 应用名的转换。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/3.0-plan/p5.webp" alt="">&lt;/p>
&lt;p>这套设计可以让 dubbogo 3.0 中同时保持对 dubbo v2.6.x、dubbo v2.7.x 和 dubbo v3.0.x 三个大版的互联互通。&lt;/p>
&lt;h2 id="统一路由的支持">统一路由的支持&lt;/h2>
&lt;p>路由在概念上可以理解为从已有的所有 IP 地址列表中，根据特定的路由规则，挑选出需要的 ip 地址子集。路由的过程需要根据配置好的路由规则进行筛选，最终取所有路由规则的交集获得结果。多个路由如同流水线一样，形成一条路由链，从所有的地址表中筛选出最终目的地址集合，再通过负载均衡策略选择访问的地址。&lt;/p>
&lt;h4 id="1-路由链">1. 路由链&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/3.0-plan/p6.webp" alt="">&lt;/p>
&lt;p>可以把路由链的逻辑简单理解为 target = rn(&amp;hellip;r3(r2(r1(src))))。对于每一个 router 内部的逻辑，可以抽象为输入地址 addrs-in 与 router 中按全量地址 addrs-all 实现切分好的 n 个&lt;strong>互不相交&lt;/strong>的地址池 addrs-pool-1 &amp;hellip; addrs-pool-n 按实现定义好的规则取交集作为输出地址。以此类推，完成整个路由链的计算。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/3.0-plan/p7.webp" alt="">&lt;/p>
&lt;h4 id="2-failover">2. failover&lt;/h4>
&lt;p>在路由规则配置文件中可以配置 failover 字段。在寻找地址失败时可以 failover， 选择其他 subset，并且顺序执行下来，直到找到地址，否则最后报地址找不到异常。&lt;/p>
&lt;h4 id="3-兜底路由">3. 兜底路由&lt;/h4>
&lt;p>在的路由规则配置中，可以配置一个没有任何条件的 match, 最终的结果是至少会有一个 subset 被选到，以达到地址空保护的作用。&lt;/p>
&lt;p>作为 2020 年 dubbogo 社区打造并将在 2021 年初亮出的的三支箭之一，dubbogo 3.0 将带来不同平常且焕然一新的开发体验，敬请广大开发者期待！&lt;/p>
&lt;p>如果你有任何疑问，欢迎钉钉扫码加入交流群【钉钉群号 31363295】：&lt;/p>
&lt;p>dubbogo 3.0 目前是社区和 dubbo 官方团队&amp;ndash; 阿里中间件团队共同合作开发。&lt;/p>
&lt;p>阿里云-中间件团队招募对 dubbo3 (java &amp;amp; go)、dapr、arthas 有兴趣的开发者。可以钉钉联系 northlatitude，或者发送邮件至 &lt;a href="mailto:beiwei.ly@alibaba-inc.com">beiwei.ly@alibaba-inc.com&lt;/a>。&lt;/p>
&lt;blockquote>
&lt;p>作者简介&lt;/p>
&lt;p>&lt;strong>李志信&lt;/strong> (GitHubID LaurenceLiZhixin)，阿里云云原生中间件团队开发工程师，dubbogo 社区开发者，中山大学软件工程专业在校学生，擅长使用 Go 语言，专注于云原生和微服务等技术方向。&lt;/p>
&lt;p>&lt;strong>于雨&lt;/strong>(github @AlexStocks)，dubbo-go 项目和社区负责人，一个有十多年服务端做着基础架构研发一线工作经验的程序员，陆续参与改进过 Muduo/Pika/Dubbo/Sentinel-go 等知名项目，目前在蚂蚁金服可信原生部从事容器编排和 service mesh 工作。&lt;/p>
&lt;/blockquote></description></item><item><title>Blog: 分布式事务框架 seata-golang 通信模型详解</title><link>https://cn.dubbo.apache.org/zh/blog/2021/01/15/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A1%86%E6%9E%B6-seata-golang-%E9%80%9A%E4%BF%A1%E6%A8%A1%E5%9E%8B%E8%AF%A6%E8%A7%A3/</link><pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/01/15/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A1%86%E6%9E%B6-seata-golang-%E9%80%9A%E4%BF%A1%E6%A8%A1%E5%9E%8B%E8%AF%A6%E8%A7%A3/</guid><description>
&lt;h2 id="简介">简介&lt;/h2>
&lt;p>Java 的世界里，大家广泛使用一个高性能网络通信框架 —— netty，很多 RPC 框架都是基于 netty 来实现的。在 golang 的世界里，getty 也是一个类似 netty 的高性能网络通信库。getty 最初由 dubbo-go 项目负责人于雨开发，作为底层通信库在 dubbo-go 中使用。随着 dubbo-go 捐献给 apache 基金会，在社区小伙伴的共同努力下，getty 也最终进入到 apache 这个大家庭，并改名 dubbo-getty。&lt;/p>
&lt;p>18 年的时候，我在公司里实践微服务，当时遇到最大的问题就是分布式事务问题。同年，阿里在社区开源他们的分布式事务解决方案，我也很快关注到这个项目，起初还叫 fescar，后来更名 seata。由于我对开源技术很感兴趣，加了很多社区群，当时也很关注 dubbo-go 这个项目，在里面默默潜水。随着对 seata 的了解，逐渐萌生了做一个 go 版本的分布式事务框架的想法。&lt;/p>
&lt;p>要做一个 golang 版的分布式事务框架，首先需要解决的一个问题就是如何实现 RPC 通信。dubbo-go 就是摆在眼前很好的一个例子，遂开始研究 dubbo-go 的底层 getty。&lt;/p>
&lt;h2 id="如何基于-getty-实现-rpc-通信">如何基于 getty 实现 RPC 通信&lt;/h2>
&lt;p>getty 框架的整体模型图如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/seata/p1.webp" alt="">&lt;/p>
&lt;p>下面结合相关代码，详述 seata-golang 的 RPC 通信过程。&lt;/p>
&lt;h3 id="1-建立连接">1. 建立连接&lt;/h3>
&lt;p>实现 RPC 通信，首先要建立网络连接，这里先从 client.go 开始看起。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (c &lt;span style="color:#719e07">*&lt;/span>client) &lt;span style="color:#268bd2">connect&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">var&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ss Session
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 建立一个 session 连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ss = c.&lt;span style="color:#268bd2">dial&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ss &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// client has been closed
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = c.&lt;span style="color:#268bd2">newSession&lt;/span>(ss)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 收发报文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ss.(&lt;span style="color:#719e07">*&lt;/span>session).&lt;span style="color:#268bd2">run&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 此处省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// don&amp;#39;t distinguish between tcp connection and websocket connection. Because
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// gorilla/websocket/conn.go:(Conn)Close also invoke net.Conn.Close()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ss.&lt;span style="color:#268bd2">Conn&lt;/span>().&lt;span style="color:#268bd2">Close&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>connect()&lt;/code> 方法通过 &lt;code>dial()&lt;/code> 方法得到了一个 session 连接，进入 &lt;code>dial()&lt;/code> 方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (c &lt;span style="color:#719e07">*&lt;/span>client) &lt;span style="color:#268bd2">dial&lt;/span>() Session {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">switch&lt;/span> c.endPointType {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> TCP_CLIENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> c.&lt;span style="color:#268bd2">dialTCP&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> UDP_CLIENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> c.&lt;span style="color:#268bd2">dialUDP&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> WS_CLIENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> c.&lt;span style="color:#268bd2">dialWS&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> WSS_CLIENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> c.&lt;span style="color:#268bd2">dialWSS&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们关注的是 TCP 连接，所以继续进入 &lt;code>c.dialTCP()&lt;/code> 方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (c &lt;span style="color:#719e07">*&lt;/span>client) &lt;span style="color:#268bd2">dialTCP&lt;/span>() Session {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">var&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> conn net.Conn
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> c.&lt;span style="color:#268bd2">IsClosed&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> c.sslEnabled {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> sslConfig, err &lt;span style="color:#719e07">:=&lt;/span> c.tlsConfigBuilder.&lt;span style="color:#268bd2">BuildTlsConfig&lt;/span>(); err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> sslConfig &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> d &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>net.Dialer{Timeout: connectTimeout}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 建立加密连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> conn, err = tls.&lt;span style="color:#268bd2">DialWithDialer&lt;/span>(d, &lt;span style="color:#2aa198">&amp;#34;tcp&amp;#34;&lt;/span>, c.addr, sslConfig)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 建立 tcp 连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> conn, err = net.&lt;span style="color:#268bd2">DialTimeout&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;tcp&amp;#34;&lt;/span>, c.addr, connectTimeout)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> gxnet.&lt;span style="color:#268bd2">IsSameAddr&lt;/span>(conn.&lt;span style="color:#268bd2">RemoteAddr&lt;/span>(), conn.&lt;span style="color:#268bd2">LocalAddr&lt;/span>()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> conn.&lt;span style="color:#268bd2">Close&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = errSelfConnect
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 返回一个 TCPSession
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#268bd2">newTCPSession&lt;/span>(conn, c)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;net.DialTimeout(addr:%s, timeout:%v) = error:%+v&amp;#34;&lt;/span>, c.addr, connectTimeout, perrors.&lt;span style="color:#268bd2">WithStack&lt;/span>(err))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;-&lt;/span>wheel.&lt;span style="color:#268bd2">After&lt;/span>(connectInterval)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>至此，我们知道了 getty 如何建立 TCP 连接，并返回 TCPSession。&lt;/p>
&lt;h3 id="2-收发报文">2. 收发报文&lt;/h3>
&lt;p>那它是怎么收发报文的呢，我们回到 connection 方法接着往下看，有这样一行 &lt;code>ss.(*session).run()&lt;/code>，在这行代码之后，代码都是很简单的操作，我们猜测这行代码运行的逻辑里面一定包含收发报文的逻辑，接着进入 &lt;code>run()&lt;/code> 方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">run&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">go&lt;/span> s.&lt;span style="color:#268bd2">handleLoop&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">go&lt;/span> s.&lt;span style="color:#268bd2">handlePackage&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里起了两个 goroutine：&lt;code>handleLoop&lt;/code> 和 &lt;code>handlePackage&lt;/code>，看字面意思符合我们的猜想，进入 &lt;code>handleLoop()&lt;/code> 方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">handleLoop&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// A select blocks until one of its cases is ready to run.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// It choose one at random if multiple are ready. Otherwise it choose default branch if none is ready.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> outPkg, ok = &lt;span style="color:#719e07">&amp;lt;-&lt;/span>s.wQ:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> iovec = iovec[:&lt;span style="color:#2aa198">0&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> idx &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>; idx &amp;lt; maxIovecNum; idx&lt;span style="color:#719e07">++&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 通过 s.writer 将 interface{} 类型的 outPkg 编码成二进制的比特
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> pkgBytes, err = s.writer.&lt;span style="color:#268bd2">Write&lt;/span>(s, outPkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> iovec = &lt;span style="color:#b58900">append&lt;/span>(iovec, pkgBytes)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将这些二进制比特发送出去
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> err = s.&lt;span style="color:#268bd2">WriteBytesArray&lt;/span>(iovec[:]&lt;span style="color:#719e07">...&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;%s, [session.handleLoop]s.WriteBytesArray(iovec len:%d) = error:%+v&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.&lt;span style="color:#268bd2">sessionToken&lt;/span>(), &lt;span style="color:#b58900">len&lt;/span>(iovec), perrors.&lt;span style="color:#268bd2">WithStack&lt;/span>(err))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.&lt;span style="color:#268bd2">stop&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// break LOOP
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> flag = &lt;span style="color:#cb4b16">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> &lt;span style="color:#719e07">&amp;lt;-&lt;/span>wheel.&lt;span style="color:#268bd2">After&lt;/span>(s.period):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> flag {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> wsFlag {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#719e07">:=&lt;/span> wsConn.&lt;span style="color:#268bd2">writePing&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Warnf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;wsConn.writePing() = error:%+v&amp;#34;&lt;/span>, perrors.&lt;span style="color:#268bd2">WithStack&lt;/span>(err))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 定时执行的逻辑，心跳等
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> s.listener.&lt;span style="color:#268bd2">OnCron&lt;/span>(s)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过上面的代码，我们不难发现，&lt;code>handleLoop()&lt;/code> 方法处理的是发送报文的逻辑，RPC 需要发送的消息首先由 &lt;code>s.writer&lt;/code> 编码成二进制比特，然后通过建立的 TCP 连接发送出去。这个 &lt;code>s.writer&lt;/code> 对应的 Writer 接口是 RPC 框架必须要实现的一个接口。&lt;/p>
&lt;p>继续看 &lt;code>handlePackage()&lt;/code> 方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">handlePackage&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> _, ok &lt;span style="color:#719e07">:=&lt;/span> s.Connection.(&lt;span style="color:#719e07">*&lt;/span>gettyTCPConn); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> s.reader &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> errStr &lt;span style="color:#719e07">:=&lt;/span> fmt.&lt;span style="color:#268bd2">Sprintf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;session{name:%s, conn:%#v, reader:%#v}&amp;#34;&lt;/span>, s.name, s.Connection, s.reader)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Error&lt;/span>(errStr)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(errStr)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = s.&lt;span style="color:#268bd2">handleTCPPackage&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> _, ok &lt;span style="color:#719e07">:=&lt;/span> s.Connection.(&lt;span style="color:#719e07">*&lt;/span>gettyWSConn); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = s.&lt;span style="color:#268bd2">handleWSPackage&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> _, ok &lt;span style="color:#719e07">:=&lt;/span> s.Connection.(&lt;span style="color:#719e07">*&lt;/span>gettyUDPConn); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = s.&lt;span style="color:#268bd2">handleUDPPackage&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(fmt.&lt;span style="color:#268bd2">Sprintf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;unknown type session{%#v}&amp;#34;&lt;/span>, s))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>进入 &lt;code>handleTCPPackage()&lt;/code> 方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">handleTCPPackage&lt;/span>() &lt;span style="color:#dc322f">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> conn = s.Connection.(&lt;span style="color:#719e07">*&lt;/span>gettyTCPConn)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bufLen = &lt;span style="color:#2aa198">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// for clause for the network timeout condition check
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// s.conn.SetReadTimeout(time.Now().Add(s.rTimeout))
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 从 TCP 连接中收到报文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> bufLen, err = conn.&lt;span style="color:#268bd2">recv&lt;/span>(buf)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将收到的报文二进制比特写入 pkgBuf
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> pktBuf.&lt;span style="color:#268bd2">Write&lt;/span>(buf[:bufLen])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> pktBuf.&lt;span style="color:#268bd2">Len&lt;/span>() &lt;span style="color:#719e07">&amp;lt;=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 通过 s.reader 将收到的报文解码成 RPC 消息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> pkg, pkgLen, err = s.reader.&lt;span style="color:#268bd2">Read&lt;/span>(s, pktBuf.&lt;span style="color:#268bd2">Bytes&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.&lt;span style="color:#268bd2">UpdateActive&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将收到的消息放入 TaskQueue 供 RPC 消费端消费
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> s.&lt;span style="color:#268bd2">addTask&lt;/span>(pkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pktBuf.&lt;span style="color:#268bd2">Next&lt;/span>(pkgLen)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// continue to handle case 5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> exit {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> perrors.&lt;span style="color:#268bd2">WithStack&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>从上面的代码逻辑我们分析出，RPC 消费端需要将从 TCP 连接收到的二进制比特报文解码成 RPC 能消费的消息，这个工作由 s.reader 实现，所以，我们要构建 RPC 通信层也需要实现 s.reader 对应的 Reader 接口。&lt;/p>
&lt;h3 id="3-底层处理网络报文的逻辑如何与业务逻辑解耦">3. 底层处理网络报文的逻辑如何与业务逻辑解耦&lt;/h3>
&lt;p>我们都知道，netty 通过 boss 线程和 worker 线程实现了底层网络逻辑和业务逻辑的解耦。那么，getty 是如何实现的呢？&lt;/p>
&lt;p>在 &lt;code>handlePackage()&lt;/code> 方法最后，我们看到，收到的消息被放入了 &lt;code>s.addTask(pkg)&lt;/code> 这个方法，接着往下分析：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (s &lt;span style="color:#719e07">*&lt;/span>session) &lt;span style="color:#268bd2">addTask&lt;/span>(pkg &lt;span style="color:#268bd2">interface&lt;/span>{}) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> f &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">func&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.listener.&lt;span style="color:#268bd2">OnMessage&lt;/span>(s, pkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s.&lt;span style="color:#268bd2">incReadPkgNum&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> taskPool &lt;span style="color:#719e07">:=&lt;/span> s.&lt;span style="color:#268bd2">EndPoint&lt;/span>().&lt;span style="color:#268bd2">GetTaskPool&lt;/span>(); taskPool &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> taskPool.&lt;span style="color:#268bd2">AddTaskAlways&lt;/span>(f)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">f&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>pkg&lt;/code> 参数传递到了一个匿名方法，这个方法最终放入了 &lt;code>taskPool&lt;/code>。这个方法很关键，在我后来写 seata-golang 代码的时候，就遇到了一个坑，这个坑后面分析。&lt;/p>
&lt;p>接着我们看一下 taskPool 的定义：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// NewTaskPoolSimple build a simple task pool
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">NewTaskPoolSimple&lt;/span>(size &lt;span style="color:#dc322f">int&lt;/span>) GenericTaskPool {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> size &amp;lt; &lt;span style="color:#2aa198">1&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> size = runtime.&lt;span style="color:#268bd2">NumCPU&lt;/span>() &lt;span style="color:#719e07">*&lt;/span> &lt;span style="color:#2aa198">100&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>taskPoolSimple{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> work: &lt;span style="color:#b58900">make&lt;/span>(&lt;span style="color:#268bd2">chan&lt;/span> task),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sem: &lt;span style="color:#b58900">make&lt;/span>(&lt;span style="color:#268bd2">chan&lt;/span> &lt;span style="color:#268bd2">struct&lt;/span>{}, size),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> done: &lt;span style="color:#b58900">make&lt;/span>(&lt;span style="color:#268bd2">chan&lt;/span> &lt;span style="color:#268bd2">struct&lt;/span>{}),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>构建了一个缓冲大小为 size （默认为 &lt;code>runtime.NumCPU() * 100&lt;/code>） 的 channel &lt;code>sem&lt;/code>。再看方法 &lt;code>AddTaskAlways(t task)&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (p &lt;span style="color:#719e07">*&lt;/span>taskPoolSimple) &lt;span style="color:#268bd2">AddTaskAlways&lt;/span>(t task) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> &lt;span style="color:#719e07">&amp;lt;-&lt;/span>p.done:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> p.work &lt;span style="color:#719e07">&amp;lt;-&lt;/span> t:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> p.work &lt;span style="color:#719e07">&amp;lt;-&lt;/span> t:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> p.sem &lt;span style="color:#719e07">&amp;lt;-&lt;/span> &lt;span style="color:#268bd2">struct&lt;/span>{}{}:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> p.wg.&lt;span style="color:#268bd2">Add&lt;/span>(&lt;span style="color:#2aa198">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">go&lt;/span> p.&lt;span style="color:#268bd2">worker&lt;/span>(t)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">goSafely&lt;/span>(t)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>加入的任务，会先由 len(p.sem) 个 goroutine 去消费，如果没有 goroutine 空闲，则会启动一个临时的 goroutine 去运行 t()。相当于有 len(p.sem) 个 goroutine 组成了 goroutine pool，pool 中的 goroutine 去处理业务逻辑，而不是由处理网络报文的 goroutine 去运行业务逻辑，从而实现了解耦。写 seata-golang 时遇到的一个坑，就是忘记设置 taskPool 造成了处理业务逻辑和处理底层网络报文逻辑的 goroutine 是同一个，我在业务逻辑中阻塞等待一个任务完成时，阻塞了整个 goroutine，使得阻塞期间收不到任何报文。&lt;/p>
&lt;h3 id="4-具体实现">4. 具体实现&lt;/h3>
&lt;p>下面的代码见getty.go ：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Reader is used to unmarshal a complete pkg from buffer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> Reader &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">Read&lt;/span>(Session, []&lt;span style="color:#dc322f">byte&lt;/span>) (&lt;span style="color:#268bd2">interface&lt;/span>{}, &lt;span style="color:#dc322f">int&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Writer is used to marshal pkg and write to session
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> Writer &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if @Session is udpGettySession, the second parameter is UDPContext.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Write&lt;/span>(Session, &lt;span style="color:#268bd2">interface&lt;/span>{}) ([]&lt;span style="color:#dc322f">byte&lt;/span>, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ReadWriter interface use for handle application packages
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> ReadWriter &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Reader
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Writer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// EventListener is used to process pkg that received from remote session
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> EventListener &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked when session opened
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// If the return error is not nil, @Session will be closed.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnOpen&lt;/span>(Session) &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked when session closed.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnClose&lt;/span>(Session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked when got error.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnError&lt;/span>(Session, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked periodically, its period can be set by (Session)SetCronPeriod
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnCron&lt;/span>(Session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoked when getty received a package. Pls attention that do not handle long time
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// logic processing in this func. You&amp;#39;d better set the package&amp;#39;s maximum length.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// If the message&amp;#39;s length is greater than it, u should should return err in
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// Reader{Read} and getty will close this connection soon.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// If ur logic processing in this func will take a long time, u should start a goroutine
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// pool(like working thread pool in cpp) to handle the processing asynchronously. Or u
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// can do the logic processing in other asynchronous way.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// !!!In short, ur OnMessage callback func should return asap.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// If this is a udp event listener, the second parameter type is UDPContext.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">OnMessage&lt;/span>(Session, &lt;span style="color:#268bd2">interface&lt;/span>{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过对整个 getty 代码的分析，我们只要实现 &lt;code>ReadWriter&lt;/code> 来对 RPC 消息编解码，再实现 &lt;code>EventListener&lt;/code> 来处理 RPC 消息的对应的具体逻辑，将 &lt;code>ReadWriter&lt;/code> 实现和 &lt;code>EventLister&lt;/code> 实现注入到 RPC 的 Client 和 Server 端，则可实现 RPC 通信。&lt;/p>
&lt;h3 id="1编解码协议实现">1）编解码协议实现&lt;/h3>
&lt;p>下面是 seata 协议的定义：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/seata/p2.webp" alt="">&lt;/p>
&lt;p>在 ReadWriter 接口的实现 &lt;code>RpcPackageHandler&lt;/code> 中，调用 Codec 方法对消息体按照上面的格式编解码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>// 消息编码为二进制比特
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>func MessageEncoder(codecType byte, in interface{}) []byte {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> switch codecType {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> case SEATA:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return SeataEncoder(in)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> default:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.Errorf(&amp;#34;not support codecType, %s&amp;#34;, codecType)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return nil
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 二进制比特解码为消息体
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>func MessageDecoder(codecType byte, in []byte) (interface{}, int) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> switch codecType {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> case SEATA:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return SeataDecoder(in)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> default:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.Errorf(&amp;#34;not support codecType, %s&amp;#34;, codecType)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return nil, 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2client-端实现">2）Client 端实现&lt;/h3>
&lt;p>再来看 client 端 &lt;code>EventListener&lt;/code> 的实现 &lt;code>RpcRemotingClient&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnOpen&lt;/span>(session getty.Session) &lt;span style="color:#dc322f">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">go&lt;/span> &lt;span style="color:#268bd2">func&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> request &lt;span style="color:#719e07">:=&lt;/span> protocal.RegisterTMRequest{AbstractIdentifyRequest: protocal.AbstractIdentifyRequest{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationId: client.conf.ApplicationId,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TransactionServiceGroup: client.conf.TransactionServiceGroup,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 建立连接后向 Transaction Coordinator 发起注册 TransactionManager 的请求
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> _, err &lt;span style="color:#719e07">:=&lt;/span> client.&lt;span style="color:#268bd2">sendAsyncRequestWithResponse&lt;/span>(session, request, RPC_REQUEST_TIMEOUT)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将与 Transaction Coordinator 建立的连接保存在连接池供后续使用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> clientSessionManager.&lt;span style="color:#268bd2">RegisterGettySession&lt;/span>(session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client.GettySessionOnOpenChannel &lt;span style="color:#719e07">&amp;lt;-&lt;/span> session.&lt;span style="color:#268bd2">RemoteAddr&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OnError ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnError&lt;/span>(session getty.Session, err &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clientSessionManager.&lt;span style="color:#268bd2">ReleaseGettySession&lt;/span>(session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OnClose ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnClose&lt;/span>(session getty.Session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clientSessionManager.&lt;span style="color:#268bd2">ReleaseGettySession&lt;/span>(session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OnMessage ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnMessage&lt;/span>(session getty.Session, pkg &lt;span style="color:#268bd2">interface&lt;/span>{}) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Info&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;received message:{%v}&amp;#34;&lt;/span>, pkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcMessage, ok &lt;span style="color:#719e07">:=&lt;/span> pkg.(protocal.RpcMessage)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> heartBeat, isHeartBeat &lt;span style="color:#719e07">:=&lt;/span> rpcMessage.Body.(protocal.HeartBeatMessage)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> isHeartBeat &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> heartBeat &lt;span style="color:#719e07">==&lt;/span> protocal.HeartBeatMessagePong {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Debugf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;received PONG from %s&amp;#34;&lt;/span>, session.&lt;span style="color:#268bd2">RemoteAddr&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> rpcMessage.MessageType &lt;span style="color:#719e07">==&lt;/span> protocal.MSGTYPE_RESQUEST &lt;span style="color:#719e07">||&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcMessage.MessageType &lt;span style="color:#719e07">==&lt;/span> protocal.MSGTYPE_RESQUEST_ONEWAY {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Debugf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;msgId:%s, body:%v&amp;#34;&lt;/span>, rpcMessage.Id, rpcMessage.Body)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 处理事务消息，提交 or 回滚
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> client.&lt;span style="color:#268bd2">onMessage&lt;/span>(rpcMessage, session.&lt;span style="color:#268bd2">RemoteAddr&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp, loaded &lt;span style="color:#719e07">:=&lt;/span> client.futures.&lt;span style="color:#268bd2">Load&lt;/span>(rpcMessage.Id)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> loaded {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response &lt;span style="color:#719e07">:=&lt;/span> resp.(&lt;span style="color:#719e07">*&lt;/span>getty2.MessageFuture)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.Response = rpcMessage.Body
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.Done &lt;span style="color:#719e07">&amp;lt;-&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client.futures.&lt;span style="color:#268bd2">Delete&lt;/span>(rpcMessage.Id)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OnCron ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (client &lt;span style="color:#719e07">*&lt;/span>RpcRemoteClient) &lt;span style="color:#268bd2">OnCron&lt;/span>(session getty.Session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 发送心跳
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> client.&lt;span style="color:#268bd2">defaultSendRequest&lt;/span>(session, protocal.HeartBeatMessagePing)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>clientSessionManager.RegisterGettySession(session)&lt;/code> 的逻辑将在下文中分析。&lt;/p>
&lt;h3 id="3server-端-transaction-coordinator-实现">3）Server 端 Transaction Coordinator 实现&lt;/h3>
&lt;p>代码见 &lt;code>DefaultCoordinator&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnOpen&lt;/span>(session getty.Session) &lt;span style="color:#dc322f">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;got getty_session:%s&amp;#34;&lt;/span>, session.&lt;span style="color:#268bd2">Stat&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnError&lt;/span>(session getty.Session, err &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 释放 TCP 连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> SessionManager.&lt;span style="color:#268bd2">ReleaseGettySession&lt;/span>(session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#268bd2">Close&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;getty_session{%s} got error{%v}, will be closed.&amp;#34;&lt;/span>, session.&lt;span style="color:#268bd2">Stat&lt;/span>(), err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnClose&lt;/span>(session getty.Session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Info&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;getty_session{%s} is closing......&amp;#34;&lt;/span>, session.&lt;span style="color:#268bd2">Stat&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnMessage&lt;/span>(session getty.Session, pkg &lt;span style="color:#268bd2">interface&lt;/span>{}) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Debugf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;received message:{%v}&amp;#34;&lt;/span>, pkg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcMessage, ok &lt;span style="color:#719e07">:=&lt;/span> pkg.(protocal.RpcMessage)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _, isRegTM &lt;span style="color:#719e07">:=&lt;/span> rpcMessage.Body.(protocal.RegisterTMRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> isRegTM {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将 TransactionManager 信息和 TCP 连接建立映射关系
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> coordinator.&lt;span style="color:#268bd2">OnRegTmMessage&lt;/span>(rpcMessage, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> heartBeat, isHeartBeat &lt;span style="color:#719e07">:=&lt;/span> rpcMessage.Body.(protocal.HeartBeatMessage)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> isHeartBeat &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> heartBeat &lt;span style="color:#719e07">==&lt;/span> protocal.HeartBeatMessagePing {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> coordinator.&lt;span style="color:#268bd2">OnCheckMessage&lt;/span>(rpcMessage, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> rpcMessage.MessageType &lt;span style="color:#719e07">==&lt;/span> protocal.MSGTYPE_RESQUEST &lt;span style="color:#719e07">||&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcMessage.MessageType &lt;span style="color:#719e07">==&lt;/span> protocal.MSGTYPE_RESQUEST_ONEWAY {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Debugf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;msgId:%s, body:%v&amp;#34;&lt;/span>, rpcMessage.Id, rpcMessage.Body)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _, isRegRM &lt;span style="color:#719e07">:=&lt;/span> rpcMessage.Body.(protocal.RegisterRMRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> isRegRM {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 将 ResourceManager 信息和 TCP 连接建立映射关系
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> coordinator.&lt;span style="color:#268bd2">OnRegRmMessage&lt;/span>(rpcMessage, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> SessionManager.&lt;span style="color:#268bd2">IsRegistered&lt;/span>(session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">defer&lt;/span> &lt;span style="color:#268bd2">func&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">recover&lt;/span>(); err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;Catch Exception while do RPC, request: %v,err: %w&amp;#34;&lt;/span>, rpcMessage, err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 处理事务消息，全局事务注册、分支事务注册、分支事务提交、全局事务回滚等
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> coordinator.&lt;span style="color:#268bd2">OnTrxMessage&lt;/span>(rpcMessage, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#268bd2">Close&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;close a unhandled connection! [%v]&amp;#34;&lt;/span>, session)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp, loaded &lt;span style="color:#719e07">:=&lt;/span> coordinator.futures.&lt;span style="color:#268bd2">Load&lt;/span>(rpcMessage.Id)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> loaded {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response &lt;span style="color:#719e07">:=&lt;/span> resp.(&lt;span style="color:#719e07">*&lt;/span>getty2.MessageFuture)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.Response = rpcMessage.Body
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.Done &lt;span style="color:#719e07">&amp;lt;-&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> coordinator.futures.&lt;span style="color:#268bd2">Delete&lt;/span>(rpcMessage.Id)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (coordinator &lt;span style="color:#719e07">*&lt;/span>DefaultCoordinator) &lt;span style="color:#268bd2">OnCron&lt;/span>(session getty.Session) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>coordinator.OnRegTmMessage(rpcMessage, session)&lt;/code> 注册 Transaction Manager，&lt;code>coordinator.OnRegRmMessage(rpcMessage, session)&lt;/code> 注册 Resource Manager。具体逻辑分析见下文。&lt;/p>
&lt;p>消息进入 &lt;code>coordinator.OnTrxMessage(rpcMessage, session)&lt;/code> 方法，将按照消息的类型码路由到具体的逻辑当中：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">switch&lt;/span> msg.&lt;span style="color:#268bd2">GetTypeCode&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalBegin:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalBeginRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalBegin&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalStatus:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalStatusRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalStatus&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalReport:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalReportRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalReport&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalCommit:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalCommitRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalCommit&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeGlobalRollback:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.GlobalRollbackRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doGlobalRollback&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeBranchRegister:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.BranchRegisterRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doBranchRegister&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> protocal.TypeBranchStatusReport:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#719e07">:=&lt;/span> msg.(protocal.BranchReportRequest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resp &lt;span style="color:#719e07">:=&lt;/span> coordinator.&lt;span style="color:#268bd2">doBranchReport&lt;/span>(req, ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4session-manager-分析">4）session manager 分析&lt;/h3>
&lt;p>Client 端同 Transaction Coordinator 建立连接起连接后，通过 &lt;code>clientSessionManager.RegisterGettySession(session)&lt;/code> 将连接保存在 &lt;code>serverSessions = sync.Map{}&lt;/code> 这个 map 中。map 的 key 为从 session 中获取的 RemoteAddress 即 Transaction Coordinator 的地址，value 为 session。这样，Client 端就可以通过 map 中的一个 session 来向 Transaction Coordinator 注册 Transaction Manager 和 Resource Manager 了。具体代码见 &lt;code>getty_client_session_manager.go&lt;/code>。&lt;/p>
&lt;p>Transaction Manager 和 Resource Manager 注册到 Transaction Coordinator 后，一个连接既有可能用来发送 TM 消息也有可能用来发送 RM 消息。我们通过 RpcContext 来标识一个连接信息：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> RpcContext &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Version &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TransactionServiceGroup &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClientRole meta.TransactionRole
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationId &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClientId &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ResourceSets &lt;span style="color:#719e07">*&lt;/span>model.Set
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Session getty.Session
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当收到事务消息时，我们需要构造这样一个 RpcContext 供后续事务处理逻辑使用。所以，我们会构造下列 map 来缓存映射关系：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">var&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// session -&amp;gt; transactionRole
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// TM will register before RM, if a session is not the TM registered,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// it will be the RM registered
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> session_transactionroles = sync.Map{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// session -&amp;gt; applicationId
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> identified_sessions = sync.Map{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// applicationId -&amp;gt; ip -&amp;gt; port -&amp;gt; session
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> client_sessions = sync.Map{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// applicationId -&amp;gt; resourceIds
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> client_resources = sync.Map{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样，Transaction Manager 和 Resource Manager 分别通过 &lt;code>coordinator.OnRegTmMessage(rpcMessage, session)&lt;/code> 和 &lt;code>coordinator.OnRegRmMessage(rpcMessage, session)&lt;/code> 注册到 Transaction Coordinator 时，会在上述 client_sessions map 中缓存 applicationId、ip、port 与 session 的关系，在 client_resources map 中缓存 applicationId 与 resourceIds（一个应用可能存在多个 Resource Manager） 的关系。&lt;/p>
&lt;p>在需要时，我们就可以通过上述映射关系构造一个 RpcContext。这部分的实现和 java 版 seata 有很大的不同，感兴趣的可以深入了解一下。具体代码见 &lt;code>getty_session_manager.go&lt;/code>。&lt;/p>
&lt;p>至此，我们就分析完了 seata-golang 整个 RPC 通信模型的机制。&lt;/p>
&lt;h3 id="seata-golang-的未来">seata-golang 的未来&lt;/h3>
&lt;p>seata-golang 从今年 4 月份开始开发，到 8 月份基本实现和 java 版 seata 1.2 协议的互通，对 mysql 数据库实现了 AT 模式（自动协调分布式事务的提交回滚），实现了 TCC 模式，TC 端使用 mysql 存储数据，使 TC 变成一个无状态应用支持高可用部署。下图展示了 AT 模式的原理：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/seata/p3.webp" alt="">&lt;/p>
&lt;p>后续，还有许多工作可以做，比如：对注册中心的支持、对配置中心的支持、和 java 版 seata 1.4 的协议互通、其他数据库的支持、raft transaction coordinator 的实现等，希望对分布式事务问题感兴趣的开发者可以加入进来一起来打造一个完善的 golang 的分布式事务框架。如果你有任何疑问，欢迎钉钉扫码加入交流群【钉钉群号 33069364】：&lt;/p>
&lt;p>另外，欢迎对 dubbogo 感兴趣的朋友到 dubbogo 社区钉钉群（钉钉群号 31363295）沟通 dubbogo 技术问题。&lt;/p>
&lt;h3 id="参考资料">参考资料&lt;/h3>
&lt;ul>
&lt;li>seata 官方: &lt;a href="https://seata.io">https://seata.io&lt;/a>&lt;/li>
&lt;li>java 版 seata: &lt;a href="https://github.com/seata/seata">https://github.com/seata/seata&lt;/a>&lt;/li>
&lt;li>seata-golang 项目地址: &lt;a href="https://github.com/opentrx/seata-golang">https://github.com/opentrx/seata-golang&lt;/a>&lt;/li>
&lt;li>seata-golang go 夜读 b 站分享: &lt;a href="https://www.bilibili.com/video/BV1oz411e72T">https://www.bilibili.com/video/BV1oz411e72T&lt;/a>&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>作者简介&lt;/p>
&lt;p>&lt;strong>刘晓敏&lt;/strong> (GitHubID dk-lockdown)，目前就职于 h3c 成都分公司，擅长使用 Java/Go 语言，在云原生和微服务相关技术方向均有涉猎，目前专攻分布式事务。
&lt;strong>于雨&lt;/strong>(（github @AlexStocks），dubbo-go 项目和社区负责人，一个有十多年服务端基础架构研发一线工作经验的程序员，陆续参与改进过 Muduo/Pika/Dubbo/Sentinel-go 等知名项目，目前在蚂蚁金服可信原生部从事容器编排和 service mesh 工作。&lt;/p>
&lt;/blockquote></description></item><item><title>Blog: dubbo-go 1.5.0</title><link>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-1.5.0/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-1.5.0/</guid><description>
&lt;h2 id="引语">引语&lt;/h2>
&lt;p>计算机技术浪潮每 10 年都有一次技术颠覆，相关知识体系最迟每 5 年都会革新一次，大概每两年贬值一半，在应用服务通信框架领域亦然。凡是有长期生命的通信框架，大概有 5 年的成长期和 5 年的稳定成熟期。每个时代都有其匹配的应用通信框架，在 20 年前的 2G 时代，强跨语言跨平台而弱性能的 gRPC 是不会被采用的。&lt;/p>
&lt;p>每个通信框架，不同的人从不同角度看出不同的结论：初学者看重易用易学，性能测评者注重性能，应用架构师考虑其维护成本，老板则考虑则综合成本。一个应用通信框架的性能固然重要，其稳定性和进化能力更重要，得到有效维护的框架可在长时间单位内降低其综合成本：学习成本、维护成本、升级成本和更换成本。&lt;/p>
&lt;p>什么是 Dubbo-go？第一，它是 Dubbo 的 Go 语言版本，全面兼容 Dubbo 是其第一要义。第二，它是一个 Go 语言应用通信框架，会充分利用作为云原生时代第一语言&amp;mdash;Go 语言的优势，扩展 dubbo 的能力。&lt;/p>
&lt;p>2008 年诞生的 Dubbo 已有十多年历史，依靠阿里和其社区，历久弥新。2016 年发布的 Dubbo-go 也已进入第五个年头，如今全面兼容 Dubbo v2.7.x 的 Dubbo-go v1.5 终于发布了。&lt;/p>
&lt;p>回首过往，Dubbo-go 已经具备如下能力：&lt;/p>
&lt;ul>
&lt;li>互联互通：打通了 gRPC 和 Spring Cloud 生态；&lt;/li>
&lt;li>可观测性：基于 OpenTracing 和 Prometheus，使得其在 Logging、Tracing 和 Metrics 方面有了长足进步；&lt;/li>
&lt;li>云原生：Dubbo-go 实现了基于 Kubernetes API Server 为注册中心的通信能力，做到了升级成本最低。&lt;/li>
&lt;/ul>
&lt;p>毋庸讳言，相较于现有成绩，发展阶段的 Dubbo-go 对未来有更多的期待之处：&lt;/p>
&lt;ul>
&lt;li>易用性：Dubbo-go 的入门成本并不低，把很多感兴趣者挡在了门外。但好消息是，随着 Dubbo-go 在阿里内部的逐步推开，阿里中间件团队对其进行了进一步的封装，经生产环境检验后会开放给社区使用。&lt;/li>
&lt;li>云原生：目前的 Dubbo-go 的基于 kubernetes 的方案，从技术分层角度来看， Kubernetes API Server 终究是系统的运维态组件，不应该暴露给应用层，否则会造成 APIServer 自身通信压力过大，且系统整体风险很高：应用层使用不当，或者框架自身的流量方面的 bug，可能会把 APIServer 打垮，后果就是造成整体后端服务能力的瘫痪！所以应用层需要感知的是 kubernetes 提供给应用层的 Operator，不断进化的 Dubbo-go 计划在 v1.6 版本中发布 Dubbo-go Operator。&lt;/li>
&lt;/ul>
&lt;p>雄关漫道真如铁，而今迈步从头越。Dubbo-go 社区【钉钉群 23331795】与 Dubbo-go 同在。&lt;/p>
&lt;h2 id="应用维度注册模型">应用维度注册模型&lt;/h2>
&lt;p>经过一段时间的努力之后，我们终于完成了应用维度的服务注册与发现。和原本已有的接口维度的注册模型比起来，新的注册模型有两个突出特点：&lt;/p>
&lt;ol>
&lt;li>和主流的注册模型保持一致。目前的主流做法都是按照应用为基本单位来进行注册的，如Spring Cloud。在支持应用维度注册之后，对于接下来的云原生支持，奠定了基础；&lt;/li>
&lt;li>大幅度减轻对注册中心的压力。在该模型之下，从注册中心的视角看过去，集群规模只和实例数量成正比，而不是现有的和服务数量成正比；&lt;/li>
&lt;/ol>
&lt;p>当然，我们在设计的时候就考虑到了用户的迁移成本。要迁移到新的注册模型，只需要将现有使用的注册中心换成新的 &lt;code>ServiceDiscoveryRegistry&lt;/code> 就可以。&lt;/p>
&lt;p>&lt;code>ServiceDiscoveryRegistry&lt;/code> 是支持多种实现的。目前来说，我们支持：&lt;/p>
&lt;ol>
&lt;li>nacos;&lt;/li>
&lt;li>etcd;&lt;/li>
&lt;li>zookeeper;&lt;/li>
&lt;/ol>
&lt;p>我们提倡新上线的业务尽量使用 nacos 和 etcd 这种更可靠稳定的注册中心。&lt;/p>
&lt;h2 id="metadata-report-元数据中心">Metadata Report 元数据中心&lt;/h2>
&lt;p>v1.5 版本在支持应用维度注册模型时，有很重要的一个问题需要解决，即接口维度的元数据存储。服务维度的注册模型和应用维度的注册模型，本质的区别是往注册中心注册的数据维度的不一致。虽然我们在应用维度注册模型中，将接口维度的数据从注册中心中剔除了，但是在rpc的框架中，一个 consumer 要想真正找到想要调用的服务地址，就必须得到 provider 端开放的服务信息。这部分数据，在 v1.5 版本中，我们将它们存储到了元数据中心中。&lt;/p>
&lt;p>元数据中心，是一个接口定义。泛指一块存储区域，可以对接口级别的元数据进行存储、读取，provider 端调用存储，consumer 端调用读取。元数据中心中的数据需要保持准确性、实时性。&lt;/p>
&lt;p>目前元数据中心，有两个父类（Go 中没有继承，此处说的父子类，单纯指子类对父类的组合关系）实现，一个是 local 实现，一个是 remote 实现。local 实现是将 provider 的内存作为虚拟元数据中心，remote 实现是指依赖 ZooKeeper、etcd、nacos 等注册中心作为元数据中心。目前 remote 有 zookeeper、nacos、etcd 和 consul 的子类实现。即用户可以将元数据信息，通过以上的第三方注册中心进行数据存储和分发。&lt;/p>
&lt;h2 id="invocation-接口支持-attribute-属性">Invocation 接口支持 attribute 属性&lt;/h2>
&lt;p>invocation 结构中新增 attribute 属性支持，用于流程内部的属性存储。和 attachment 不同点在于，attachment会从 consumer 传递到 provider，但 attribute 属性不会。&lt;/p>
&lt;h2 id="k8s注册中心">k8s注册中心&lt;/h2>
&lt;p>在 v1.5 版本之前，k8s 注册中心的实现是通过直接使用 &lt;a href="https://github.com/kubernetes/client-go">k8s client&lt;/a> 中Pod对象的 List&amp;amp;&amp;amp;Watch 接口。在本次迭代中引入了 k8s informer。这样做的原因在于两点，首先一定的程度上来讲 dubbo-go 的 k8s 注册中心也是一个 k8s controller，使用 informer 的模式更加 k8s native。更重要的是社区计划后续向 CRD+Operator 的模式演进，informer 模式是对后续的演进的探索。除了这个铺垫之外，本次迭代还对跨 namespace 的服务发现做了支持。再有就是为了减少对 kube-apiserver List&amp;amp;&amp;amp;Watch 的压力，对 provider 和 consumer 的行为进行了区分，provider 不再进行 Watch 而仅对 kube-apiserver 进行写操作。&lt;/p>
&lt;h1 id="优化路由模型">优化路由模型&lt;/h1>
&lt;p>在 1.5 版本之前，Router 模型中属性是包含：优先级与路由属性，Router Chain 只包含路由属性。从中能识别出其实 Router Chain 也是一种特殊 Router。1.5 版本之后，使 Router 更抽象，分离出其优先级属性，新增 Priority Router、Chain 继承 Router 使其变为特殊的 Router，使关系上看起来更加清晰。如下图：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/1.5/router.png" alt="">&lt;/p>
&lt;h1 id="回顾与展望">回顾与展望&lt;/h1>
&lt;p>Dubbo-go 处于一个比较稳定成熟的状态。目前新版本正处于往云原生方向的尝试，应用服务维度注册是首先推出的功能，这是一个和之前模型完全不一样的新注册模型。该版本是我们朝云原生迈进新一步的关键版本。除此之外，包含在该版本也有一些之前提到的优化。&lt;/p>
&lt;p>下一个版本 v1.5.1，虽然仍是以兼容 Dubbo 2.7.x 为主要任务，但在分布式能力的增强上，也是我们关注的重点。&lt;/p>
&lt;p>在&lt;strong>分布式事务&lt;/strong>方面，有一个重要的基于 Seata 扩展实现。通过增加过滤器，在服务端接收 xid 并结合 seata-golang&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> 达到支持分布式事务的目的。 从而使 Dubbo-go 在分布式场景下，让用户有更多的选择，能适应更多的个性化场景。&lt;/p>
&lt;p>与此同时，在&lt;strong>传输链路安全性&lt;/strong>上，TLS 安全传输链路是该版本重要功能之一。通过提供统一入口，未来能引入更多的与传输链路安全性相关的功能，适应用户不一样的使用场景。&lt;/p>
&lt;p>&lt;strong>注册中心模型&lt;/strong>上，支持多注册中心集群负载均衡。业务部署假设是双注册中心（图 1 ），从原来双注册中心中所有 Provider 一起选址。优化成选址时的多了一层注册中心集群间的负载均衡（图 2 ）。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/1.5/multi-registry.png" alt="">&lt;/p>
&lt;p>（图 1 ）&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/1.5/loadbalance.png" alt="">&lt;/p>
&lt;p>（图 2 ）&lt;/p>
&lt;p>以前的 dubbo-go RPC 层直接复用了 getty 框架 的 RPC[&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>(https://github.com/AlexStocks/getty/tree/feature/rpc)]，未能实现协议和应用通信地址的隔离。阿里中间件展图同学重构了 dubbo-go RPC 层，实现了连接复用：可以实现 consumer 与 provider 端的同一个 TCP 连接上进行多协议通信。相关 PR 业已合并，会在 dubbo-go v1.5.1 中发布。&lt;/p>
&lt;p>目前下一个版本正在紧锣密鼓的开发中，具体规划及任务清单&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> ，都已经在 Github 上体现。&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://github.com/seata-golang/seata-golang">https://github.com/seata-golang/seata-golang&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a href="https://github.com/AlexStocks/getty/tree/feature/rpc">https://github.com/AlexStocks/getty/tree/feature/rpc&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>&lt;a href="https://github.com/apache/dubbo-go/projects/8">https://github.com/apache/dubbo-go/projects/8&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Blog: dubbo-go 1.5.1</title><link>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-1.5.1/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-1.5.1/</guid><description>
&lt;p>近期我们发布了 dubbo-go v1.5.1，虽然是 v1.5 的一个子版本，但相比于 v1.5.0， 社区还是投入了很大人力添加了如下重大改进。&lt;/p>
&lt;h2 id="1-应用维度注册模型">1 应用维度注册模型&lt;/h2>
&lt;p>在新模型 release 后，我们发现 Provider 每个 URL 发布元数据都会注册 ServiceInstance，影响性能需要优化。&lt;/p>
&lt;p>我们的优化方案是：&lt;/p>
&lt;p>去除 ServiceDiscoveryRegistry 中注册 ServiceInstance 的代码，在 config_loader 中的loadProviderConfig 方法的最后注册 ServiceInstance&lt;/p>
&lt;p>具体步骤：&lt;/p>
&lt;ol>
&lt;li>获取所有注册的 Registry，过滤出 ServiceDiscoveryRegistry，拿取所有 ServiceDiscovery。&lt;/li>
&lt;li>创建 ServiceInstance。&lt;/li>
&lt;li>每个 ServiceDiscovery 注册 ServiceInstance。&lt;/li>
&lt;/ol>
&lt;p>保证 Provider 在注册成功之后，才暴露元数据信息。&lt;/p>
&lt;h2 id="2-支持基于-seata-的事务">2 支持基于 Seata 的事务&lt;/h2>
&lt;p>基于 Seata 扩展实现。通过增加过滤器，在服务端接收 xid 并结合 &lt;a href="https://github.com/seata-golang/seata-golang">seata-golang&lt;/a> 达到支持分布式事务的目的。 从而使 Dubbo-go 在分布式场景下，让用户有更多的选择，能适应更多的个性化场景。&lt;/p>
&lt;p>我们在 dubbo-samples 中给出了 &lt;a href="https://github.com/apache/dubbo-go-samples/tree/1.5/seata">事务测试用例&lt;/a> 。&lt;/p>
&lt;h2 id="3-多注册中心集群负载均衡">3 多注册中心集群负载均衡&lt;/h2>
&lt;p>对于多注册中心订阅的场景，选址时的多了一层注册中心集群间的负载均衡：&lt;/p>
&lt;p>在 Cluster Invoker 这一级，我们支持的选址策略有：&lt;/p>
&lt;ul>
&lt;li>指定优先级&lt;/li>
&lt;li>同 zone 优先&lt;/li>
&lt;li>权重轮询&lt;/li>
&lt;/ul>
&lt;h2 id="4-传输链路安全性">4 传输链路安全性&lt;/h2>
&lt;p>该版本在传输链路的安全性上做了尝试，对于内置的 Dubbo getty Server 提供了基于 TLS 的安全链路传输机制。&lt;/p>
&lt;p>为尽可能保证应用启动的灵活性，TLS Cert 的指定通过配置文件方式，具体请参见 Dubbo-go 配置读取规则与 TLS 示例：&lt;/p>
&lt;h2 id="5-路由功能增强">5 路由功能增强&lt;/h2>
&lt;p>本次路由功能重点支持了 动态标签路由 和 应用/服务级条件路由。&lt;/p>
&lt;h3 id="51-动态标签路由">5.1 动态标签路由&lt;/h3>
&lt;p>标签路由通过将某一个或多个服务的提供者划分到同一个分组，约束流量只在指定分组中流转，从而实现流量隔离的目的，可以作为蓝绿发布、灰度发布等场景的能力基础。&lt;/p>
&lt;p>标签主要是指对 Provider 端应用实例的分组，目前有两种方式可以完成实例分组，分别是&lt;code>动态规则打标&lt;/code>和&lt;code>静态规则打标&lt;/code>，其中动态规则相较于静态规则优先级更高，而当两种规则同时存在且出现冲突时，将以动态规则为准。&lt;/p>
&lt;h4 id="511-动态规则打标">5.1.1 动态规则打标&lt;/h4>
&lt;p>可随时在&lt;a href="https://cn.dubbo.apache.org/zh/docsv2.7/user/examples/routing-rule/">服务治理控制台&lt;/a>下发标签归组规则&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># governance-tagrouter-provider应用增加了两个标签分组tag1和tag2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># tag1包含一个实例 127.0.0.1:20880&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># tag2包含一个实例 127.0.0.1:20881&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: &lt;span style="color:#cb4b16">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">runtime&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">enabled&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">key&lt;/span>: governance-tagrouter-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">tags&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: tag1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">addresses&lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;127.0.0.1:20880&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: tag2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">addresses&lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;127.0.0.1:20881&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="512-静态规则打标">5.1.2 静态规则打标&lt;/h4>
&lt;p>可以在 server 配置文件的 tag 字段里设置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">services&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;UserProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;hangzhouzk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.ikurento.user.UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">loadbalance&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;random&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">warmup&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;100&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">tag&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;beijing&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;GetUser&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">loadbalance&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;random&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>consumer 添加 tag 至 attachment 即可&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>ctx &lt;span style="color:#719e07">:=&lt;/span> context.&lt;span style="color:#268bd2">Background&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>attachment &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">make&lt;/span>(&lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]&lt;span style="color:#dc322f">string&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>attachment[&lt;span style="color:#2aa198">&amp;#34;dubbo.tag&amp;#34;&lt;/span>] = &lt;span style="color:#2aa198">&amp;#34;beijing&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ctx = context.&lt;span style="color:#268bd2">WithValue&lt;/span>(ctx, constant.AttachmentKey, attachment)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>err &lt;span style="color:#719e07">:=&lt;/span> userProvider.&lt;span style="color:#268bd2">GetUser&lt;/span>(ctx, []&lt;span style="color:#268bd2">interface&lt;/span>{}{&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>}, user)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>请求标签的作用域为每一次 invocation，使用 attachment 来传递请求标签，注意保存在 attachment 中的值将会在一次完整的远程调用中持续传递，得益于这样的特性，我们只需要在起始调用时，通过一行代码的设置，达到标签的持续传递。&lt;/p>
&lt;h4 id="513-规则详解">5.1.3 规则详解&lt;/h4>
&lt;h5 id="格式">格式&lt;/h5>
&lt;ul>
&lt;li>&lt;code>Key&lt;/code>明确规则体作用到哪个应用。&lt;strong>必填&lt;/strong>。&lt;/li>
&lt;li>&lt;code>enabled=true&lt;/code> 当前路由规则是否生效，可不填，缺省生效。&lt;/li>
&lt;li>&lt;code>force=false&lt;/code> 当路由结果为空时，是否强制执行，如果不强制执行，路由结果为空的路由规则将自动失效，可不填，缺省为 &lt;code>false&lt;/code>。&lt;/li>
&lt;li>&lt;code>runtime=false&lt;/code> 是否在每次调用时执行路由规则，否则只在提供者地址列表变更时预先执行并缓存结果，调用时直接从缓存中获取路由结果。如果用了参数路由，必须设为 &lt;code>true&lt;/code>，需要注意设置会影响调用的性能，可不填，缺省为 &lt;code>false&lt;/code>。&lt;/li>
&lt;li>&lt;code>priority=1&lt;/code> 路由规则的优先级，用于排序，优先级越大越靠前执行，可不填，缺省为 &lt;code>0&lt;/code>。&lt;/li>
&lt;li>&lt;code>tags&lt;/code> 定义具体的标签分组内容，可定义任意n（n&amp;gt;=1）个标签并为每个标签指定实例列表。&lt;strong>必填&lt;/strong>&lt;/li>
&lt;li>
&lt;ul>
&lt;li>name， 标签名称&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>addresses， 当前标签包含的实例列表&lt;/li>
&lt;/ul>
&lt;h5 id="降级约定">降级约定&lt;/h5>
&lt;ol>
&lt;li>&lt;code>dubbo.tag=tag1&lt;/code> 时优先选择 标记了 &lt;code>tag=tag1&lt;/code> 的 provider。若集群中不存在与请求标记对应的服务，默认将降级请求 tag 为空的 provider；如果要改变这种默认行为，即找不到匹配 tag1 的 provider 返回异常，需设置&lt;code>dubbo.force.tag=true&lt;/code>。&lt;/li>
&lt;li>&lt;code>dubbo.tag&lt;/code> 未设置时，只会匹配 tag 为空的 provider。即使集群中存在可用的服务，若 tag 不匹配也就无法调用，这与约定 1 不同，携带标签的请求可以降级访问到无标签的服务，但不携带标签/携带其他种类标签的请求永远无法访问到其他标签的服务。&lt;/li>
&lt;/ol>
&lt;h3 id="52-应用服务级条件路由">5.2 应用/服务级条件路由&lt;/h3>
&lt;p>您可以在路由规则配置中配置多个条件路由及其粒度&lt;/p>
&lt;p>Sample:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># dubbo router yaml configure file&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">routerRules&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">scope&lt;/span>: application
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">key&lt;/span>: BDTService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">priority&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">enable&lt;/span>: &lt;span style="color:#cb4b16">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">conditions &lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;host = 192.168.199.208 =&amp;gt; host = 192.168.199.208 &amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">scope&lt;/span>: service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">key&lt;/span>: com.ikurento.user.UserProvider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">priority&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">conditions &lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;host = 192.168.199.208 =&amp;gt; host = 192.168.199.208 &amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="521-规则详解">5.2.1 规则详解&lt;/h4>
&lt;h5 id="各字段含义">各字段含义&lt;/h5>
&lt;ul>
&lt;li>scope表示路由规则的作用粒度，scope的取值会决定key的取值。必填。&lt;/li>
&lt;li>
&lt;ul>
&lt;li>service 服务粒度&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>application 应用粒度&lt;/li>
&lt;li>Key明确规则体作用在哪个服务或应用。必填。&lt;/li>
&lt;li>
&lt;ul>
&lt;li>scope=service时，key取值为[{group}/]{service}[:{version}]的组合&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>scope=application时，key取值为application名称&lt;/li>
&lt;li>enabled=true 当前路由规则是否生效，可不填，缺省生效。&lt;/li>
&lt;li>force=false 当路由结果为空时，是否强制执行，如果不强制执行，路由结果为空的路由规则将自动失效，可不填，缺省为 false。&lt;/li>
&lt;li>runtime=false 是否在每次调用时执行路由规则，否则只在提供者地址列表变更时预先执行并缓存结果，调用时直接从缓存中获取路由结果。如果用了参数路由，必须设为 true，需要注意设置会影响调用的性能，可不填，缺省为 false。&lt;/li>
&lt;li>priority=1 路由规则的优先级，用于排序，优先级越大越靠前执行，可不填，缺省为 0。&lt;/li>
&lt;li>conditions 定义具体的路由规则内容。必填。&lt;/li>
&lt;/ul>
&lt;h2 id="6-回顾与展望">6 回顾与展望&lt;/h2>
&lt;p>Dubbo-go 处于一个比较稳定成熟的状态。目前新版本正处于往云原生方向的尝试，应用服务维度注册是首先推出的功能，这是一个和之前模型完全不一样的新注册模型。该版本是我们朝云原生迈进新一步的关键版本。除此之外，包含在该版本也有一些之前提到的优化。&lt;/p>
&lt;p>下一个版本 v1.5.2，本次的关注重点以通信模型改进为主，除此之外，与 2.7.x 的兼容性、易用性及质量保证也是本次关注的信息。&lt;/p>
&lt;p>在&lt;strong>服务发现&lt;/strong>，会支持更加多的方式，如：文件、Consul。 从而使 Dubbo-go 在服务发现场景下，让用户有更多的选择，能适应更多的个性化场景。&lt;/p>
&lt;p>另外 &lt;strong>易用性及质量保证&lt;/strong>，主要关注的是 samples 与自动化构建部分。可降低用户上手 Dubbo-go 的难度，提高代码质量。&lt;/p>
&lt;p>目前下一个版本正在紧锣密鼓的开发中，具体规划及任务清单&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> ，都已经在 Github 上体现。&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://github.com/apache/dubbo-go/projects/10">https://github.com/apache/dubbo-go/projects/10&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Blog: dubbo-go 可信调用实现</title><link>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-%E5%8F%AF%E4%BF%A1%E8%B0%83%E7%94%A8%E5%AE%9E%E7%8E%B0/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-%E5%8F%AF%E4%BF%A1%E8%B0%83%E7%94%A8%E5%AE%9E%E7%8E%B0/</guid><description>
&lt;p>Apache Dubbo/Dubbo-Go 作为阿里巴巴开源的一款服务治理框架，因其适应 Java/Go 开发者面向接口的编程习惯、完全透明的调用方式、优越的性能以及强大的扩展性等优点，在国内使用非常广泛。除此之外，Dubbo 开源版本原生集成了很多开箱即用的服务治理功能，包括链路追踪，路由、负载均衡、服务注册发现、监控、认证等。&lt;/p>
&lt;p>本文将讲解如何在 Dubbo/Dubbo-Go 中实现灵活，安全和高效的身份验证和授权方案。&lt;/p>
&lt;h2 id="可信的目的">可信的目的&lt;/h2>
&lt;p>为什么需要鉴权认证？实际生产中类似支付之类的安全性敏感的业务会有限制匿名系统调用的需求，其他业务在接入该类敏感业务之前，需要通过审批方可正常调用，这就需要对这类敏感服务进行权限管控。尽管 Dubbo 开源版本中支持 Token 方式的鉴权实现，但是该实现方式总体来说安全性并不高，并且无法满足我们需要动态下发以及变更的灵活性需求。&lt;/p>
&lt;p>针对于此，我们内部着重从巩固安全性和拓展灵活性层面重新设计了一套 Dubbo/Dubbo-Go 的服务间调用的鉴权认证功能。本文我们将主要从实现层面讲解其大致实现思路。&lt;/p>
&lt;h2 id="可信方案">可信方案&lt;/h2>
&lt;p>抽象来看鉴权认证主要围绕以下两个问题，&lt;/p>
&lt;ul>
&lt;li>身份认证：指验证应用的身份，每个应用在其生命周期内只有唯一身份，无法变更和伪造。&lt;/li>
&lt;li>权限鉴定：根据身份信息鉴定权限是否满足调用。权限粒度可以进行控制。&lt;/li>
&lt;/ul>
&lt;p>我们通过 Access Key ID/Secret Access Key (后文简称为 AK/SK) 信息标识应用和应用之间的身份关系。例如上游 应用A 依赖下游 服务B 和 C，则 A 对 B 和 C 分别有一套 AK/SK。其相互独立没有任何关系。就算 A服务 的 AK/SK 信息泄漏，也无法通过该 AK/SK 信息调用其他的服务。&lt;/p>
&lt;p>在权限鉴定方面也借鉴了公有云开放 API 常用的 AK/SK 签名机制。 在请求过程中使用 SK 签名生成 SigningKey，并通过 Dubbo 的 attachment 机制将额外的元数据信息以及 SigningKey 传输到服务端，交由服务端计算和验签，验签通过方能正常处理和响应。&lt;/p>
&lt;p>签名过程主要通过如下三个方式进行加强 SigningKey 的可靠性和安全性。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>验证请求者的身份&lt;/p>
&lt;p>签名会通过对应应用的SK作为加密密钥对请求元数据(以及参数)进行加密，保证签名的唯一性和不可伪造性。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>支持对参数进行计算签名，防止非法篡改&lt;/p>
&lt;p>若请求参数在传输过程中遭到非法篡改，则收到请求后服务端验签匹配将失败，身份校验无法通过，从而防止请求参数被篡改。考虑到签名以及验签过程中加入请求参数的计算可能会影响性能，所以这个过程是可选的。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>防止重放攻击&lt;/p>
&lt;p>每一次请求生成的SigningKey都具有指定的有效时间。如请求被截获，该请求无法在有效时间外进行调用。一定程度避免了重放攻击。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>同时为了去掉明文配置，防止AK/SK信息泄漏，我们通过鉴权系统分发和管理所有AK/SK信息。并且通过对接内部审批流程，达到流程化和规范化。需要鉴权的应用会通过启动获取的方式拉当前应用分发出去或者是已被授权的AK/SK信息。这种方式也带来了另一种好处，新增、吊销以及更新权限信息也无需重启应用。&lt;/p>
&lt;h2 id="可信流程">可信流程&lt;/h2>
&lt;p>结合上面的这些需求和方案，整个接入和鉴权流程图如下所示：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/trusted-call/process.png" alt="">&lt;/p>
&lt;p>整体流程如下：&lt;/p>
&lt;ol>
&lt;li>使用该功能的应用需要提前申请对应的证书，并向提供服务的应用提交申请访问工单，由双方负责人审批通过后，请求鉴权服务中心自动生成键值对。&lt;/li>
&lt;li>除此之外，开启鉴权认证的服务在应用启动之后，会运行一个后台线程，长轮询远鉴权服务中心，查询是否有新增权限变动信息，如果有则进行全量/增量的拉取。&lt;/li>
&lt;li>上游应用在请求需要鉴权的服务时，会通过SK作为签名算法的 key，对本次请求的元数据信息甚至是参数信息进行计算得到签名，通过 Dubbo协议 Attachment字段 传送到对端，除此之外还有请求时间戳、AK信息等信息。&lt;/li>
&lt;li>下游应用在处理鉴权服务时会对请求验签，验签通过则继续处理请求，否则直接返回异常。&lt;/li>
&lt;/ol>
&lt;p>其中需要说明的是第三步，使用鉴权服务的应用和鉴权服务中心的交互需通过 HTTPS 的双向认证，并在 TLS 信道上进行数据交互，保证 AK/SK 信息传输的安全性。&lt;/p>
&lt;p>该方案目前已经有 Java/Go 实现，均已合并到 dubbo/dubbo-go。除了默认的 Hmac 签名算法实现之外，我们将签名和认证方法进行抽象，以Dubbo Go中的实现为例。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Authenticator
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> Authenticator &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Sign
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// give a sign to request
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Sign&lt;/span>(protocol.Invocation, &lt;span style="color:#719e07">*&lt;/span>common.URL) &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Authenticate
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// verify the signature of the request is valid or not
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Authenticate&lt;/span>(protocol.Invocation, &lt;span style="color:#719e07">*&lt;/span>common.URL) &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>使用者可通过 SPI 机制定制签名和认证方式，以及适配公司内部基础设施的密钥服务下发 AK/SK。&lt;/p>
&lt;h2 id="示例">示例&lt;/h2>
&lt;p>以 &lt;a href="https://github.com/apache/dubbo-go-samples/tree/master/helloworld/">Helloworld 示例&lt;/a> 中的代码接入当前社区版本中的默认鉴权认证功能实现为例：&lt;/p>
&lt;p>在无需改变代码的情况下，只需要在配置上增加额外的相关鉴权配置即可，dubbo-go 服务端配置示例如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">services&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;UserProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 可以指定多个registry，使用逗号隔开;不指定默认向所有注册中心注册&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;hangzhouzk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 相当于dubbo.xml中的interface&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.ikurento.user.UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">loadbalance&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;random&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 本服务开启auth&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">auth&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 启用auth filter，对请求进行验签&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filter&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;auth&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 默认实现通过配置文件配置AK、SK&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">params&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">.accessKeyId&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;SYD8-23DF&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">.secretAccessKey&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;BSDY-FDF1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">warmup&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;100&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;GetUser&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">loadbalance&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;random&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>dubbo-go 客户端配置示例如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">references&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;UserProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 可以指定多个registry，使用逗号隔开;不指定默认向所有注册中心注册&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;hangzhouzk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.ikurento.user.UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 本服务开启sign filter，需要签名&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filter&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sign&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 默认实现通过配置文件配置AK、SK&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">params&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">.accessKeyId&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;SYD8-23DF&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">.secretAccessKey&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;BSDY-FDF1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;GetUser&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到，dubbo-go 接入鉴权认证的功能也十分简单。需要补充说明的是，配置文件文件中 ak/sk 都加了特殊前缀 &amp;ldquo;.&amp;quot;，是为了说明该字段是敏感信息，不能在发起网络请求时传输出去，相关代码可参阅 &lt;a href="https://github.com/apache/dubbo-go/pull/509">dubbo-go-pr-509&lt;/a>。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>Apache Dubbo 作为一款老而弥新的服务治理框架，无论是其自身还是其生态都还在飞速进化中。本文描述的最新实现的可信服务调用，是为了避免敏感接口被匿名用户调用而在 SDK 层面提供的额外保障，在 RPC 层面保障安全性。&lt;/p>
&lt;p>Dubbo-Go 作为 Dubbo 生态中发展最快的成员，目前基本上保持与 Dubbo 齐头并进的态势。Dubbo-Go 社区钉钉群号为 23331795， 欢迎你的加入。&lt;/p>
&lt;blockquote>
&lt;p>作者信息： 郑泽超，Apache Dubbo/Dubbo-Go committer，GithubID: CodingSinger，目前就职于上海爱奇艺科技有限公司，Java/Golang 开发工程师。&lt;/p>
&lt;/blockquote></description></item><item><title>Blog: Dubbo-go 源码笔记（一）Server 端开启服务过程</title><link>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%B8%80server-%E7%AB%AF%E5%BC%80%E5%90%AF%E6%9C%8D%E5%8A%A1%E8%BF%87%E7%A8%8B/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%B8%80server-%E7%AB%AF%E5%BC%80%E5%90%AF%E6%9C%8D%E5%8A%A1%E8%BF%87%E7%A8%8B/</guid><description>
&lt;p>当拿到一款框架之后，一种不错的源码阅读方式大致如下：从运行最基础的 helloworld demo 源码开始 —&amp;gt; 再查看配置文件 —&amp;gt; 开启各种依赖服务（比如zk、consul） —&amp;gt; 开启服务端 —&amp;gt; 再到通过 client 调用服务端 —&amp;gt; 打印完整请求日志和回包。调用成功之后，再根据框架的设计模型，从配置文件解析开始，自顶向下递阅读整个框架的调用栈。&lt;/p>
&lt;p>对于 C/S 模式的 rpc 请求来说，整个调用栈被拆成了 client 和 server 两部分，所以可以分别从 server 端的配置文件解析阅读到 server 端的监听启动，从 client 端的配置文件解析阅读到一次 invoker Call 调用。这样一次完整请求就明晰了起来。&lt;/p>
&lt;h2 id="运行官网提供的-helloworld-demo">运行官网提供的 helloworld-demo&lt;/h2>
&lt;p>&lt;strong>官方 demo 相关链接&lt;/strong>：https://github.com/dubbogo/dubbo-samples/tree/master/golang/helloworld/dubbo&lt;/p>
&lt;h3 id="1-dubbo-go-27-版本-quickstart">1. dubbo-go 2.7 版本 QuickStart&lt;/h3>
&lt;h4 id="1开启一个-go-server-服务">1）开启一个 go-server 服务&lt;/h4>
&lt;ul>
&lt;li>将仓库 clone 到本地&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ git clone https://github.com/dubbogo/dubbo-samples.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>进入 dubbo 目录&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">cd&lt;/span> dubbo-samples/golang/helloworld/dubbo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>进入目录后可看到四个文件夹，分别支持 go 和 java 的 client 以及 server，我们尝试运行一个 go 的 server。进入 app 子文件夹内，可以看到里面保存了 go 文件。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">cd&lt;/span> go-server/app
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>sample 文件结构&lt;/li>
&lt;/ul>
&lt;p>可以在 go-server 里面看到三个文件夹：app、assembly、profiles。&lt;/p>
&lt;p>其中 app 文件夹下保存 go 源码，assembly 文件夹下保存可选的针对特定环境的 build 脚本，profiles 下保存配置文件。对于 dubbo-go 框架，配置文件非常重要，没有文件将导致服务无法启动。&lt;/p>
&lt;ul>
&lt;li>设置指向配置文件的环境变量&lt;/li>
&lt;/ul>
&lt;p>由于 dubbo-go 框架依赖配置文件启动，让框架定位到配置文件的方式就是通过环境变量来找。对于 server 端需要两个必须配置的环境变量：CONF_PROVIDER_FILE_PATH、APP_LOG_CONF_FILE，分别应该指向服务端配置文件、日志配置文件。&lt;/p>
&lt;p>在 sample 里面，我们可以使用 dev 环境，即 profiles/dev/log.yml 和 profiles/dev/server.yml 两个文件。在 app/ 下，通过命令行中指定好这两个文件：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">CONF_PROVIDER_FILE_PATH&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;../profiles/dev/server.yml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">APP_LOG_CONF_FILE&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;../profiles/dev/log.yml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>设置 go 代理并运行服务&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ go run .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果提示 timeout，则需要设置 goproxy 代理。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">GOPROXY&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;http://goproxy.io&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>再运行 go run 即可开启服务。&lt;/p>
&lt;h4 id="2运行-zookeeper">2）运行 zookeeper&lt;/h4>
&lt;p>安装 zookeeper，并运行 zkServer, 默认为 2181 端口。&lt;/p>
&lt;h4 id="3运行-go-client-调用-server-服务">3）运行 go-client 调用 server 服务&lt;/h4>
&lt;ul>
&lt;li>进入 go-client 的源码目录&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">cd&lt;/span> go-client/app
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>同理，在 /app 下配置环境变量&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">CONF_CONSUMER_FILE_PATH&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;../profiles/dev/client.yml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">APP_LOG_CONF_FILE&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;../profiles/dev/log.yml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置 go 代理：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">GOPROXY&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;http://goproxy.io&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>运行程序&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ go run .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>即可在日志中找到打印出的请求结果：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>response result: &amp;amp;&lt;span style="color:#719e07">{&lt;/span>A001 Alex Stocks &lt;span style="color:#2aa198">18&lt;/span> 2020-10-28 14:52:49.131 +0800 CST&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样，在运行的 server 中，也可以在日志中找到打印出的请求：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>req:&lt;span style="color:#719e07">[]&lt;/span>interface &lt;span style="color:#719e07">{}{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rsp:main.User&lt;span style="color:#719e07">{&lt;/span>Id:&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>, Name:&lt;span style="color:#2aa198">&amp;#34;Alex Stocks&amp;#34;&lt;/span>, Age:18, Time:time.Time&lt;span style="color:#719e07">{&lt;/span>...&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>恭喜！一次基于 dubbo-go 的 rpc 调用成功。&lt;/p>
&lt;h4 id="4常见问题">4）常见问题&lt;/h4>
&lt;ul>
&lt;li>当日志开始部分出现 profiderInit 和 ConsumerInit 均失败的日志，检查环境变量中配置路径是否正确，配置文件是否正确。&lt;/li>
&lt;li>当日志中出现 register 失败的情况，一般为向注册中心注册失败，检查注册中心是否开启，检查配置文件中关于 register 的端口是否正确。&lt;/li>
&lt;li>sample 的默认开启端口为 20000，确保启动前无占用。&lt;/li>
&lt;/ul>
&lt;h3 id="2-配置环境变量">2. 配置环境变量&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">APP_LOG_CONF_FILE&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;../profiles/dev/log.yml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">CONF_CONSUMER_FILE_PATH&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;../profiles/dev/client.yml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3-服务端源码">3. 服务端源码&lt;/h3>
&lt;h4 id="1目录结构">1）目录结构&lt;/h4>
&lt;p>dubbo-go 框架的 example 提供的目录如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p1.png" alt="">&lt;/p>
&lt;ul>
&lt;li>app/ 文件夹下存放源码，可以自己编写环境变量配置脚本 buliddev.sh&lt;/li>
&lt;li>assembly/ 文件夹下存放不同平台的构建脚本&lt;/li>
&lt;li>profiles/ 文件夹下存放不同环境的配置文件&lt;/li>
&lt;li>target/ 文件夹下存放可执行文件&lt;/li>
&lt;/ul>
&lt;h3 id="2关键源码">2）关键源码&lt;/h3>
&lt;p>源码放置在 app/ 文件夹下，主要包含 server.go 和 user.go 两个文件，顾名思义，server.go 用于使用框架开启服务以及注册传输协议；user.go 则定义了 rpc-service 结构体，以及传输协议的结构。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>user.go&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">SetProviderService&lt;/span>(&lt;span style="color:#b58900">new&lt;/span>(UserProvider))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ------for hessian2------
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> hessian.&lt;span style="color:#268bd2">RegisterPOJO&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>User{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> User &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Id &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Age &lt;span style="color:#dc322f">int32&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Time time.Time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> UserProvider &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (u &lt;span style="color:#719e07">*&lt;/span>UserProvider) &lt;span style="color:#268bd2">GetUser&lt;/span>(ctx context.Context, req []&lt;span style="color:#268bd2">interface&lt;/span>{}) (&lt;span style="color:#719e07">*&lt;/span>User, &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到，user.go 中存在 init 函数，是服务端代码中最先被执行的部分。User 为用户自定义的传输结构体，UserProvider 为用户自定义的 rpc_service；包含一个 rpc 函数，GetUser。当然，用户可以自定义其他的 rpc 功能函数。&lt;/p>
&lt;p>在 init 函数中，调用 config 的 SetProviderService 函数，将当前 rpc_service 注册在框架 config 上。&lt;/p>
&lt;p>&lt;strong>可以查看 dubbo 官方文档提供的设计图：&lt;/strong>&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p2.png" alt="">&lt;/p>
&lt;p>service 层下面就是 config 层，用户服务会逐层向下注册，最终实现服务端的暴露。&lt;/p>
&lt;p>rpc-service 注册完毕之后，调用 hessian 接口注册传输结构体 User。&lt;/p>
&lt;p>至此，init 函数执行完毕。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>server.go&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// they are necessary:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// export CONF_PROVIDER_FILE_PATH=&amp;#34;xxx&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// export APP_LOG_CONF_FILE=&amp;#34;xxx&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hessian.&lt;span style="color:#268bd2">RegisterPOJO&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>User{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">initSignal&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">initSignal&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> signals &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">make&lt;/span>(&lt;span style="color:#268bd2">chan&lt;/span> os.Signal, &lt;span style="color:#2aa198">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>之后执行 main 函数。&lt;/p>
&lt;p>main 函数中只进行了两个操作，首先使用 hessian 注册组件将 User 结构体注册（与之前略有重复），从而可以在接下来使用 getty 打解包。&lt;/p>
&lt;p>之后调用 config.Load 函数，该函数位于框架 config/config_loader.go 内，这个函数是整个框架服务的启动点，&lt;strong>下面会详细讲这个函数内重要的配置处理过程&lt;/strong>。执行完 Load() 函数之后，配置文件会读入框架，之后根据配置文件的内容，将注册的 service 实现到配置结构里，再调用 Export 暴露给特定的 registry，进而开启特定的 service 进行对应端口的 tcp 监听，成功启动并且暴露服务。&lt;/p>
&lt;p>最终开启信号监听 initSignal() 优雅地结束一个服务的启动过程。&lt;/p>
&lt;h3 id="4-客户端源码">4. 客户端源码&lt;/h3>
&lt;p>客户端包含 client.go 和 user.go 两个文件，其中 user.go 与服务端完全一致，不再赘述。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>client.go&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// they are necessary:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// export CONF_CONSUMER_FILE_PATH=&amp;#34;xxx&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// export APP_LOG_CONF_FILE=&amp;#34;xxx&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hessian.&lt;span style="color:#268bd2">RegisterPOJO&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>User{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> time.&lt;span style="color:#268bd2">Sleep&lt;/span>(&lt;span style="color:#2aa198">3e9&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">println&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;\n\n\nstart to test dubbo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> user &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>User{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#719e07">:=&lt;/span> userProvider.&lt;span style="color:#268bd2">GetUser&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), []&lt;span style="color:#268bd2">interface&lt;/span>{}{&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>}, user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">println&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;response result: %v\n&amp;#34;&lt;/span>, user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">initSignal&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>main 函数和服务端也类似，首先将传输结构注册到 hessian 上，再调用 config.Load() 函数。在下文会介绍，客户端和服务端会根据配置类型执行 config.Load() 中特定的函数 loadConsumerConfig() 和 loadProviderConfig()，从而达到“开启服务”、“调用服务”的目的。&lt;/p>
&lt;p>加载完配置之后，还是通过实现服务、增加函数 proxy、申请 registry 和 reloadInvoker 指向服务端 ip 等操作，重写了客户端实例 userProvider 的对应函数，这时再通过调用 GetUser 函数，可以直接通过 invoker，调用到已经开启的服务端，实现 rpc 过程。&lt;/p>
&lt;p>下面会从 server 端和 client 端两个角度，详细讲解服务启动、registry 注册和调用过程。&lt;/p>
&lt;h3 id="5-自定义配置文件非环境变量方法">5. 自定义配置文件（非环境变量）方法&lt;/h3>
&lt;h4 id="1服务端自定义配置文件">1）服务端自定义配置文件&lt;/h4>
&lt;ul>
&lt;li>var providerConfigStr = &lt;code>xxxxx&lt;/code>// 配置文件内容，可以参考 log 和 client。在这里你可以定义配置文件的获取方式，比如配置中心，本地文件读取。&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>&lt;strong>log 地址&lt;/strong>：https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/log.yml&lt;/p>
&lt;p>&lt;strong>client 地址&lt;/strong>：https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/client.yml&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>在 &lt;code>config.Load()&lt;/code> 之前设置配置，例如：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hessian.&lt;span style="color:#268bd2">RegisterPOJO&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>User{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> providerConfig &lt;span style="color:#719e07">:=&lt;/span> config.ProviderConfig{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> yaml.&lt;span style="color:#268bd2">Unmarshal&lt;/span>([]&lt;span style="color:#b58900">byte&lt;/span>(providerConfigStr), &lt;span style="color:#719e07">&amp;amp;&lt;/span>providerConfig)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">SetProviderConfig&lt;/span>(providerConfig)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> defaultServerConfig &lt;span style="color:#719e07">:=&lt;/span> dubbo.&lt;span style="color:#268bd2">GetDefaultServerConfig&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dubbo.&lt;span style="color:#268bd2">SetServerConfig&lt;/span>(defaultServerConfig)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">SetLoggerLevel&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;warn&amp;#34;&lt;/span>) &lt;span style="color:#586e75">// info,warn
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="2客户端自定义配置文件">2）客户端自定义配置文件&lt;/h4>
&lt;ul>
&lt;li>var consumerConfigStr = &lt;code>xxxxx&lt;/code>// 配置文件内容，可以参考 log 和 clien。在这里你可以定义配置文件的获取方式，比如配置中心，本地文件读取。&lt;/li>
&lt;li>在 &lt;code>config.Load()&lt;/code> 之前设置配置，例如：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> p &lt;span style="color:#719e07">:=&lt;/span> config.ConsumerConfig{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> yaml.&lt;span style="color:#268bd2">Unmarshal&lt;/span>([]&lt;span style="color:#b58900">byte&lt;/span>(consumerConfigStr), &lt;span style="color:#719e07">&amp;amp;&lt;/span>p)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">SetConsumerConfig&lt;/span>(p)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> defaultClientConfig &lt;span style="color:#719e07">:=&lt;/span> dubbo.&lt;span style="color:#268bd2">GetDefaultClientConfig&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dubbo.&lt;span style="color:#268bd2">SetClientConf&lt;/span>(defaultClientConfig)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">SetLoggerLevel&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;warn&amp;#34;&lt;/span>) &lt;span style="color:#586e75">// info,warn
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> user &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>User{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#719e07">:=&lt;/span> userProvider.&lt;span style="color:#268bd2">GetUser&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), []&lt;span style="color:#268bd2">interface&lt;/span>{}{&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>}, user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Print&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#268bd2">Print&lt;/span>(user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="server-端">Server 端&lt;/h2>
&lt;p>服务暴露过程涉及到多次原始 rpcService 的封装、暴露，网上其他文章的图感觉太过笼统，在此，简要地绘制了一个用户定义服务的数据流图：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p3.png" alt="">&lt;/p>
&lt;h3 id="1-加载配置">1. 加载配置&lt;/h3>
&lt;h4 id="1框架初始化">1）框架初始化&lt;/h4>
&lt;p>在加载配置之前，框架提供了很多已定义好的协议、工厂等组件，都会在对应模块 init 函数内注册到 extension 模块上，以供接下来配置文件中进行选用。&lt;/p>
&lt;p>其中重要的有：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>默认函数代理工厂&lt;/strong>：common/proxy/proxy_factory/default.go&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> extension.&lt;span style="color:#268bd2">SetProxyFactory&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;default&amp;#34;&lt;/span>, NewDefaultProxyFactory)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它的作用是将原始 rpc-service 进行封装，形成 proxy_invoker，更易于实现远程 call 调用，详情可见其 invoke 函数。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>注册中心注册协议&lt;/strong>：
registry/protocol/protocol.go&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> extension.&lt;span style="color:#268bd2">SetProtocol&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;registry&amp;#34;&lt;/span>, GetProtocol)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它负责将 invoker 暴露给对应注册中心，比如 zk 注册中心。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>zookeeper 注册协议&lt;/strong>：registry/zookeeper/zookeeper.go&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> extension.&lt;span style="color:#268bd2">SetRegistry&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>, newZkRegistry)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它合并了 base_resiger，负责在服务暴露过程中，将服务注册在 zookeeper 注册器上，从而为调用者提供调用方法。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>dubbo 传输协议&lt;/strong>：protocol/dubbo/dubbo.go&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> extension.&lt;span style="color:#268bd2">SetProtocol&lt;/span>(DUBBO, GetProtocol)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它负责监听对应端口，将具体的服务暴露，并启动对应的事件 handler，将远程调用的 event 事件传递到 invoker 内部，调用本地 invoker 并获得执行结果返回。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>filter 包装调用链协议&lt;/strong>：protocol/protocolwrapper/protocol_filter_wrapper.go&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> extension.&lt;span style="color:#268bd2">SetProtocol&lt;/span>(FILTER, GetProtocol)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它负责在服务暴露过程中，将代理 invoker 打包，通过配置好的 filter 形成调用链，并交付给 dubbo 协议进行暴露。&lt;/p>
&lt;p>上述提前注册好的框架已实现的组件，在整个服务暴露调用链中都会用到，会根据配置取其所需。&lt;/p>
&lt;h4 id="2配置文件">2）配置文件&lt;/h4>
&lt;p>服务端需要的重要配置有三个字段：services、protocols、registries。&lt;/p>
&lt;p>profiles/dev/server.yml:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">registries &lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;demoZk&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">timeout &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;127.0.0.1:2181&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">services&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;UserProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 可以指定多个registry，使用逗号隔开;不指定默认向所有注册中心注册&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;demoZk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 相当于dubbo.xml中的interface&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.ikurento.user.UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">loadbalance&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;random&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">warmup&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;100&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;GetUser&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">loadbalance&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;random&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protocols&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>: &lt;span style="color:#2aa198">20000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中 service 指定了要暴露的 rpc-service 名（&amp;ldquo;UserProvider）、暴露的协议名（&amp;ldquo;dubbo&amp;rdquo;）、注册的协议名(&amp;ldquo;demoZk&amp;rdquo;)、暴露的服务所处的 interface、负载均衡策略、集群失败策略及调用的方法等等。&lt;/p>
&lt;p>其中，中间服务的协议名需要和 registries 下的 mapkey 对应，暴露的协议名需要和 protocols 下的 mapkey 对应。&lt;/p>
&lt;p>可以看到上述例子中，使用了 dubbo 作为暴露协议，使用了 zookeeper 作为中间注册协议，并且给定了端口。如果 zk 需要设置用户名和密码，也可以在配置中写好。&lt;/p>
&lt;h4 id="3配置文件的读入和检查">3）配置文件的读入和检查&lt;/h4>
&lt;blockquote>
&lt;p>config/config_loader.go:: Load()&lt;/p>
&lt;/blockquote>
&lt;p>在上述 example 的 main 函数中，有 config.Load() 函数的直接调用，该函数执行细节如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Load Dubbo Init
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">Load&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init router
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">initRouter&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init the global event dispatcher
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> extension.&lt;span style="color:#268bd2">SetAndInitGlobalDispatcher&lt;/span>(&lt;span style="color:#268bd2">GetBaseConfig&lt;/span>().EventDispatcherType)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// start the metadata report if config set
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">startMetadataReport&lt;/span>(&lt;span style="color:#268bd2">GetApplicationConfig&lt;/span>().MetadataType, &lt;span style="color:#268bd2">GetBaseConfig&lt;/span>().MetadataReportConfig); err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Errorf&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;Provider starts metadata report error, and the error is {%#v}&amp;#34;&lt;/span>, err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// reference config
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">loadConsumerConfig&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// service config
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">loadProviderConfig&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init the shutdown callback
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">GracefulShutdownInit&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在本文中，我们重点关心 loadConsumerConfig() 和 loadProviderConfig() 两个函数。&lt;/p>
&lt;p>对于 provider 端，可以看到 loadProviderConfig() 函数代码如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p4.png" alt="">&lt;/p>
&lt;p>前半部分是配置的读入和检查，进入 for 循环后，是单个 service 的暴露起始点。&lt;/p>
&lt;p>前面提到，在配置文件中已经写好了要暴露的 service 的种种信息，比如服务名、interface 名、method 名等等。在图中 for 循环内，会将所有 service 的服务依次实现。&lt;/p>
&lt;p>for 循环的第一行，根据 key 调用 GetProviderService 函数，拿到注册的 rpcService 实例，这里对应上述提到的 init 函数中，用户手动注册的自己实现的 rpc-service 实例：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p5.png" alt="">&lt;/p>
&lt;p>这个对象也就成为了 for 循环中的 rpcService 变量，将这个对象注册通过 Implement 函数写到 sys（ServiceConfig 类型）上，设置好 sys 的 key 和协议组，最终调用了 sys 的 Export 方法。&lt;/p>
&lt;p>此处对应流程图的部分：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p6.png" alt="">&lt;/p>
&lt;p>至此，框架配置结构体已经拿到了所有 service 有关的配置，以及用户定义好的 rpc-service 实例，它触发了 Export 方法，旨在将自己的实例暴露出去。这是 Export 调用链的起始点。&lt;/p>
&lt;h3 id="2-原始-service-封装入-proxy_invoker">2. 原始 service 封装入 proxy_invoker&lt;/h3>
&lt;blockquote>
&lt;p>config/service_config.go :: Export()&lt;/p>
&lt;/blockquote>
&lt;p>接下来进入 ServiceConfig.Export() 函数.&lt;/p>
&lt;p>这个函数进行了一些细碎的操作，比如为不同的协议分配随机端口，如果指定了多个中心注册协议，则会将服务通过多个中心注册协议的 registryProtocol 暴露出去，我们只关心对于一个注册协议是如何操作的。还有一些操作比如生成调用 url 和注册 url，用于为暴露做准备。&lt;/p>
&lt;h4 id="1首先通过配置生成对应-registryurl-和-serviceurl">1）首先通过配置生成对应 registryUrl 和 serviceUrl&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p7.png" alt="">&lt;/p>
&lt;p>registryUrl 是用来向中心注册组件发起注册请求的，对于 zookeeper 的话，会传入其 ip 和端口号，以及附加的用户名密码等信息。&lt;/p>
&lt;p>这个 regUrl 目前只存有注册（zk）相关信息，后续会补写入 ServiceIvk，即服务调用相关信息，里面包含了方法名，参数等&amp;hellip;&lt;/p>
&lt;h4 id="2对于一个注册协议将传入的-rpc-service-实例注册在-commonservicemap">2）对于一个注册协议，将传入的 rpc-service 实例注册在 common.ServiceMap&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p8.png" alt="">&lt;/p>
&lt;p>这个 Register 函数将服务实例注册了两次，一次是以 Interface 为 key 写入接口服务组内，一次是以 interface 和 proto 为 key 写入特定的一个唯一的服务。&lt;/p>
&lt;p>后续会从 common.Map 里面取出来这个实例。&lt;/p>
&lt;h4 id="3获取默认代理工厂将实例封装入代理-invoker">3）获取默认代理工厂，将实例封装入代理 invoker&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 拿到一个proxyInvoker，这个invoker的url是传入的regUrl，这个地方将上面注册的service实例封装成了invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 这个GetProxyFactory返回的默认是common/proxy/proxy_factory/default.go
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 这个默认工厂调用GetInvoker获得默认的proxyInvoker，保存了当前注册url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>invoker &lt;span style="color:#719e07">:=&lt;/span> extension.&lt;span style="color:#268bd2">GetProxyFactory&lt;/span>(providerConfig.ProxyFactory).&lt;span style="color:#268bd2">GetInvoker&lt;/span>(&lt;span style="color:#719e07">*&lt;/span>regUrl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 暴露出来 生成exporter,开启tcp监听
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 这里就该跳到registry/protocol/protocol.go registryProtocol 调用的Export，将当前proxyInvoker导出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>exporter = c.cacheProtocol.&lt;span style="color:#268bd2">Export&lt;/span>(invoker)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这一步的 GetProxyFactory(&amp;ldquo;default&amp;rdquo;) 方法获取默认代理工厂，通过传入上述构造的 regUrl，将 url 封装入代理 invoker。&lt;/p>
&lt;p>可以进入 common/proxy/proxy_factory/default.go::ProxyInvoker.Invoke() 函数里，看到对于 common.Map 取用为 svc 的部分，以及关于 svc 对应 Method 的实际调用 Call 的函数如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p9.png" alt="">&lt;/p>
&lt;p>到这里，上面 GetInvoker(*regUrl) 返回的 invoker 即为 proxy_invoker，它封装好了用户定义的 rpc_service，并将具体的调用逻辑封装入了 Invoke 函数内。&lt;/p>
&lt;blockquote>
&lt;p>为什么使用 Proxy_invoker 来调用？&lt;/p>
&lt;p>通过这个 proxy_invoke 调用用户的功能函数，调用方式将更加抽象化，可以在代码中看到，通过 ins 和 outs 来定义入参和出参，将整个调用逻辑抽象化为 invocation 结构体，而将具体的函数名的选择、参数向下传递和 reflect 反射过程封装在 invoke 函数内，这样的设计更有利于之后远程调用。个人认为这是 dubbo Invoke 调用链的设计思想。&lt;/p>
&lt;p>至此，实现了图中对应的部分：&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p10.png" alt="">&lt;/p>
&lt;h3 id="3-registry-协议在-zkregistry-上暴露上面的-proxy_invoker">3. registry 协议在 zkRegistry 上暴露上面的 proxy_invoker&lt;/h3>
&lt;p>上面，我们执行到了 exporter = c.cacheProtocol.Export(invoker)。&lt;/p>
&lt;p>这里的 cacheProtocol 为一层缓存设计，对应到原始的 demo 上，这里是默认实现好的 registryProtocol。&lt;/p>
&lt;blockquote>
&lt;p>registry/protocol/protocol.go:: Export()&lt;/p>
&lt;/blockquote>
&lt;p>这个函数内构造了多个 EventListener，非常有 java 的设计感。&lt;/p>
&lt;p>我们只关心服务暴露的过程，先忽略这些监听器。&lt;/p>
&lt;h4 id="1获取注册-url-和服务-url">1）获取注册 url 和服务 url&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p11.png" alt="">&lt;/p>
&lt;h4 id="2获取注册中心实例-zkregistry">2）获取注册中心实例 zkRegistry&lt;/h4>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p12.png" alt="">&lt;/p>
&lt;p>一层缓存操作，如果 cache 没有需要从 common 里面重新拿 zkRegistry。&lt;/p>
&lt;h4 id="3zkregistry-调用-registry-方法在-zookeeper-上注册-dubbopath">3）zkRegistry 调用 Registry 方法，在 zookeeper 上注册 dubboPath&lt;/h4>
&lt;p>上述拿到了具体的 zkRegistry 实例，该实例的定义在：registry/zookeeper/registry.go。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p13.png" alt="">&lt;/p>
&lt;p>该结构体组合了 registry.BaseRegistry 结构，base 结构定义了注册器基础的功能函数，比如 Registry、Subscribe 等，但在这些默认定义的函数内部，还是会调用 facade 层（zkRegistry 层）的具体实现函数，这一设计模型能在保证已有功能函数不需要重复定义的同时，引入外层函数的实现，类似于结构体继承却又复用了代码。这一设计模式值得学习。&lt;/p>
&lt;p>我们查看上述 registry/protocol/protocol.go:: Export() 函数，直接调用了:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 1. 通过zk注册器，调用Register()函数，将已有@root@rawurl注册到zk上
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> err &lt;span style="color:#719e07">:=&lt;/span> reg.&lt;span style="color:#268bd2">Register&lt;/span>(&lt;span style="color:#719e07">*&lt;/span>registeredProviderUrl)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>将已有 RegistryUrl 注册到了 zkRegistry 上。&lt;/p>
&lt;p>这一步调用了 baseRegistry 的 Register 函数，进而调用 zkRegister 的 DoRegister 函数，进而调用：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p14.png" alt="">&lt;/p>
&lt;p>在这个函数里，将对应 root 创造一个新的节点。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p15.png" alt="">&lt;/p>
&lt;p>并且写入具体 node 信息，node 为 url 经过 encode 的结果，&lt;strong>包含了服务端的调用方式。&lt;/strong>&lt;/p>
&lt;p>这部分的代码较为复杂，具体可以看 baseRegistry 的 processURL() 函数：http://t.tb.cn/6Xje4bijnsIDNaSmyPc4Ot。&lt;/p>
&lt;p>至此，将服务端调用 url 注册到了 zookeeper 上，而客户端如果想获取到这个 url，只需要传入特定的 dubboPath，向 zk 请求即可。目前 client 是可以获取到访问方式了，但服务端的特定服务还没有启动，还没有开启特定协议端口的监听，这也是 registry/protocol/protocol.go:: Export() 函数接下来要做的事情。&lt;/p>
&lt;h4 id="4proxy_invoker-封装入-wrapped_invoker得到-filter-调用链">4）proxy_invoker 封装入 wrapped_invoker，得到 filter 调用链&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// invoker封装入warppedInvoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> wrappedInvoker &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#268bd2">newWrappedInvoker&lt;/span>(invoker, providerUrl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 经过为invoker增加filter调用链，再使用dubbo协议Export，开启service并且返回了Exporter 。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// export_1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> cachedExporter = extension.&lt;span style="color:#268bd2">GetProtocol&lt;/span>(protocolwrapper.FILTER).&lt;span style="color:#268bd2">Export&lt;/span>(wrappedInvoker)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>新建一个 WrappedInvoker，用于之后链式调用。&lt;/p>
&lt;p>拿到提前实现并注册好的 ProtocolFilterWrapper，调用 Export 方法，进一步暴露。&lt;/p>
&lt;blockquote>
&lt;p>protocol/protocolwrapped/protocol_filter_wrapper.go:Export()&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p16.png" alt="">&lt;/p>
&lt;blockquote>
&lt;p>protocol/protocolwrapped/protocol_filter_wrapper.go:buildInvokerChain&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p17.png" alt="">&lt;/p>
&lt;p>可见，根据配置的内容，通过链式调用的构造，将 proxy_invoker 层层包裹在调用链的最底部，最终返回一个调用链 invoker。&lt;/p>
&lt;p>对应图中部分：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p18.png" alt="">&lt;/p>
&lt;p>至此，我们已经拿到 filter 调用链，期待将这个 chain 暴露到特定端口，用于相应请求事件。&lt;/p>
&lt;h4 id="5通过-dubbo-协议暴露-wrapped_invoker">5）通过 dubbo 协议暴露 wrapped_invoker&lt;/h4>
&lt;blockquote>
&lt;p>protocol/protocolwrapped/protocol_filter_wrapper.go:Export()&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 通过dubbo协议Export dubbo_protocol调用的 export_2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> pfw.protocol.&lt;span style="color:#268bd2">Export&lt;/span>(invoker)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>回到上述 Export 函数的最后一行，调用了 dubboProtocol 的 Export 方法，将上述 chain 真正暴露。&lt;/p>
&lt;p>该 Export 方法的具体实现在：protocol/dubbo/dubbo_protocol.go: Export()。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p19.png" alt="">&lt;/p>
&lt;p>这一函数做了两个事情：构造触发器、启动服务。&lt;/p>
&lt;ul>
&lt;li>将传入的 Invoker 调用 chain 进一步封装，封装成一个 exporter，再将这个 export 放入 map 保存。&lt;strong>注意！这里把 exporter 放入了 SetExporterMap中，在下面服务启动的时候，会以注册事件监听器的形式将这个 exporter 取出！&lt;/strong>&lt;/li>
&lt;li>调用 dubboProtocol 的 openServer 方法，开启一个针对特定端口的监听。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p20.png" alt="">&lt;/p>
&lt;p>如上图所示，一个 Session 被传入，开启对应端口的事件监听。&lt;/p>
&lt;p>至此构造出了 exporter，完成图中部分：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p21.png" alt="">&lt;/p>
&lt;h3 id="4-注册触发动作">4. 注册触发动作&lt;/h3>
&lt;p>上述只是启动了服务，但还没有看到触发事件的细节，点进上面的 s.newSession 可以看到，dubbo 协议为一个 getty 的 session 默认使用了如下配置：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p22.png" alt="">&lt;/p>
&lt;p>其中很重要的一个配置是 EventListener，传入的是 dubboServer 的默认 rpcHandler。&lt;/p>
&lt;blockquote>
&lt;p>protocol/dubbo/listener.go:OnMessage()&lt;/p>
&lt;/blockquote>
&lt;p>rpcHandler 有一个实现好的 OnMessage 函数，根据 getty 的 API，当 client 调用该端口时，会触发 OnMessage。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OnMessage notified when RPC server session got any message in connection
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> (h &lt;span style="color:#719e07">*&lt;/span>RpcServerHandler) &lt;span style="color:#268bd2">OnMessage&lt;/span>(session getty.Session, pkg &lt;span style="color:#268bd2">interface&lt;/span>{}) {
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这一函数实现了在 getty session 接收到 rpc 调用后的一系列处理：&lt;/p>
&lt;ul>
&lt;li>传入包的解析&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p23.png" alt="">&lt;/p>
&lt;ul>
&lt;li>根据请求包构造请求 url&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p24.png" alt="">&lt;/p>
&lt;ul>
&lt;li>拿到对应请求 key，找到要被调用的 exporter&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p25.png" alt="">&lt;/p>
&lt;ul>
&lt;li>拿到对应的 Invoker&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p26.png" alt="">&lt;/p>
&lt;ul>
&lt;li>构造 invocation&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p27.png" alt="">&lt;/p>
&lt;ul>
&lt;li>调用&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p28.png" alt="">&lt;/p>
&lt;ul>
&lt;li>返回&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/code1/p29.png" alt="">&lt;/p>
&lt;p>整个被调过程一气呵成。实现了从 getty.Session 的调用事件，到经过层层封装的 invoker 的调用。&lt;/p>
&lt;p>至此，一次 rpc 调用得以正确返回。&lt;/p>
&lt;h2 id="小结">小结&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>关于 Invoker 的层层封装&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>能把一次调用抽象成一次 invoke；能把一个协议抽象成针对 invoke 的封装；能把针对一次 invoke 所做出的特定改变封装到 invoke 函数内部，可以降低模块之间的耦合性。层层封装逻辑更加清晰。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>关于 URL 的抽象&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>关于 dubbo 的统一化请求对象 URL 的极度抽象是之前没有见过的&amp;hellip; 个人认为这样封装能保证请求参数列表的简化和一致。但在开发的过程中，滥用极度抽象的接口可能造成&amp;hellip; debug 的困难？以及不知道哪些字段是当前已经封装好的，哪些字段是无用的。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>关于协议的理解&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>之前理解的协议还是太过具体化了，而关于 dubbo-go 对于 dubboProtocol 的协议，我认为是基于 getty 的进一步封装，它定义了客户端和服务端，对于 getty 的 session 应该有哪些特定的操作，从而保证主调和被调的协议一致性，而这种保证也是一种协议的体现，是由 dubbo 协议来规范的。&lt;/p>
&lt;p>如果你有任何疑问，欢迎钉钉扫码加入交流群：钉钉群号 23331795！&lt;/p>
&lt;blockquote>
&lt;p>作者简介 &lt;strong>李志信&lt;/strong> (GitHubID LaurenceLiZhixin)，中山大学软件工程专业在校学生，擅长使用 Java/Go 语言，专注于云原生和微服务等技术方向&lt;/p>
&lt;/blockquote></description></item><item><title>Blog: dubbo-go 中 REST 协议实现</title><link>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-%E4%B8%AD-rest-%E5%8D%8F%E8%AE%AE%E5%AE%9E%E7%8E%B0/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-%E4%B8%AD-rest-%E5%8D%8F%E8%AE%AE%E5%AE%9E%E7%8E%B0/</guid><description>
&lt;h2 id="什么是-rest-协议">什么是 REST 协议&lt;/h2>
&lt;p>REST 是 &lt;strong>RE&lt;/strong>presentational &lt;strong>S&lt;/strong>tate &lt;strong>T&lt;/strong>ransfer（表述性状态转移）的简写，是一种软件架构风格。虽然 REST 架构风格不是一定要基于 HTTP 协议进行传输，但是因为 HTTP
协议的通用性和易用性，现在越来越多的 web 服务采用基于 HTTP 协议实现 RESTful 架构。&lt;/p>
&lt;p>在 dubbo-go 中的 REST 协议指的是一种基于 HTTP 协议的远程调用方式。简单的来讲，REST 协议就是把dubbo 服务发布成 RESTful 风格的 HTTP 接口并且能够能像调用 dubbo 接口一样的方式调用 HTTP
接口。&lt;/p>
&lt;h2 id="为什么要支持-rest-协议">为什么要支持 REST 协议&lt;/h2>
&lt;p>在没有 REST 协议之前，小伙伴们是否一直在苦恼这样几个问题：&lt;/p>
&lt;ol>
&lt;li>传统的 web 服务不能直接调用我们发布的 dubbo 服务&lt;/li>
&lt;li>前端不能直接调用 dubbo 服务&lt;/li>
&lt;li>dubbo 服务不能发布 Open API&lt;/li>
&lt;/ol>
&lt;p>上述问题，就是 REST 协议解决的核心问题。现在我们很多应用场景都是需要与异构的系统进行交互，而 REST 采用的 HTTP 通信协议非常适合用来打通异构系统，如图：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/rest/rest-call.webp" alt="">&lt;/p>
&lt;h2 id="rest-协议没那么简单">REST 协议没那么简单&lt;/h2>
&lt;p>REST 协议核心要解决一个问题：&lt;strong>Go 方法到 HTTP 接口的双向映射&lt;/strong>。普通 HTTP 调用 dubbo-go 服务，考虑的是HTTP 到 &lt;strong>Go&lt;/strong> 方法的映射；而 dubbo-go 服务调用 HTTP 服务，则是 **
Go** 方法到 HTTP 接口的映射。&lt;/p>
&lt;p>下面是我们要与 &lt;strong>Go&lt;/strong> 方法要做映射的 HTTP 请求协议内容：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>POST /path/{pathParam}?queryParam=1 HTTP/1.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Accept: application/json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Content-Type: application/json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Host: http://localhost:8080
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{&amp;#34;id&amp;#34;:1111}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在服务提供方，当上面这种请求发送到服务器时，我们要把它路由到下面这个 &lt;strong>Go&lt;/strong> 方法中，在服务消费方，我们也可以通过调用下面的 &lt;strong>Go&lt;/strong> 方法把方法参数转化为上面的HTTP请求：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> Provider &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 该方法应该对应上面的http请求
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GetResult &lt;span style="color:#268bd2">func&lt;/span>(pathParam &lt;span style="color:#dc322f">string&lt;/span>, queryParam &lt;span style="color:#dc322f">string&lt;/span>, body &lt;span style="color:#268bd2">interface&lt;/span>{}, host &lt;span style="color:#dc322f">string&lt;/span>) (&lt;span style="color:#719e07">*&lt;/span>Result, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在消费方调用 &lt;code>GetResult&lt;/code> 时，传入各个参数的值：&lt;/p>
&lt;ul>
&lt;li>变量 pathParam 的内容应该是字符串 &amp;ldquo;pathParam&amp;rdquo;；&lt;/li>
&lt;li>变量 queryParam 的内容应该是字符串 &amp;ldquo;1&amp;rdquo; ；&lt;/li>
&lt;li>变量 body 应该是有以字符串 &amp;ldquo;id&amp;rdquo; 为 key ，1111 为 value 的一个map；&lt;/li>
&lt;li>当然 host 变量的内容应该是字符串 &amp;ldquo;http://localhost:8080&amp;rdquo; 。&lt;/li>
&lt;/ul>
&lt;p>在服务端执行 &lt;code>GetResult&lt;/code> 方法时，得到的参数会与消费方调用时传入的值相同。&lt;/p>
&lt;p>总结下来，我们要建立以下这些映射关系&lt;/p>
&lt;ol>
&lt;li>路径映射&lt;/li>
&lt;li>Header 处理（固定 Header 和 Header 值也是参数两种情况）&lt;/li>
&lt;li>POST or GET or &amp;hellip;（HTTP 方法映射）&lt;/li>
&lt;li>参数映射&lt;/li>
&lt;/ol>
&lt;p>要完成这种映射，我们首先要解决的是，如何知道这种映射关系？&lt;/p>
&lt;p>答案只有一个，通过用户配置。而用户配置所面临的困难是，复杂且琐碎。（解决思路是提供大量默认配置减轻配置的负担，自定义配置方式允许用户使用自己熟悉的配置形式）&lt;/p>
&lt;p>另外一个难点在于，使用何种 web 框架的问题。有些公司内部使用的是自研的 web 框架，他们有成熟的技术基础和运维能力。于是就会考虑说，能不能让 dubbo-go 在支持 REST 协议的时候，能够让他们将 REST 协议使用的web
框架替换成他们所期望的呢？&lt;/p>
&lt;h2 id="如何建立http接口与方法的映射关系">如何建立HTTP接口与方法的映射关系&lt;/h2>
&lt;p>下面我举一个HTTP接口与方法映射的具体例子：&lt;/p>
&lt;p>&lt;strong>Go&lt;/strong> 结构体定义如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> UserProvider &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> GetUser &lt;span style="color:#268bd2">func&lt;/span>(id &lt;span style="color:#dc322f">string&lt;/span>, name &lt;span style="color:#dc322f">string&lt;/span>, age &lt;span style="color:#dc322f">int&lt;/span>) (&lt;span style="color:#719e07">*&lt;/span>User, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>要发布的HTTP接口形式是： http://127.0.0.1/UserProvider/GetUser/{id}?name=test&amp;amp;age=1&lt;/p>
&lt;p>服务端配置如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">services&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;UserProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> //注册中心
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> //启用REST协议
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;rest&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> //DUBBO的接口名
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.ikurento.user.UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 服务接口路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rest_path&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;/UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;GetUser&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 方法接口路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rest_path&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;/GetUser/{id}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // HTTP方法
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rest_method&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;GET&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // HTTP查询参数
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rest_query_params&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;1:name,2:age&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // HTTP路径参数
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rest_path_params&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;0:id&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 可以提供的内容类型
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rest_produces&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;application/json;application/xml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 可以接受的客户端参数类型
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rest_consumes&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;application/json;charset=utf-8,*/*&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // HTTP Body
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rest_body&lt;/span>: -&lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在配置文件中我们定义了方法的路径，HTTP方法等接口参数，这里需要注意的是路径参数和查询参数的配置方式，0:name 的意思是查询参数 name 对应 &lt;code>GetUser&lt;/code> 方法的第一个参数，还有 rest_body
配置的数字也是对应这方法的参数，这里没有 body 参数所有就配置了 &lt;code>-1&lt;/code>。&lt;/p>
&lt;h2 id="rest协议的调用过程">REST协议的调用过程&lt;/h2>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/rest/rest-call-process.webp" alt="">&lt;/p>
&lt;p>上图展示了用户在 Consumer 端调用 &lt;code>GetUser&lt;/code> 方法到 Provdier 端 &lt;code>GetUser&lt;/code> 方法被执行的整个过程，在 &lt;code>RestClient&lt;/code> 和 &lt;code>RestServer&lt;/code> 中分别**实现了 Go 方法参数到 HTTP
请求的转换和 HTTP 请求到 Go 方法的转换，这是最为核心和复杂的部分。**换言之，我们在这里实现了前面提到的 Go 方法和 HTTP 请求的双向映射。&lt;/p>
&lt;p>这里我们可以注意到 &lt;code>RestClient&lt;/code> 和 &lt;code>RestServer&lt;/code> 是可以用户自行扩展的，下面我将具体介绍一下在REST协议中有哪些扩展点设计。&lt;/p>
&lt;h2 id="rest协议的扩展点设计">REST协议的扩展点设计&lt;/h2>
&lt;p>基于 dubbo-go 良好的 extension 扩展设计，我们定义了多个扩展点，用户可以自定义功能实现。&lt;/p>
&lt;h3 id="自定义http服务器">自定义HTTP服务器&lt;/h3>
&lt;p>RestServer的扩展接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> RestServer &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// sever启动函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Start&lt;/span>(url common.URL)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 发布接口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Deploy&lt;/span>(restMethodConfig &lt;span style="color:#719e07">*&lt;/span>rest_config.RestMethodConfig, routeFunc &lt;span style="color:#268bd2">func&lt;/span>(request RestServerRequest, response RestServerResponse))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 删除接口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">UnDeploy&lt;/span>(restMethodConfig &lt;span style="color:#719e07">*&lt;/span>rest_config.RestMethodConfig)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// server关闭
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Destroy&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 dubbo-go 的 v1.4.0 中默认使用 go-restful 作为 HTTP 服务器，如果用户想用其他 HTTP 容器可以实现上面的接口，并在配置文件中配置使用自己自定义的服务器。&lt;/p>
&lt;p>这个接口中，最核心的方法是 Deploy，在 restMethodConfig 方法参数中有用户配置的接口路径等一系列参数，routeFunc 是 HTTP 接口需要被路由执行的函数。不同的http服务器会有不同的 request 和
response ，所以我们定义了 &lt;code>RestServerRequest&lt;/code> 接口和 &lt;code>RestServerResponse&lt;/code> 接口让用户进行适配。&lt;/p>
&lt;h3 id="自定义http客户端">自定义HTTP客户端&lt;/h3>
&lt;p>RestClient 的扩展接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// RestOptions
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> RestOptions &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RequestTimeout time.Duration
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConnectTimeout time.Duration
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// RestClientRequest
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> RestClientRequest &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Header http.Header
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Location &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Path &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Method &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> PathParams &lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]&lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> QueryParams &lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]&lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Body &lt;span style="color:#268bd2">interface&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// RestClient user can implement this client interface to send request
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> RestClient &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">Do&lt;/span>(request &lt;span style="color:#719e07">*&lt;/span>RestClientRequest, res &lt;span style="color:#268bd2">interface&lt;/span>{}) &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>最后的请求到客户端时，都会被封装为 &lt;code>RestRequest&lt;/code>，用户可以非常简单快速的扩展自己的 Client 端。&lt;code>RestOptions&lt;/code> 中有一些客户端的超时配置，在创建自己的客户端时需要根据这些配置初始化客户端。&lt;/p>
&lt;h3 id="自定义-rest-配置形式">自定义 REST 配置形式&lt;/h3>
&lt;p>前面提到，REST 协议一个很麻烦的地方在于，配置很繁琐很琐碎。Go 不同于 Java，可以通过注解的形式来简化配置。&lt;/p>
&lt;p>所以我们考虑到用户不同的使用习惯和公司的配置风格，提供了这个扩展点。&lt;/p>
&lt;p>ConfigReader 的扩展接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> ConfigReader &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Consumer配置读取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">ReadConsumerConfig&lt;/span>(reader &lt;span style="color:#719e07">*&lt;/span>bytes.Buffer) &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Provider配置读取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">ReadProviderConfig&lt;/span>(reader &lt;span style="color:#719e07">*&lt;/span>bytes.Buffer) &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>ReadConsumerConfig&lt;/code> 和 &lt;code>ReadProviderConfig&lt;/code> 方法的参数是配置文件的文件流，在实现方法中可以再次解析，也可以使用二次编译或者硬编码方式等其他方式读取配置。这是一个通用的配置读取接口，以后可以用来扩展
REST 配置之外的其他配置，所以需要在方法中调用方法设置配置，如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 设置Rest的消费者配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>config.&lt;span style="color:#268bd2">SetRestConsumerServiceConfigMap&lt;/span>(restConsumerServiceConfigMap)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 设置Rest的提供者配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>config.&lt;span style="color:#268bd2">SetRestProviderServiceConfigMap&lt;/span>(restProviderServiceConfigMap)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="如何添加-http-过滤器">如何添加 HTTP 过滤器&lt;/h2>
&lt;p>因为不同 HTTP 服务器的过滤器，拦截器或者是 middleware 添加方式都不同，所以我们很难定义一个接口满足所有服务器。因此我们单独为 go-restful 定义了一个添加 filter 的方法，这里我们需要注意的一点是必须在 REST 接口发布前添加filter。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>server_impl.&lt;span style="color:#268bd2">AddGoRestfulServerFilter&lt;/span>(&lt;span style="color:#268bd2">func&lt;/span>(request &lt;span style="color:#719e07">*&lt;/span>restful.Request, response &lt;span style="color:#719e07">*&lt;/span>restful.Response, chain &lt;span style="color:#719e07">*&lt;/span>restful.FilterChain) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 鉴权等功能
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> chain.&lt;span style="color:#268bd2">ProcessFilter&lt;/span>(request, response)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 启动dubbo服务，发布rest等接口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="展望">展望&lt;/h2>
&lt;p>以上是关于 REST 协议的一些介绍，具体的实现我就不详细讲了，大家可以去参阅源码。&lt;/p>
&lt;p>如果想看具体的Example，请参考：&lt;/p>
&lt;p>&lt;a href="https://github.com/dubbogo/dubbo-samples/tree/master/golang/general/rest">https://github.com/dubbogo/dubbo-samples/tree/master/golang/general/rest&lt;/a>&lt;/p>
&lt;p>REST 未来需要支持 HTTPS 协议和基于 open tracing 标准 api 的链路追踪。REST 的配置信息未来也不是 REST协议独有的，这些配置信息未来可以作为每个 dubbo 接口的元数据，存储到元数据中心，为网关提供
HTTP 协议与 dubbo 协议之间的映射关系。&lt;/p>
&lt;blockquote>
&lt;p>作者：蒋超，github id Patrick0308，在 杭州贝安云科技有限公司 任职服务开发工程师。&lt;/p>
&lt;/blockquote></description></item><item><title>Blog: dubbo-go 中将 Kubernets 原⽣作为注册中⼼的设计和实现</title><link>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-%E4%B8%AD%E5%B0%86-kubernets-%E5%8E%9F%E4%BD%9C%E4%B8%BA%E6%B3%A8%E5%86%8C%E4%B8%AD%E7%9A%84%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%AE%9E%E7%8E%B0/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2021/01/14/dubbo-go-%E4%B8%AD%E5%B0%86-kubernets-%E5%8E%9F%E4%BD%9C%E4%B8%BA%E6%B3%A8%E5%86%8C%E4%B8%AD%E7%9A%84%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%AE%9E%E7%8E%B0/</guid><description>
&lt;p>今天这篇⽂章将会介绍 dubbo-go 将 Kubernetes 作为注册中⼼的服务注册的初衷、设计⽅案，以及具体实现。&lt;/p>
&lt;p>到⽬前为⽌该⽅案的实现已经被合并到 dubbo-go 的 master 分⽀。具体实现为关于 Kubernetes 的 &lt;a href="https://github.com/apache/dubbo-go/pull/400">PullRequest&lt;/a> 。&lt;/p>
&lt;h2 id="kubernetes管理资源的哲学">Kubernetes管理资源的哲学&lt;/h2>
&lt;p>Kubernetes 作为容器集群化管理⽅案管理资源的维度可主观的分为服务进程管理和服务接⼊管理。&lt;/p>
&lt;ul>
&lt;li>服务实例管理，主要体现⽅式为 Pod 设计模式加控制器模式，控制器保证具有特定标签 （ Kubernetes-Label ）的 Pod 保持在恒定的数量（多删，少补）。&lt;/li>
&lt;li>服务管理，主要为 Kubernetes-Service ，该 Service 默认为具有特定标签（ Kubernetes-Label ）的 Pod 统⼀提供⼀个 VIP（ Kubernetes-ClusterIP ）所有需要请求该组 Pod 的请求都默认会按照 round-robin 的负载策略转发到真正提供服务的 Pod 。并且 CoreDNS 为该 Kubernetes-Service 提供集群内唯⼀的域名。&lt;/li>
&lt;/ul>
&lt;h2 id="kubernetes的服务发现模型">Kubernetes的服务发现模型&lt;/h2>
&lt;p>为了明确 K8s 在服务接入管理提供的解决方案，我们以 kube-apiserver 提供的 API(HTTPS) 服务为例。K8s 集群为该服务分配了一个集群内有效的 ClusterIP ，并通过 CoreDNS 为其分配了唯一的域名 kubernetes 。如果集群内的 Pod 需要访问该服务时直接通过 https://kubernetes:443 即可完成。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/k8s/k8s-service-discovery.png" alt="">&lt;/p>
&lt;p>具体流程如上图所示 ( 红⾊为客户端，绿⾊为 kube-apiserver )：&lt;/p>
&lt;ol>
&lt;li>⾸先客户端通过 CoreDNS 解析域名为 &lt;strong>kubernetes&lt;/strong> 的服务获得对应的 Cluster IP 为 10.96.0.1。&lt;/li>
&lt;li>客户端向 10.96.0.1 发起 HTTP 请求。&lt;/li>
&lt;li>HTTP 请求 kube-proxy 所创建的 IP tables 拦截随机 DNAT 为 10.0.2.16 或者 10.0.2.15 。&lt;/li>
&lt;li>Client 与最终提供服务的 Pod 建⽴连接并交互。&lt;/li>
&lt;/ol>
&lt;p>由此可⻅，Kubernetes 提供的服务发现为域名解析级别。&lt;/p>
&lt;h2 id="dubbo-的服务发现模型">dubbo 的服务发现模型&lt;/h2>
&lt;p>同样为了明确 dubbo 服务发现的模型，以⼀个简单的 dubbo-consumer 发现并访问 Provider 的具体流程为例。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/k8s/dubbo-service-discovery.png" alt="">&lt;/p>
&lt;p>具体流程如上图所示：&lt;/p>
&lt;ol>
&lt;li>Provider 将本进程的元数据注册到 Registry 中，包括 IP，Port，以及服务名称等。&lt;/li>
&lt;li>Consumer 通过 Registry 获取 Provider 的接⼊信息，直接发起请求&lt;/li>
&lt;/ol>
&lt;p>由此可⻅，dubbo 当前的服务发现模型是针对 Endpoint 级别的，并且注册的信息不只 IP 和端⼝包括其他的⼀些元数据。&lt;/p>
&lt;h2 id="无法直接使用-kubernetes-服务发现模型的原因">无法直接使用 Kubernetes 服务发现模型的原因&lt;/h2>
&lt;p>通过上述两个⼩节，答案基本已经⽐较清晰了。总结⼀下，⽆法直接使⽤ Kubernetes 作为注册中⼼的原因主要为以下⼏点:&lt;/p>
&lt;ol>
&lt;li>Kubernetes-Service 标准的资源对象具有的服务描述字段 中并未提供完整的 dubbo 进程元数据字段因此，⽆法直接使⽤Kubernetes-Service 进⾏服务注册与发现。&lt;/li>
&lt;li>dubbo-go 的服务注册是基于每个进程的，每个 dubbo 进程均需进⾏独⽴的注册。&lt;/li>
&lt;li>Kubernetes-Service 默认为服务创建 VIP，提供 round-robin 的负载策略也与 dubbo-go⾃有的 Cluster 模块的负载策略形成了冲突。&lt;/li>
&lt;/ol>
&lt;h2 id="dubbo-go-所采的注册发现案">Dubbo-go 所采⽤的注册/发现⽅案&lt;/h2>
&lt;h3 id="服务注册">服务注册&lt;/h3>
&lt;p>Kubernetes 基于 Service 对象实现服务注册／发现。可是 dubbo 现有⽅案为每个 dubbo-go 进程独⽴注册，因此 dubbo-go选择将该进程具有的独有的元数据写⼊运⾏该 &lt;strong>dubbo-go&lt;/strong> 进程的 &lt;strong>Pod&lt;/strong> 在 &lt;strong>Kubernetes&lt;/strong>中的 &lt;strong>Pod&lt;/strong> 资源对象的描述信息中。每个运⾏ dubbo 进程的 Pod 将本进程的元数据写⼊ Kubernetes-Pod Annotations 字段。为了避免与其他使⽤Annotations 字段的 Operator 或者其他类型的控制器（ Istio ） 的字段冲突。dubbo-go 使⽤ Key 为 &lt;strong>dubbo.io/annotation&lt;/strong> value 为具体存储的 K/V 对的数组的 json 编码后的 base64 编码。&lt;/p>
&lt;p>样例为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Pod
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">annotations&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">dubbo.io/annotation&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>W3siayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY29tLmlrdXJlbnRvLnVzZXIuVXNlcl
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Byb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ZXIvY29uc3VtZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>9tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvcHJvdmlkZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>0uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvY29uc3VtZXJzL2NvbnN1bWVyJTNBJTJGJTJGMTcy
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>LjE3LjAuOCUyRlVzZXJQcm92aWRlciUzRmNhdGVnb3J5JTNEY29uc3VtZXJzJTI2ZHViYm8lM0RkdW
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Jib2dvLWNvbnN1bWVyLTIuNi4wJTI2cHJvdG9jb2wlM0RkdWJibyIsInYiOiIifV0=
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由于每个 dubbo-go 的 Pod 均只负责注册本进程的元数据，因此 Annotations 字段⻓度也不会因为运⾏ dubbo-go 进程的 Pod 数量增加⽽增加。&lt;/p>
&lt;h3 id="服务发现">服务发现&lt;/h3>
&lt;p>依赖kubernetes Api-Server 提供了Watch的功能。可以观察特定namespace内各Pod对象的变化。 dubbo-go为了避免dubbo-go进程watch到与dubbo-go进程⽆关的Pod的变化，dubbo-go将watch的条件限制在当前Pod所在的namespace，以及 watch 具有 Key为&lt;strong>dubbo.io/label&lt;/strong> Value为 &lt;strong>dubbo.io-value&lt;/strong> 的Pod。在Watch到对应Pod的变化后实时更新本地Cache，并通过Registry提供的Subscribe通&lt;/p>
&lt;p>知建⽴在注册中⼼之上的服务集群管理，或者其他功能。&lt;/p>
&lt;h3 id="总体设计图">总体设计图&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-go/k8s/design.png" alt="">&lt;/p>
&lt;p>具体流程如上图所示：&lt;/p>
&lt;ol>
&lt;li>启动 dubbo-go 的 Deployment 或其他类型控制器使⽤ Kubernetes Downward-Api 将本 Pod 所在 namespace 通过环境变量的形式注⼊ dubbo-go 进程。&lt;/li>
&lt;li>dubbo-go 进程的 Pod 启动后通过环境变量获得当前的 namespace 以及该 Pod 名称，调⽤ Kubernetes-Apiserver PATCH 功能为本 Pod 添加 Key 为 &lt;strong>dubbo.io/label&lt;/strong> Value为 &lt;strong>dubbo.io-value&lt;/strong>的label。&lt;/li>
&lt;li>dubbo-go 进程调⽤ Kubernetes-Apiserver 将本进程的元数据通过 PATCH 接⼝写⼊当前 Pod 的 Annotations 字段。&lt;/li>
&lt;li>dubbo-go 进程 LIST 当前 namespace 下其他具有同样标签的 Pod，并解码对应的 Annotations 字段 获取其他 Pod 的信息。&lt;/li>
&lt;li>dubbo-go 进程 WATCH 当前 namespace 下其他具有同样标签的 Pod 的 Annotations 的字段变化。&lt;/li>
&lt;/ol>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>K8s 已经为其承载的服务提供了一套服务发现，服务注册，以及服务集群管理机制。而 dubbo-go 的同时也拥有自成体系的服务集群管理。这两个功能点形成了冲突，在无法调谐两者的情况， dubbo-go 团队决定保持 dubbo 自有的服务集群管理系，而选择性的放弃了 Service 功能，将元数据直接写入到 Pod 对象的 Annotations 中。&lt;/p>
&lt;p>当然这只是 dubbo-go 在将 K8s 作为服务注册中心的方案之一，后续社区会以更加“云原生”的形式对接 K8s ，让我们拭目以待吧。&lt;/p>
&lt;p>dubbo-go 社区钉钉群 :23331795 ,欢迎你的加入。&lt;/p>
&lt;blockquote>
&lt;p>作者信息： 王翔，GithubID: sxllwx，就职于成都达闼科技有限公司，golang开发工程师。&lt;/p>
&lt;/blockquote></description></item></channel></rss>