| <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – 源码解析</title><link>https://dubbo.apache.org/zh-cn/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</link><description>Recent content in 源码解析 on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 28 Apr 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://dubbo.apache.org/zh-cn/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: 引言</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</guid><description> |
| <h2 id="引言">引言</h2> |
| <p>服务指标统计体系是 Dubbo 可观测能力的重要组成部分。 |
| dubbo-metrics 指标模块旨在将 dubbo 内部零散的 Metrics 相关类综合到一个单独的模块中,提供一套更加完善、全面、可拓展、解耦合的指标采样-统计-导出解决方案。</p> |
| <p>dubbo-metrics 模块包括:</p> |
| <ul> |
| <li>dubbo-metrics-api 公用接口包</li> |
| <li>dubbo-metrics-prometheus 普罗米修斯适配包</li> |
| <li>dubbo-metrics-metadata 元数据中心指标监控包</li> |
| <li>dubbo-metrics-registry 注册中心指标监控包</li> |
| <li>dubbo-metrics-config-center 配置中心指标监控包</li> |
| <li>dubbo-metrics-default 接口默认实现包,提供dubbo内部核心指标的监控功能</li> |
| </ul> |
| <p>在设计上,dubbo-metrics 深入应用事件驱动编程思想,总体体现出下图的事件处理链路:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/metrics-event-struct.png" alt="metrics-event-struct"></p> |
| <p>在拓展上,dubbo-metrics 抽象了一套指标导出接口与抽象实现,可实现兼容多种指标统计监控中心,默认提供了普罗米修斯实现。</p></description></item><item><title>Blog: 1-指标样本的收集与存储</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</guid><description> |
| <h2 id="一指标样本的收集与存储">一、指标样本的收集与存储</h2> |
| <h3 id="指标样本收集">指标样本收集</h3> |
| <p><strong>指标收集器(Collector)是指标对外导出的入口</strong>。最终导出到指标统计中心的指标采样实际均直接来源于各个指标采样器。因此,我们将从各个收集器实现开始,分析 dubbo-metrics 模块是如何工作的。</p> |
| <p>指标收集操作定义在 MetricsCollector (指标采集器,SPI)接口中,可以通过它的实现收集某一类的指标样本(MetricSample)。它主要有以下实现,对应着不同类型的指标:</p> |
| <ul> |
| <li>ConfigCenterMetricsCollector <strong>配置中心操作相关指标收集器</strong> ,收集配置信息的变化次数</li> |
| <li>MetadataMetricsCollector <strong>元数据中心操作相关指标收集器</strong>,收集提供者、消费者对元数据中心操作(推送数据、拉取数据)情况的计数、耗时统计。</li> |
| <li>RegistryMetricsCollector <strong>注册中心相关操作指标收集器</strong>,收集应用级、接口级服务注册成功、失败、耗时的相关计数。</li> |
| <li>DefaultMetricsCollector <strong>默认指标收集器</strong>,内置多种采样器来完成不同类型的内部指标采样。</li> |
| <li>HistogramMetricsCollector <strong>直方图指标收集器</strong>,利用 micrometer API 处理直方图类型的指标,它的实现较为特殊。</li> |
| </ul> |
| <p>配置中心 、元数据、服务注册及默认指标收集器均实现自混合指标收集器(CombMetricsCollector)。混合指标收集器实现了 ApplicationMetricsCollector 、ServiceMetricsCollector 、MethodMetricsCollector 三个接口(定义按应用名收集、按应用名-服务名收集和按应用-方法名收集指标的操作),因此它们可以进行应用、服务和方法三个层面的指标收集工作。</p> |
| <p>默认指标收集器的特点是通过内部的指标采样器(MetricsSampler)完成指标事件的处理操作,而不是其它收集器的指标监听器(MetricsListener)</p> |
| <p>直方图指标收集器则负责收集直方图类型的指标。它利用直方图度量寄存器(HistogramMetricRegister)借助 micrometer API 完成直方图样本的采集。直方图类型包括百分位数、服务水平目标、最小预期值、最大预期值、统计数据分布有效期等。</p> |
| <p><strong>Collector的继承关系:</strong></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/collectors.png" alt="collectors"></p> |
| <p>可以看出,每个指标收集器都具有来自 MetricsListener 的监听指标事件的能力。为什么指标收集器本身需要监听指标事件? 在后文中,我们将讨论指标收集器是如何利用内置的子转发器(SubDispatcher)转发指标事件,并完成计数处理的。</p> |
| <h3 id="指标样本存储">指标样本存储</h3> |
| <p>前文中,我们了解了指标收集的入口是指标收集器(Collector)。那么各个收集器从哪里收集指标样本?</p> |
| <p><strong>对于配置中心、元数据中心、 注册中心的指标收集器:</strong></p> |
| <p>它们分别负责采集三大中心模块的指标,均继承于<strong>混合数据收集器(CombMetricsCollector)</strong>,而混合数据收集器中实现了 export 方法 。</p> |
| <p>混合数据收集器内部有一个<strong>基本数据聚合器(BaseStatComposite)</strong>,它实现了 MetricsExport 接口,该接口定义了指标导出操作,混合数据收集器则利用它的 export 方法导出指标。</p> |
| <p>基本数据聚合器是一个抽象类,内有三个属性:ApplicationStatComposite 、ServiceStatComposite 和 RtStatComposite 。它们的作用:</p> |
| <ul> |
| <li><strong>ApplicationStatComposite 应用数据聚合器</strong>,应用程序级别相关事件的计数 ,根据指标Key( MetricsKey )和应用名统计指标,提供计数递增操作</li> |
| <li><strong>ServiceStatComposite 服务数据聚合器</strong>,服务级别相关事件的计数,根据指标Key、应用名和服务名统计指标,提供计数递增操作</li> |
| <li><strong>MethodStatComposite 方法数据聚合器</strong>,方法级别相关事件的计数,存储各方法RPC调用相关计数。</li> |
| <li><strong>RtStatComposite,Rt(Response Time,响应时间)数据聚合器</strong>,包括应用级别和服务级别。根据应用名、服务名、注册的指标名及相应相应时间统计指标,提供添加操作。</li> |
| </ul> |
| <p>对于以上四个聚合器,他们的职责就是存储某一类型的采样样本。</p> |
| <p>** 基本数据聚合器 (BaseStatComposite)** 对这三个子聚合器的操作进行了简单整合,统一提供给外界。<strong>而混合指标收集器(CombMetricsCollector)</strong> 也基本保留了内部基本数据聚合器的所有操作,将其封装为 <code>increment</code>、<code>setNum</code>、<code>addRt </code>三个方法(及它们的重载,分别收集应用级数据和服务级数据)向上提供。外部组件可以直接调用这些收集器完成指标更新操作。</p> |
| <p><strong>当调用元数据中心指标收集器、注册中心指标收集器的 collect 方法时,最终会调用<code>BaseStatComposite.export(MetricsCategory category)</code> , 该方法会收集内部三个聚合器的指标并返回。</strong></p> |
| <p>需要注意的是, 配置中心指标收集器不依赖于基本数据聚合器 导出数据,它在创建时将基本数据聚合器置为null,而使用自己的 updatedMetrics 字段存储采样:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ConfigCenterMetricsCollector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>ConfigCenterMetric<span style="color:#719e07">,</span> AtomicLong<span style="color:#719e07">&gt;</span> updatedMetrics <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ConfigCenterMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//BaseStatComposite = null |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span><span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>混合指标收集器和数据聚合器之间呈现如下的包含关系:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/composite-struct.png" alt="composite-struct"></p> |
| <p><strong>DefaultMetricsCollector 默认指标采集器:</strong></p> |
| <p>它不直接存储采样数据,而是通过收集其下**指标采样器(MetricsSampler)**的样本来完成采样工作。这些采样器包括:</p> |
| <ul> |
| <li>方法采样器</li> |
| <li>应用采样器</li> |
| <li>线程池采样器</li> |
| </ul> |
| <p>这些采样器完成采样后,还会利用采集器中的**事件多播器(Multicaster)**将指标事件发布出去,可以被其它监听器处理。详细流程将在后文中探讨。</p> |
| <p><strong>HistogramMetricsCollector 直方图指标采集器:</strong></p> |
| <p>由于需要使用Timer完成直方图属性的统计,它使用自己的容器存储采样数据。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">HistogramMetricsCollector</span> <span style="color:#268bd2">implements</span> MetricsListener <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//方法指标样本与对应的Timer |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> ConcurrentHashMap<span style="color:#719e07">&lt;</span>MethodMetric<span style="color:#719e07">,</span> Timer<span style="color:#719e07">&gt;</span> rt <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>Timer(计时器)由 micrometer API 提供,常用于统计一分钟内的大量事件。</p></description></item><item><title>Blog: 2-指标收集器的指标采集流程</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</guid><description> |
| <h2 id="二指标收集器的指标采集流程">二、指标收集器的指标采集流程</h2> |
| <p>在前文中,我们了解了指标收集器(Collector)最终收集的数据只有三个来源:</p> |
| <ul> |
| <li> |
| <p>实现自混合指标收集器(CombMetricsCollector) 的元数据指标收集器(MetadataMetricsCollector)和注册中心指标收集器(RegistryMetricsCollector),它们的样本均存储在内置的基本数据聚合器中。具体来说,是基本数据聚合器下的四个子数据聚合器中:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/composite-struct.png" alt="composite-struct"></p> |
| </li> |
| <li> |
| <p><strong>DefaultMetricsCollector 默认指标收集器</strong>,它的样本不仅来自于指标事件,还来自其下**采样器(Sampler)**中,用于Dubbo核心模块的采样。</p> |
| </li> |
| <li> |
| <p><strong>HistogramMetricsCollector 直方图指标收集器</strong>,由于采样数据的特殊性,它的样本直接以 Map 存储在内部。</p> |
| </li> |
| </ul> |
| <p>接下来,我们需要明确它们存储的指标是如何添加进去的。</p> |
| <h3 id="1服务治理模块的指标采集流程">1,服务治理模块的指标采集流程</h3> |
| <p>通过之前的分析,我们知道服务治理模块的指标采集器均实现自混合指标收集器(CombMetricsCollector)。它对基本数据聚合器(BaseStatComposite) 的大部分方法做了封装。基本数据聚合器又封装了四个负责存储不同类型指标采样的子聚合器。</p> |
| <p>这四个子聚合器包括:</p> |
| <ul> |
| <li>ApplicationStatComposite</li> |
| <li>ServiceStatComposite</li> |
| <li>MethodStatComposite</li> |
| <li>RtStatComposite</li> |
| </ul> |
| <p>实际上,<strong>元数据、注册中心指标收集器</strong>更新、添加指标的操作都是通过混合指标收集器暴露的方法进行。而具体的,是通过 <code>setNum</code>、<code>increment</code>、<code>addRt</code> 这三个方法(及它们的重载)进行操作。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//CombMetricsCollector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> BaseStatComposite stats<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setNum</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> String applicationName<span style="color:#719e07">,</span> String serviceKey<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> num<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>stats<span style="color:#719e07">.</span>setServiceKey<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> applicationName<span style="color:#719e07">,</span> serviceKey<span style="color:#719e07">,</span> num<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">increment</span><span style="color:#719e07">(</span>String applicationName<span style="color:#719e07">,</span> MetricsKey metricsKey<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>stats<span style="color:#719e07">.</span>incrementApp<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> applicationName<span style="color:#719e07">,</span> SELF_INCREMENT_SIZE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">increment</span><span style="color:#719e07">(</span>String applicationName<span style="color:#719e07">,</span> String serviceKey<span style="color:#719e07">,</span> MetricsKey metricsKey<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> size<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>stats<span style="color:#719e07">.</span>incrementServiceKey<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> applicationName<span style="color:#719e07">,</span> serviceKey<span style="color:#719e07">,</span> size<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addRt</span><span style="color:#719e07">(</span>String applicationName<span style="color:#719e07">,</span> String registryOpType<span style="color:#719e07">,</span> Long responseTime<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> stats<span style="color:#719e07">.</span>calcApplicationRt<span style="color:#719e07">(</span>applicationName<span style="color:#719e07">,</span> registryOpType<span style="color:#719e07">,</span> responseTime<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addRt</span><span style="color:#719e07">(</span>String applicationName<span style="color:#719e07">,</span> String serviceKey<span style="color:#719e07">,</span> String registryOpType<span style="color:#719e07">,</span> Long responseTime<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> stats<span style="color:#719e07">.</span>calcServiceKeyRt<span style="color:#719e07">(</span>applicationName<span style="color:#719e07">,</span> serviceKey<span style="color:#719e07">,</span> registryOpType<span style="color:#719e07">,</span> responseTime<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span></code></pre></div><p>由于几个方法实际上的调用链路类似,我们选择从其中的 setNum 方法开始分析。</p> |
| <p>其在<strong>数据聚合器层面的调用链路</strong>可以总结为:setNum 方法调用基本数据聚合器的 setServiceKey 方法,该方法又会调用服务数据聚合器(ServiceStatComposite)的同名 setServiceKey 方法(我们已经知道基本数据聚合器内封装了四个不同类型的子聚合器),这个方法实质上是<strong>对应用层面的特定指标(由指标Key决定)进行注册并赋初始值(参数中的 num)。</strong></p> |
| <p>setNum 的用法均位于注册中心事件多播器(RegistryMetricsEventMulticaster)中声明的 MCat 接口中,在 APPLICATION_NOTIFY_FINISH 和 APPLICATION_DIRECTORY_POST 两个常量初始化时被调用。MCat 接口本身作为常量类使用,并在初始化时<strong>注册真正的指标常量</strong>:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//RegistryMetricsEventMulticaster.MCat |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>MetricsCat APPLICATION_NOTIFY_FINISH <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCat<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>NOTIFY_METRIC_NUM_LAST<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> AbstractMetricsListener<span style="color:#719e07">.</span>onFinish<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>addRt<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> placeType<span style="color:#719e07">.</span>getType<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;</span> lastNumMap <span style="color:#719e07">=</span> Collections<span style="color:#719e07">.</span>unmodifiableMap<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>ATTACHMENT_KEY_LAST_NUM_MAP<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> lastNumMap<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>k<span style="color:#719e07">,</span> v<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>setNum<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> k<span style="color:#719e07">,</span> v<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> MetricsCat APPLICATION_DIRECTORY_POST <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCat<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_VALID<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> AbstractMetricsListener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;&gt;</span> summaryMap <span style="color:#719e07">=</span> event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>ATTACHMENT_DIRECTORY_MAP<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> summaryMap<span style="color:#719e07">.</span>forEach<span style="color:#719e07">((</span>metricsKey<span style="color:#719e07">,</span> map<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>k<span style="color:#719e07">,</span> v<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>setNum<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> k<span style="color:#719e07">,</span> v<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//... |
| </span></span></span></code></pre></div><p>此处声明的指标常量都是 MetricsCat 类型的。其中部分常量在创建时还传入了该指标的收集逻辑,如Key 为 NOTIFY_METRIC_NUM_LAST 的常量。以下为 MetricsCat 的定义:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MetricsCat</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> MetricsPlaceType placeType<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> Function<span style="color:#719e07">&lt;</span>CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;,</span> AbstractMetricsListener<span style="color:#719e07">&gt;</span> eventFunc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsCat</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> BiFunction<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;,</span> AbstractMetricsListener<span style="color:#719e07">&gt;</span> biFunc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>eventFunc <span style="color:#719e07">=</span> collector <span style="color:#719e07">-&gt;</span> biFunc<span style="color:#719e07">.</span>apply<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> collector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsCat</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> TpFunction<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> MetricsPlaceType<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;,</span> AbstractMetricsListener<span style="color:#719e07">&gt;</span> tpFunc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>eventFunc <span style="color:#719e07">=</span> collector <span style="color:#719e07">-&gt;</span> tpFunc<span style="color:#719e07">.</span>apply<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetricsCat <span style="color:#268bd2">setPlaceType</span><span style="color:#719e07">(</span>MetricsPlaceType placeType<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>placeType <span style="color:#719e07">=</span> placeType<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Function<span style="color:#719e07">&lt;</span>CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;,</span> AbstractMetricsListener<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getEventFunc</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> eventFunc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一个接受三个入参,一个返回值的函数接口。通过构造函数我们可以知道这三个入参分别是MetricsKey, MetricsPlaceType, CombMetricsCollector&lt;TimeCounterEvent&gt;,返回值为AbstractMetricsListener。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">@FunctionalInterface</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">TpFunction</span><span style="color:#719e07">&lt;</span>T<span style="color:#719e07">,</span> U<span style="color:#719e07">,</span> K<span style="color:#719e07">,</span> R<span style="color:#719e07">&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> R <span style="color:#268bd2">apply</span><span style="color:#719e07">(</span>T t<span style="color:#719e07">,</span> U u<span style="color:#719e07">,</span> K k<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>MetricsCat 类除了构造器,只提供了两个public方法,都是获取其内部属性的。</p> |
| <p>其实质上 eventFunc 字段的载体,提供了为特定指标生产监听器的逻辑,因此 <code>MetricsCat </code> 可以看做<strong>为特定指标生产指标监听器的工厂</strong>,用户在创建时传入这个监听器的处理逻辑。</p> |
| <p>通过泛型,我们可以知道它构造时使用的两个参数分别为 MetricsKey(指标Key)和一个接受 <code>MetricsKey, MetricsPlaceType, CombMetricsCollector&lt;TimeCounterEvent&gt;</code> 三个参数,返回一个 <code>AbstractMetricsListener</code> 的函数。之所以要多封装一层函数,是因为 <code>placeType</code> 字段在 <code>MetricsKey</code> 实例构造之后才会提供,借此实现延迟初始化。</p> |
| <p>回到之前两个<strong>在 MCat 中定义了监听器生产方法</strong>的两个常量的初始化流程:它们在创建 MetricsCat 时传入的TpFunction中定义的操作为:返回通过 AbstractMetricsListener.onFinish获取的事件完成监听器。当指定MetricsKey 的指标统计事件完成时,这个监听器中的 onEventFinish 方法就会被调用。 而 MetricsCat 构造时传入的 MetricsKey 会被作为 AbstractMetricsListener 的构造参数,用于指定监听的指标。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//RegistryMetricsEventMulticaster.MCat |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">new</span> MetricsCat<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>NOTIFY_METRIC_NUM_LAST<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> AbstractMetricsListener<span style="color:#719e07">.</span>onFinish<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>addRt<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> placeType<span style="color:#719e07">.</span>getType<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;</span> lastNumMap <span style="color:#719e07">=</span> Collections<span style="color:#719e07">.</span>unmodifiableMap<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>ATTACHMENT_KEY_LAST_NUM_MAP<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> lastNumMap<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>k<span style="color:#719e07">,</span> v<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>setNum<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> k<span style="color:#719e07">,</span> v<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">));</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//AbstractMetricsListener |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onFinish</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> Consumer<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> finishFunc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> AbstractMetricsListener<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onEventFinish</span><span style="color:#719e07">(</span>TimeCounterEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//此处是finishFunc就是之前 event -&gt;{...} 中定义的lambda函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> finishFunc<span style="color:#719e07">.</span>accept<span style="color:#719e07">(</span>event<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">};</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>三个形参 (key, placeType, collector) 中的 collector 为 <code>CombMetricsCollector&lt;TimeCounterEvent&gt;</code>,意味着它的三个实现(ConfigCenterMetricsCollector 、MetadataMetricsCollector、RegistryMetricsCollector)都可以作为参数。</p> |
| <p>至此,我们可以总结,对于这两个参数, <code>MetricsCat</code> 创建时嵌套的两层 lambda 函数最终是为了注册特定指标的监听器,并定义事件结束时的处理逻辑(内层的lambda)。在处理事件时,会调用混合指标收集器(CombMetricsCollector) 的 <code>addRT</code> 方法添加响应时间计时,还会调用 <code>setNum</code> 来添加指标计数。</p> |
| <p>由于此处的 <code>MetricsKey</code> 在 MetricsCat创建时就被传入,我们可以确定这两个字段存储了以下两个指标的统计逻辑:</p> |
| <ul> |
| <li> |
| <p>NOTIFY_METRIC_NUM_LAST:Last Notify Nums , 最后一个事件完成时的计数 。监听器中使用的是 <code>setNum</code>,事件结束时直接更新指定key指标的计数为传入的值,同时使用 <code>addRt </code> 来统计事件持续时长</p> |
| </li> |
| <li> |
| <p>DIRECTORY_METRIC_NUM_VALID:Valid Directory Urls,服务目录中注册成功的url数量。监听器中同样使用的是 <code>setNum</code>,事件结束后直接更新为服务目录中的最新计数</p> |
| </li> |
| </ul> |
| <p>之后,三个相关的 MetricsCat(指标类型) 实例会被绑定到一个 CategoryOverall(指标综合) 实例中,绑定的逻辑按一个事件进行的三个过程:<strong>事件发生、事件结束、事件失败</strong>,分别对应 CategoryOverall 的第2、3、4个参数,其中事件发生时的逻辑不能为 null。而第一个参数为 <code>MetricsPlaceType</code>,该参数封装了指标类型标识(如 register 服务注册、subscribe 服务订阅)和该指标的收集级别(应用还是服务)。</p> |
| <p>还记得 <code>MetricsCat</code> 中 <code>TpFunction</code> 的三个入参吗?其中第二个 placeType 就是这个参数。 <code>CategoryOverall</code> 在构造时会将它设置到其中的三个 MetricsCat 中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// CategorySet:常量接口,同样位于RegistryMetricsEventMulticaster中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">interface</span> <span style="color:#268bd2">CategorySet</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> CategoryOverall APPLICATION_NOTIFY <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CategoryOverall<span style="color:#719e07">(</span>OP_TYPE_NOTIFY<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>APPLICATION_NOTIFY_POST<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>APPLICATION_NOTIFY_FINISH<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategoryOverall SERVICE_DIRECTORY <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CategoryOverall<span style="color:#719e07">(</span>OP_TYPE_DIRECTORY<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>APPLICATION_DIRECTORY_POST<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategoryOverall SERVICE_REGISTER <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CategoryOverall<span style="color:#719e07">(</span>OP_TYPE_REGISTER_SERVICE<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>SERVICE_REGISTER_POST<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>SERVICE_REGISTER_FINISH<span style="color:#719e07">,</span> MCat<span style="color:#719e07">.</span>SERVICE_REGISTER_ERROR<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>CategoryOverall<span style="color:#719e07">&gt;</span> ALL <span style="color:#719e07">=</span> Arrays<span style="color:#719e07">.</span>asList<span style="color:#719e07">(</span>APPLICATION_REGISTER<span style="color:#719e07">,</span> APPLICATION_SUBSCRIBE<span style="color:#719e07">,</span> APPLICATION_NOTIFY<span style="color:#719e07">,</span> SERVICE_DIRECTORY<span style="color:#719e07">,</span> SERVICE_REGISTER<span style="color:#719e07">,</span> SERVICE_SUBSCRIBE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><code>CategorySet</code> 中的常量都会被封装到List中,在 <code>RegistryMetricsEventMulticaster</code> 创建时统一调用:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">RegistryMetricsCollector</span> <span style="color:#268bd2">extends</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">RegistryMetricsEventMulticaster</span><span style="color:#719e07">(</span>RegistryMetricsCollector collector<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategorySet<span style="color:#719e07">.</span>ALL<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>categorySet <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过 MetricsCat 实例中的定义的监听器创建逻辑,逐个注册监听器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>addListener<span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getPost<span style="color:#719e07">().</span>getEventFunc<span style="color:#719e07">().</span>apply<span style="color:#719e07">(</span>collector<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getFinish<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>addListener<span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getFinish<span style="color:#719e07">().</span>getEventFunc<span style="color:#719e07">().</span>apply<span style="color:#719e07">(</span>collector<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getError<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>addListener<span style="color:#719e07">(</span>categorySet<span style="color:#719e07">.</span>getError<span style="color:#719e07">().</span>getEventFunc<span style="color:#719e07">().</span>apply<span style="color:#719e07">(</span>collector<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//... |
| </span></span></span></code></pre></div><p><strong>由此,我们也明确了 RegistryMetricsEventMulticaster (指标注册事件多播器)的作用</strong>:统一定义、管理事件,并在初始化时注册其中定义各种事件的<strong>监听器</strong>。</p> |
| <p>它继承了 SimpleMetricsEventMulticaster,其中的 publishEvent 方法在被调用时就会尝试调用所有监听器,判断其是否对当前事件类型感兴趣,选择是否进行调用。同时,这些监听器会对特定指标数据进行计算,更新到对应的收集器中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsEventMulticaster |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishEvent</span><span style="color:#719e07">(</span>MetricsEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>event <span style="color:#719e07">instanceof</span> EmptyEvent<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>validateIfApplicationConfigExist<span style="color:#719e07">(</span>event<span style="color:#719e07">))</span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricsListener listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>listener<span style="color:#719e07">.</span>isSupport<span style="color:#719e07">(</span>event<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> listener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>event<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>我们通过分析混合指标收集器(CombMetricsCollector) 中的 setNum 方法的用法,了解到了 Composite 中的数据来源之一是注册指标事件多播器(RegistryMetricsEventMulticaster)中为服务注册相关指标创建的指标监听器。实际上,increment、addRt方法都是由指标监听器的各个实现调用的。</p> |
| <p>应用程序指标监听器(MetricsApplicationListener)中提供了 AbstractMetricsListener 的几个匿名实现,提供<strong>应用层面事件发生、完成、抛出异常三种情况下对给定指标的计数或RT的计算</strong>,大多数用做处理应用层面指标事件的 MetricsListener 都是它提供的三个监听器实现:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MetricsApplicationListener</span> <span style="color:#268bd2">extends</span> AbstractMetricsListener <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsApplicationListener</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//此处的Event均为TimeCounterEvent,在它被创建时就会自动开始计时 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onPostEventBuild</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> AbstractMetricsListener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>increment<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> metricsKey<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onFinishEventBuild</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> MetricsPlaceType placeType<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> AbstractMetricsListener<span style="color:#719e07">.</span>onFinish<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>increment<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> metricsKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>addRt<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> placeType<span style="color:#719e07">.</span>getType<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onErrorEventBuild</span><span style="color:#719e07">(</span>MetricsKey metricsKey<span style="color:#719e07">,</span> MetricsPlaceType placeType<span style="color:#719e07">,</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> AbstractMetricsListener<span style="color:#719e07">.</span>onError<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>increment<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> metricsKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> collector<span style="color:#719e07">.</span>addRt<span style="color:#719e07">(</span>event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> placeType<span style="color:#719e07">.</span>getType<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>还有 MetricsServiceListener(服务指标监听器),它和 MetricsApplicationListener 十分类似,提供的是服务层面的指标监听器的通用实现,不再重复分析。</p> |
| <p><strong>可以用一句话简单的总结这三个 Collector 注册指标监听器的流程 : Collector 内部的 Mulicaster/Dispatcher 在被 Collector 创建时直接向自己注册已声明的指标监听器。</strong></p> |
| <p>至此,我们可以总结出 MetricsEvent 的部分消息转发路径 :</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/event-dispatch-simple.png" alt="event-dispatch-simple"></p> |
| <h3 id="2dubbo-核心模块的指标采集流程">2,Dubbo 核心模块的指标采集流程</h3> |
| <p>DefaultMetricsCollector(默认指标采集器) 作为指标采集器的默认实现,其主要通过采样器(Sampler)收集dubbo应用核心RPC功能的相关指标。 采样器包括以下几种:</p> |
| <ul> |
| <li>线程池线程状态(最大线程数、最小线程数、活跃线程数等),对应 <strong>ThreadPoolMetricsSampler,线程池指标采样器</strong></li> |
| <li>线程池中线程耗尽事件的计数,对应 <strong>ThreadRejectMetricsCountSampler, 线程耗尽次数采样器</strong></li> |
| <li>应用指标收集情况(收集次数),对应 DefaultMetricsCollector 中实现的 SimpleMetricsCountSampler 匿名子类</li> |
| </ul> |
| <p><strong>这些采样器内部会存储其负责采样类型指标的样本。由于默认指标采集器同样继承自 CombMetricsCollector,它也同时具有与前文中分析的三大中心指标收集器相似的指标转发流程。</strong></p> |
| <p>除了线程池指标采样器,其它两个采样器均实现自简单指标计数采样器(SimpleMetricsCountSampler)。它实现了通用的指标存取操作。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/sampler-struct.png" alt="sampler-struct"></p> |
| <p>简单指标计数采样器内部的指标样本容器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">abstract</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">SimpleMetricsCountSampler</span><span style="color:#719e07">&lt;</span>S<span style="color:#719e07">,</span> K<span style="color:#719e07">,</span> M <span style="color:#268bd2">extends</span> Metric<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">implements</span> MetricsCountSampler<span style="color:#719e07">&lt;</span>S<span style="color:#719e07">,</span> K<span style="color:#719e07">,</span> M<span style="color:#719e07">&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>K<span style="color:#719e07">,</span> ConcurrentMap<span style="color:#719e07">&lt;</span>M<span style="color:#719e07">,</span> AtomicLong<span style="color:#719e07">&gt;&gt;</span> metricCounter <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>其中:泛型 M 为指标类型,如方法指标 MethodMetric;泛型 K 为指标名称类型,如 String;泛型 S 为请求源类型,如 String 或 Invocation。请求源用于定位触发采样的请求来源,指标名称则用于对指标进行分组,便于按名称来分组检索指标数据。</strong></p> |
| <p>以及对特定指标的增减操作:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsCountSampler |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">inc</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doExecute<span style="color:#719e07">(</span>source<span style="color:#719e07">,</span> metricName<span style="color:#719e07">,</span> counter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> counter<span style="color:#719e07">.</span>incrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">dec</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doExecute<span style="color:#719e07">(</span>source<span style="color:#719e07">,</span> metricName<span style="color:#719e07">,</span> counter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> counter<span style="color:#719e07">.</span>decrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">incOnEvent</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doExecute<span style="color:#719e07">(</span>source<span style="color:#719e07">,</span> metricName<span style="color:#719e07">,</span> counter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> counter<span style="color:#719e07">.</span>incrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">decOnEvent</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doExecute<span style="color:#719e07">(</span>source<span style="color:#719e07">,</span> metricName<span style="color:#719e07">,</span> counter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> counter<span style="color:#719e07">.</span>decrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>对于四个增加、减少计数的方法,它们最终都会调用 <code>doExecute</code> 方法来完成计数操作,其中 counter 函数定义了对计数器的操作(增加、减少)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsCountSampler |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExecute</span><span style="color:#719e07">(</span>S source<span style="color:#719e07">,</span> K metricsName<span style="color:#719e07">,</span> Function<span style="color:#719e07">&lt;</span>AtomicLong<span style="color:#719e07">,</span> Boolean<span style="color:#719e07">&gt;</span> counter<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetricsCountSampleConfigurer<span style="color:#719e07">&lt;</span>S<span style="color:#719e07">,</span> K<span style="color:#719e07">,</span> M<span style="color:#719e07">&gt;</span> sampleConfigure <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCountSampleConfigurer<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> sampleConfigure<span style="color:#719e07">.</span>setSource<span style="color:#719e07">(</span>source<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> sampleConfigure<span style="color:#719e07">.</span>setMetricsName<span style="color:#719e07">(</span>metricsName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//利用子类重写的countConfigure为 sampleConfigure 设置事件发布函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>countConfigure<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过指标名获取对应的指标计数器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>M<span style="color:#719e07">,</span> AtomicLong<span style="color:#719e07">&gt;</span> metricAtomic <span style="color:#719e07">=</span> metricCounter<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>metricsName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metricAtomic <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metricAtomic <span style="color:#719e07">=</span> metricCounter<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>metricsName<span style="color:#719e07">,</span> k <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">.</span>getMetric<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;metrics is null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> AtomicLong atomicCounter <span style="color:#719e07">=</span> metricAtomic<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">.</span>getMetric<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>atomicCounter <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> atomicCounter <span style="color:#719e07">=</span> metricAtomic<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">.</span>getMetric<span style="color:#719e07">(),</span> k <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> AtomicLong<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// counter函数定义了对atomicCounter的增减操作,如 inc方法定义的counter是对atomicCounter+1,dec方法定义的是对atomicCounter-1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Boolean isEvent <span style="color:#719e07">=</span> counter<span style="color:#719e07">.</span>apply<span style="color:#719e07">(</span>atomicCounter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果本次计数操作应该触发事件... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isEvent<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取子类设置的事件发布函数,发布事件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> sampleConfigure<span style="color:#719e07">.</span>getFireEventHandler<span style="color:#719e07">().</span>accept<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><code>doExecute</code> 做了两件事:</p> |
| <p>1,判断当前指标是否存在,如果不存在就放到容器中。</p> |
| <p>2,调用提供的计数函数对指标进行修改,对应 counter 字段。</p> |
| <p>以下为各采样器在 countConfigure 方法中提供的创建指标实例的逻辑:</p> |
| <ul> |
| <li>DefaultMetricsCollector 中 SimpleMetricsCountSampler 的匿名实现 (applicationSampler)提供的<code>countConfigure</code> 方法:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">countConfigure</span><span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> MetricsCountSampleConfigurer<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> MetricsEvent<span style="color:#719e07">.</span>Type<span style="color:#719e07">,</span> ApplicationMetric<span style="color:#719e07">&gt;</span> sampleConfigure<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供根据 configure 创建指标实例的函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> sampleConfigure<span style="color:#719e07">.</span>configureMetrics<span style="color:#719e07">(</span>configure <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ApplicationMetric<span style="color:#719e07">(</span>sampleConfigure<span style="color:#719e07">.</span>getSource<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><ul> |
| <li>ThreadRejectMetricsCountSampler 中提供的 <code>countConfigure</code> 方法:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">countConfigure</span><span style="color:#719e07">(</span>MetricsCountSampleConfigurer<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">,</span> ThreadPoolRejectMetric<span style="color:#719e07">&gt;</span> sampleConfigure<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供根据 configure 创建指标实例的函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> sampleConfigure<span style="color:#719e07">.</span>configureMetrics<span style="color:#719e07">(</span>configure <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ThreadPoolRejectMetric<span style="color:#719e07">(</span>collector<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">(),</span>configure<span style="color:#719e07">.</span>getSource<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><hr> |
| <p>默认指标收集器继承自 CombMetricsCollector,内部包含一个 DefaultSubDispatcher,因此它自身也可以作为指标事件的转发器,接受其它指标监听器的注册。</p> |
| <p>在之前,我们发现了 AggregateMetricsCollector(聚合指标收集器)会将自己注册为 DefaultMetricsCollector 的监听器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AggregateMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> registerListener<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerListener</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span>applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>DefaultMetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>addListener<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>还有 HistogramMetricsCollector (直方图指标收集器)也会将自己注册为它的监听器。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">HistogramMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> registerListener<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerListener</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>DefaultMetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getEventMulticaster<span style="color:#719e07">().</span>addListener<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>因此,聚合指标收集器和直方图指标收集器的指标事件来源于默认指标收集器转发的指标事件</strong>。通过默认指标转发器的 isSupport 方法,还可以发现这些指标事件的类型是 RequestEvent (RPC请求事件)或 RequestBeforeEvent(请求前失败事件:实际请求发送之前在 ClusterFilter 中产生的异常)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isSupport</span><span style="color:#719e07">(</span>MetricsEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> event <span style="color:#719e07">instanceof</span> RequestEvent <span style="color:#719e07">||</span> event <span style="color:#719e07">instanceof</span> RequestBeforeEvent<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/default-metrics-collector-struct.png" alt="default-metrics-collector-struct"></p> |
| <p><strong>至此,我们也明确了 Dubbo 应用内部核心模块的相关指标是如何收集的:默认指标收集器除了接受上层指标转发器的指标事件之外,还会通过各种采样器对埋点采样,通过 SubDispatcher 统一转发指标事件,通知注册为它的监听器的其它 Collector 完成采样。</strong></p> |
| <h3 id="3-直方图相关指标的采集流程">3, 直方图相关指标的采集流程</h3> |
| <p>直方图指标收集器(HistogramMetricsCollector)也是一个较为特殊的收集器,它主要负责RPC调用响应时间直方图指标这一种指标的收集。</p> |
| <p>由于直方图指标收集器只需要采集单一类型的指标,它直接使用Map来存储采样数据,而非更复杂的数据聚合器(Composite)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//HistogramMetricsCollector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> ConcurrentHashMap<span style="color:#719e07">&lt;</span>MethodMetric<span style="color:#719e07">,</span> Timer<span style="color:#719e07">&gt;</span> rt <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span></code></pre></div><p><strong>其中,key为方法指标,Timer则是该方法对应的RT计时器。该计时器由 micrometer 提供,在跟踪短时间内的大量事件时具有良好的性能。</strong></p> |
| <p>前文中已经提到,直方图指标收集器在初始化时会将自己注册为默认指标收集器(DefaultMetricsCollector)中的监听器,与聚合指标收集器相同(AggregateMetricsCollector)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerListener</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>DefaultMetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getEventMulticaster<span style="color:#719e07">().</span>addListener<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这意味着它接收的指标事件实际也来自于默认指标收集器中的采样器。之前的分析中,我们知道默认指标收集器目前实际只转发来自 MetricsDispatcher 的请求相关事件,因此直方图指标收集器也只会收集请求响应时间相关的指标采样。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//HistogramMetricsCollector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onRTEvent</span><span style="color:#719e07">(</span>RequestEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metricRegister <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MethodMetric metric <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MethodMetric<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>MetricsConstants<span style="color:#719e07">.</span>INVOCATION<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">long</span> responseTime <span style="color:#719e07">=</span> event<span style="color:#719e07">.</span>getTimePair<span style="color:#719e07">().</span>calc<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> HistogramMetricSample sample <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HistogramMetricSample<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>METRIC_RT_HISTOGRAM<span style="color:#719e07">.</span>getNameByType<span style="color:#719e07">(</span>metric<span style="color:#719e07">.</span>getSide<span style="color:#719e07">()),</span> |
| </span></span><span style="display:flex;"><span> MetricsKey<span style="color:#719e07">.</span>METRIC_RT_HISTOGRAM<span style="color:#719e07">.</span>getDescription<span style="color:#719e07">(),</span> metric<span style="color:#719e07">.</span>getTags<span style="color:#719e07">(),</span> RT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Timer timer <span style="color:#719e07">=</span> ConcurrentHashMapUtils<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>rt<span style="color:#719e07">,</span> metric<span style="color:#719e07">,</span> k <span style="color:#719e07">-&gt;</span> metricRegister<span style="color:#719e07">.</span>register<span style="color:#719e07">(</span>sample<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> timer<span style="color:#719e07">.</span>record<span style="color:#719e07">(</span>responseTime<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>当接收到事件时,直方图指标收集器会先计算当前调用花费的时间,然后为计时器(Time)添加一条响应时间记录。</p></description></item><item><title>Blog: 3-指标监听注册梳理</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</guid><description> |
| <h2 id="三指标监听注册梳理">三、指标监听注册梳理</h2> |
| <p>在前一章中,我们了解了不同收集器中的指标样本是如何被监听器添加进去的。接下来,我们将归纳指标监听器 的创建位置,及它们对应统计的指标。</p> |
| <p>通过之前的分析,我们已经知道指标 注册事件多播器(RegistryMetricsEventMulticaster)中定义了并绑定了服务注册相关的指标。这种绑定操作同样存在于其它几个简单指标事件多播器(SimpleMetricsEventMulticaster)的几个实现中。</p> |
| <h3 id="转发器注册">转发器注册</h3> |
| <p><strong>RegistrySubDispatcher (服务注册指标转发器)注册了服务注册相关指标:</strong></p> |
| <ul> |
| <li>应用级实例注册成功/失败/总数计数 (APPLICATION_REGISTER_&hellip;)</li> |
| <li>应用级服务接口订阅成功/失败/总数计数 (APPLICATION_SUBSCRIBE_&hellip;)</li> |
| <li>服务级注册成功/失败/总数计数 (SERVICE_REGISTER_&hellip;)</li> |
| <li>特殊的 APPLICATION_NOTIFY_FINISH 和 APPLICATION_DIRECTORY_POST (应用服务目录变化次数)</li> |
| </ul> |
| <p><strong>MetadataSubDispatcher(元数据指标转发器)注册应用元数据相关指标</strong></p> |
| <ul> |
| <li> |
| <p>应用推送元数据相关计数 (APPLICATION_PUSH_&hellip;)</p> |
| </li> |
| <li> |
| <p>应用订阅元数据相关计数 (APPLICAITON_SUBSCRIBE_&hellip;)</p> |
| </li> |
| <li> |
| <p>服务订阅元数据相关计数 (SERVICE_SUBSCRIBE_&hellip;)</p> |
| </li> |
| </ul> |
| <p><strong>ConfigCenterSubDispatcher (配置中心指标转发器) 注册配置中心配置更新次数指标</strong></p> |
| <ul> |
| <li>配置中心推送新配置次数 (CONFIGCENTER_METRIC_TOTAL)</li> |
| </ul> |
| <p><strong>DefaultSubDispatcher (默认转发器) 注册核心RPC调用次数指标</strong></p> |
| <ul> |
| <li>请求次数 (METRIC_REQUESTS)</li> |
| <li>请求成功次数(METRIC_REQUESTS_SUCCEED)</li> |
| <li>请求失败次数(METRIC_REQUEST_BUSINESS_FAILED)</li> |
| </ul> |
| <p><strong>MetricsDispatcher</strong></p> |
| <p>MetricsDispatcher 较为特殊,它负责 ApplicationModel 下所有 MetricsCollector(前文中提到的指标收集器) 的初始化注册工作,并将它们添加到自己的监听器列表中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MetricsDispatcher</span> <span style="color:#268bd2">extends</span> SimpleMetricsEventMulticaster <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@SuppressWarnings</span><span style="color:#719e07">({</span><span style="color:#2aa198">&#34;rawtypes&#34;</span><span style="color:#719e07">})</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsDispatcher</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ScopeBeanFactory beanFactory <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>MetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>extensionLoader <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> customizeCollectors <span style="color:#719e07">=</span> extensionLoader |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getActivateExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricsCollector customizeCollector <span style="color:#719e07">:</span> customizeCollectors<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> beanFactory<span style="color:#719e07">.</span>registerBean<span style="color:#719e07">(</span>customizeCollector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> customizeCollectors<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>addListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>需要注意,以上几个实现均继承自 SimpleMetricsEventMulticaster,因此它们都具有注册监听、转发事件的能力。它们将自己注册到对应领域的指标 Collector 中,并在收到指标事件时转发到自己注册的监听器中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsEventMulticaster |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addListener</span><span style="color:#719e07">(</span>MetricsListener<span style="color:#719e07">&lt;?&gt;</span> listener<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> listeners<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>listener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishEvent</span><span style="color:#719e07">(</span>MetricsEvent event<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>event <span style="color:#719e07">instanceof</span> EmptyEvent<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>validateIfApplicationConfigExist<span style="color:#719e07">(</span>event<span style="color:#719e07">))</span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricsListener listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>listener<span style="color:#719e07">.</span>isSupport<span style="color:#719e07">(</span>event<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> listener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>event<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//... |
| </span></span></span></code></pre></div><p><strong>SubDispatcher 和 Collector 之间的对应关系:</strong></p> |
| <ul> |
| <li>MetadataSubDispatcher -&gt; MetadataMetricsCollector 元数据指标事件</li> |
| <li>RegistrySubDispatcher -&gt; RegistryMetricsCollector 服务注册指标事件</li> |
| <li>ConfigCenterSubDispatcher -&gt; ConfigCenterMetricsCollector 配置中心指标事件</li> |
| <li>MetricsDispatcher 由 MetricsEventBus 通过 BeanFactory 加载。它是所有事件转发的入口。</li> |
| </ul> |
| <h3 id="事件触发">事件触发</h3> |
| <p>剩下的问题就是这些监听器是如何被触发的。</p> |
| <p>可以发现三大中心的指标转发器都是在它们对应的Collector中创建的:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ConfigCenterMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>setEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ConfigCenterMetricsDispatcher<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">MetadataMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>setEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">new</span> MetadataMetricsEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">RegistryMetricsCollector</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>setEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryMetricsEventMulticaster<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这意味这想要通过它们发布事件,需要通过它们对应的 <code>Collector</code> 来访问。</p> |
| <p>如前文所述, MetricsDispatcher 在初始化时会尝试获取并加载所有 MetricsCollector 的SPI拓展,</p> |
| <p>三大中心的MetricsCollector (Metadata/Registry/ConfigCenter)也会在这里被初始化,并添加为 MetricsDispatcher 的监听器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsDispatcher</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>MetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricsCollector customizeCollector <span style="color:#719e07">:</span> customizeCollectors<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> beanFactory<span style="color:#719e07">.</span>registerBean<span style="color:#719e07">(</span>customizeCollector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> customizeCollectors<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>addListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>对于 MetricsDispatcher,它由 MetricsEventBus 创建。而 MetricsEventBus 自身作为指标相关消息的总线,会接收所有指标消息,并将它们转发给监听者。</p> |
| <p>MetricsEvenetBus 提供了三个方法来发布指标事件:</p> |
| <ul> |
| <li><code>publish(MetricsEvent event)</code> ,将事件发布给所有订阅者,只发布一次且不关心事件处理结果</li> |
| <li><code>post(MetricsEvent event, Supplier&lt;T&gt; targetSupplier)</code> ,将事件发布给所有订阅者,并根据是否产生异常判断事件成功或失败,调用 MetricsDispatcher 发布对应的事件。</li> |
| <li><code>post(MetricsEvent event, Supplier&lt;T&gt; targetSupplier, Function&lt;T, Boolean&gt; trFunction)</code> ,额外的 trFunction 可用于通过业务结果判断事件成功或失败。 <code>targetSupplier</code> 为业务操作函数,泛型T为业务结果类型。</li> |
| </ul> |
| <p>这三个方法均会通过 MetricsDispatcher 来转发事件。</p> |
| <p>在之前的分析中,我们知道 MetricsDispatcher 创建了所有 MetricsCollector 拓展,并将它们注册为自己的监听者。</p> |
| <p>因此,当 MetricsEventBus 接收到发布的信息时,它会将信息转发到所有 MetricsCollector 中。对于 CombMetricsCollector 的实现,它们又会调用自己创建的 MetricsEventMulticaster 再次转发消息,到具体指标的监听器。</p> |
| <p>之后,这些监听器就会根据自己的逻辑修改Collector中的指标计数。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/metris-event-dispatch-full.png" alt="image-20230629160012950"></p> |
| <h3 id="事件发布">事件发布</h3> |
| <p>接下来,我们将寻找指标事件发布的源头。</p> |
| <p>通过前文的分析,我们知道 MetricsEventBus 是所有指标事件发布的入口。具体来说,它有以下的用法:</p> |
| <ul> |
| <li>AbstractDirectory</li> |
| <li>ServiceConfig</li> |
| <li>DefaultApplicationDeployer</li> |
| <li>ApolloDynamicConfiguration</li> |
| <li>NacosDynamicConfiguration</li> |
| <li>ZookeeperDataListener</li> |
| <li>AbstractMetadataReport</li> |
| </ul> |
| <p>我们将逐个分析每个用法。</p> |
| <p><strong>AbstractDirectory</strong></p> |
| <p>AbstractDirectory 在修改 Invoker 状态相关的操作完成后都会通过 MetricsEventBus 发布 refreshDirectoryEvent(服务目录更新事件,类型为 RegistryEvent ),将当前目录中各种状态 Invoker 实例的最新数量作为附件添加到 RegistryEvent 中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//AbstractDirectory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">recoverDisabledInvoker</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>RegistryEvent<span style="color:#719e07">.</span>refreshDirectoryEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> getSummary<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setInvokers</span><span style="color:#719e07">(</span>BitList<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;&gt;</span> invokers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>RegistryEvent<span style="color:#719e07">.</span>refreshDirectoryEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> getSummary<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setInvokers</span><span style="color:#719e07">(</span>BitList<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;&gt;</span> invokers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>RegistryEvent<span style="color:#719e07">.</span>refreshDirectoryEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> getSummary<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> Map<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;&gt;</span> <span style="color:#268bd2">getSummary</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;&gt;</span> summaryMap <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目录中可用的Invoker数量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> summaryMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_VALID<span style="color:#719e07">,</span> groupByServiceKey<span style="color:#719e07">(</span>getValidInvokers<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目录中不可用的Invoker数量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> summaryMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_DISABLE<span style="color:#719e07">,</span> groupByServiceKey<span style="color:#719e07">(</span>getDisabledInvokers<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目录中等待重连的Invoker数量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> summaryMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_TO_RECONNECT<span style="color:#719e07">,</span> groupByServiceKey<span style="color:#719e07">(</span>getInvokersToReconnect<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> summaryMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_ALL<span style="color:#719e07">,</span> groupByServiceKey<span style="color:#719e07">(</span>getInvokers<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> summaryMap<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span></code></pre></div><p>该事件最终会由 RegistryMetricsCollector 中的 RegistryMetricsDispatcher 转发到关系该事件的监听器中。<strong>事件和监听器之间通过 MetricsKey匹配</strong> 。</p> |
| <p>最终,MetricsKey 为 <code>**DIRECTORY_METRIC_NUM_VALID**</code> 的监听器会处理这个事件,并更新 Collector中的计数。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//DIRECTORY_METRIC_NUM_VALID 对应的 Listener。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>MetricsCat APPLICATION_DIRECTORY_POST <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCat<span style="color:#719e07">(</span>MetricsKey<span style="color:#719e07">.</span>DIRECTORY_METRIC_NUM_VALID<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> placeType<span style="color:#719e07">,</span> collector<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> AbstractMetricsListener<span style="color:#719e07">.</span>onEvent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>MetricsKey<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Integer<span style="color:#719e07">&gt;&gt;</span> summaryMap <span style="color:#719e07">=</span> event<span style="color:#719e07">.</span>getAttachmentValue<span style="color:#719e07">(</span>ATTACHMENT_DIRECTORY_MAP<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> summaryMap<span style="color:#719e07">.</span>forEach<span style="color:#719e07">((</span>metricsKey<span style="color:#719e07">,</span> map<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>k<span style="color:#719e07">,</span> v<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> collector<span style="color:#719e07">.</span>setNum<span style="color:#719e07">(</span>metricsKey<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>appName<span style="color:#719e07">(),</span> k<span style="color:#719e07">,</span> v<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">));</span> |
| </span></span></code></pre></div><p>这样,服务目录中不同状态 Invoker 的计数就通过 RegistryMetricsCollector 更新到了 ServiceStatComposite 中。</p> |
| <p><strong>ServiceConfig</strong></p> |
| <p>当通过 ServiceConfig 导出、注册一个服务时,它会发布一个服务导出事件。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ServiceConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">protected</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExport</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> doExportUrls<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> exported<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrls</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>post<span style="color:#719e07">(</span>RegistryEvent<span style="color:#719e07">.</span>toRsEvent<span style="color:#719e07">(</span>module<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">(),</span> getUniqueServiceName<span style="color:#719e07">(),</span> protocols<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">*</span> registryURLs<span style="color:#719e07">.</span>size<span style="color:#719e07">()),</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//该函数会被同步执行,如果抛出异常则触发 MetricsEvent 的 onError方法,否则触发 onFinish |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ProtocolConfig protocolConfig <span style="color:#719e07">:</span> protocols<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String pathKey <span style="color:#719e07">=</span> URL<span style="color:#719e07">.</span>buildKey<span style="color:#719e07">(</span>getContextPath<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span>p <span style="color:#719e07">-&gt;</span> p <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;/&#34;</span> <span style="color:#719e07">+</span> path<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>orElse<span style="color:#719e07">(</span>path<span style="color:#719e07">),</span> group<span style="color:#719e07">,</span> version<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>serverService<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>pathKey<span style="color:#719e07">,</span> interfaceClass<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> doExportUrlsFor1Protocol<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> providerModel<span style="color:#719e07">.</span>setServiceUrls<span style="color:#719e07">(</span>urls<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>事件发布时,该事件会被转发到 RegistryMetricsCollector,触发对应的 Listener 增加 <strong>SERVICE_REGISTER_METRIC_REQUESTS</strong> (当前服务级注册请求总数)的计数,然后执行定义的 provider 函数。根据是否抛出异常,之后执行 onError 方法 或 onFinish 方法,增加 <strong>SERVICE_REGISTER_METRIC_REQUESTS_FAILED</strong> (当前服务级注册请求失败总数)或<strong>SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED</strong> (当前服务级注册请求成功总数) 的计数。</p> |
| <p><strong>DefaultApplicationDeployer</strong></p> |
| <p>它在应用部署过程中,初始化配置中心时,发布配置发生改变的事件。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startConfigCenter</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> compositeDynamicConfiguration<span style="color:#719e07">.</span>addConfiguration<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> prepareEnvironment<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> DynamicConfiguration <span style="color:#268bd2">prepareEnvironment</span><span style="color:#719e07">(</span>ConfigCenterConfig configCenter<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Add metrics |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> configCenter<span style="color:#719e07">.</span>getConfigFile<span style="color:#719e07">(),</span> configCenter<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span>configCenter<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">(),</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> configMap<span style="color:#719e07">.</span>size<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isNotEmpty<span style="color:#719e07">(</span>appGroup<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> appConfigFile<span style="color:#719e07">,</span> appGroup<span style="color:#719e07">,</span> configCenter<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">(),</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> appConfigMap<span style="color:#719e07">.</span>size<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在 <code>prepareEnvironment</code> 方法中,会按配置中心设置的组(group)和当前应用程序的名称作为组名发布两次事件。该事件会被转发到 ConfigCenterMetricsCollector,增加 <strong>CONFIGCENTER_METRIC_TOTAL</strong> (配置中心配置变化次数)的计数。</p> |
| <p><strong>ApolloDynamicConfiguration</strong></p> |
| <p>动态配置功能的 Apollo 配置中心实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ApolloDynamicConfiguration |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onChange</span><span style="color:#719e07">(</span>com<span style="color:#719e07">.</span>ctrip<span style="color:#719e07">.</span>framework<span style="color:#719e07">.</span>apollo<span style="color:#719e07">.</span>model<span style="color:#719e07">.</span>ConfigChangeEvent changeEvent<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span>ConfigCenterEvent<span style="color:#719e07">.</span>APOLLO_PROTOCOL<span style="color:#719e07">,</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> SELF_INCREMENT_SIZE<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>当Apollo配置中心的配置发生变化时,它的 <code>onChange</code> 方法会被触发,并在最后发布一个 ConfigCenterEvent。该事件最终转发到ConfigCenterMetricsCollector 中,同样增加 **CONFIGCENTER_METRIC_TOTAL **的计数。</p> |
| <p><strong>NacosDynamicConfiguration</strong></p> |
| <p>动态配置功能的 Nacos 配置中心实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//NacosDynamicConfiguration |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">innerReceive</span><span style="color:#719e07">(</span>String dataId<span style="color:#719e07">,</span> String group<span style="color:#719e07">,</span> String configInfo<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> event<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> event<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> ConfigCenterEvent<span style="color:#719e07">.</span>NACOS_PROTOCOL<span style="color:#719e07">,</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> SELF_INCREMENT_SIZE<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>当Nacos配置中心的配置发生变化时,它的 <code>innerReceive</code> 方法被触发,发布一个 ConfigCenterEvent。它的处理流程和 ApolloDynamicConfiguration 一致,最终增加 **CONFIGCENTER_METRIC_TOTAL **的计数。</p> |
| <p><strong>ZookeeperDataListener</strong></p> |
| <p>动态配置功能的 Zookeeper 实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ZookeeperDataListener |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">dataChanged</span><span style="color:#719e07">(</span>String path<span style="color:#719e07">,</span> Object value<span style="color:#719e07">,</span> EventType eventType<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>publish<span style="color:#719e07">(</span>ConfigCenterEvent<span style="color:#719e07">.</span>toChangeEvent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> configChangeEvent<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> configChangeEvent<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> ConfigCenterEvent<span style="color:#719e07">.</span>ZK_PROTOCOL<span style="color:#719e07">,</span> ConfigChangeType<span style="color:#719e07">.</span>ADDED<span style="color:#719e07">.</span>name<span style="color:#719e07">(),</span> SELF_INCREMENT_SIZE<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在指标收集层面,它的行为和前文中两个配置中心一致,此处不详细展开三个配置中心具体实现的异同。</p> |
| <p><strong>AbstractMetadataReport</strong></p> |
| <p>元数据报告接口的抽象实现。它的三个实现 (NacosMetadataReport、RedisMetadataReport、ZookeeperMetadataReport)均使用它的指标事件逻辑。</p> |
| <p>当订阅新服务并获取它的元数据时,它会发布一个 MetadataEvent 触发相关指标的修改。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadataTask</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus<span style="color:#719e07">.</span>post<span style="color:#719e07">(</span>metadataEvent<span style="color:#719e07">,</span> <span style="color:#719e07">()</span> <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> result <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> doStoreProviderMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> saveProperties<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> <span style="color:#719e07">!</span>syncReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> result <span style="color:#719e07">=</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> aBoolean <span style="color:#719e07">-&gt;</span> aBoolean |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在前文中,我们提到 MetricsEventBus.post 的第二个参数是实际要进行的业务操作,第三个参数则是根据业务操作返回值判断操作是否成功的逻辑。</p> |
| <p>此处的业务操作是尝试存储目标服务的元数据。执行操作之前,会先发布事件,最终增加 <strong>STORE_PROVIDER_METADATA</strong> (尝试存储服务元数据次数)的计数。如果产生异常,会增加 <strong>STORE_PROVIDER_METADATA_ERROR</strong> (存储服务元数据失败) 的计数,否则增加<strong>STORE_PROVIDER_METADATA_SUCCEED</strong>(存储服务元数据成功) 的计数。</p></description></item><item><title>Blog: 4-指标转换与导出</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</guid><description> |
| <h2 id="四指标转换与导出">四、指标转换与导出</h2> |
| <p>本章主要梳理指标收集完成后,向外部收集器导出的流程。</p> |
| <p>通过之前的分析,我们知道不同类型指标的收集分别由各个 Collector 实现进行。它们底层的 MetricsCollector 接口定义了指标导出的操作。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@SPI</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">MetricsCollector</span><span style="color:#719e07">&lt;</span>E <span style="color:#268bd2">extends</span> TimeCounterEvent<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">extends</span> MetricsLifeListener<span style="color:#719e07">&lt;</span>E<span style="color:#719e07">&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isCollectEnabled</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Collect metrics as {@link MetricSample} |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return List of MetricSample |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricSample<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">collect</span><span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>而指标报告器 (MetricsReporter) 的实现会定时调用Collector 的 <code>collect</code> 方法,更新并导出指标数据。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">MetricsReporter</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新统计数据,定时调用collect() |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshData</span><span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取指标数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String <span style="color:#268bd2">getResponse</span><span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取带指标名的指标样本(单个指标) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">default</span> String <span style="color:#268bd2">getResponseWithName</span><span style="color:#719e07">(</span>String metricsName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>指标报告器有两个实现:DefaultMetricsReporter 和 PrometheusMetricsReporter,它们都实现自 AbstractMetricsRepoter,并使用它的指标刷新逻辑 (<code>refreshData</code>方法)。</p> |
| <p>AbstractMetricsRepoter 初始化时会获取并保存所有 Collector 的实现,每次刷新数据,调用<code>refreshData</code>方法时都会遍历这些收集器,更新指标数据。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//AbstractMetricsRepoter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initCollectors</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> otherCollectors <span style="color:#719e07">=</span> beanFactory<span style="color:#719e07">.</span>getBeansOfType<span style="color:#719e07">(</span>MetricsCollector<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> collectors<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>otherCollectors<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshData</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> collectors<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>collector <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricSample<span style="color:#719e07">&gt;</span> samples <span style="color:#719e07">=</span> collector<span style="color:#719e07">.</span>collect<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetricSample sample <span style="color:#719e07">:</span> samples<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将Dubbo的度量类型适配为micrometer的度量类型,并将其添加到CompositeMeterRegistry中,借此实现多监控系统的支持。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span></code></pre></div><p>DefaultMetricsReporter 和 PrometheusMetricsReporter 各自实现了自己的指标采样逻辑 (<code>getResponse</code>方法)。</p> |
| <p><strong>PrometheusMetricsReporter</strong></p> |
| <p>它通过 PrometheusMeterRegistry 获取普罗米修斯支持格式的样本数据:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//PrometheusMetricsReporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">getResponse</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> prometheusRegistry<span style="color:#719e07">.</span>scrape<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在初始化时,它会开启一个定时任务,定时向普罗米修斯服务端推送采样数据:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//PrometheusMetricsReporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> SimpleMeterRegistry meterRegistry <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SimpleMeterRegistry<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doInit</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> addMeterRegistry<span style="color:#719e07">(</span>prometheusRegistry<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> schedulePushJob<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">schedulePushJob</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里的URL是DefaultMetricsReporter中定义的指标报告URL,提供了指标服务的具体地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> pushEnabled <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>PROMETHEUS_PUSHGATEWAY_ENABLED_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>pushEnabled<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> pushJobExecutor<span style="color:#719e07">.</span>scheduleWithFixedDelay<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> push<span style="color:#719e07">(</span>pushGateway<span style="color:#719e07">,</span> job<span style="color:#719e07">),</span> pushInterval<span style="color:#719e07">,</span> pushInterval<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>SECONDS<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">push</span><span style="color:#719e07">(</span>PushGateway pushGateway<span style="color:#719e07">,</span> String job<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> refreshData<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将本次采样数据添加到pushGateway,等待下次抓取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> pushGateway<span style="color:#719e07">.</span>pushAdd<span style="color:#719e07">(</span>prometheusRegistry<span style="color:#719e07">.</span>getPrometheusRegistry<span style="color:#719e07">(),</span> job<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>PushGateway 和 PrometheusRegistry 均为 micrometer 提供的 API。</p> |
| <p><strong>PrometheusRegistry 将度量数据转换为普罗米修斯支持的格式,而 PushGateway 存储样本,暴露一个HTTP端点供普罗米修斯服务端抓取。</strong></p> |
| <p>PushGateway 本身只是一个度量数据的缓存区,普罗米修斯服务端每从其中抓取一次数据,其内部的样本就会被清除。</p> |
| <p><strong>DefaultMetricsReporter</strong></p> |
| <p>指标报告器的默认实现,提供了按指标名称导出特定指标的方法。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//DefaultMetricsReporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">getResponse</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">getResponseWithName</span><span style="color:#719e07">(</span>String metricsName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> meterRegistry<span style="color:#719e07">.</span>getMeters<span style="color:#719e07">().</span>stream<span style="color:#719e07">().</span>filter<span style="color:#719e07">(</span>meter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据名称过滤样本 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> metricsValue<span style="color:#719e07">.</span>forEach<span style="color:#719e07">((</span>key<span style="color:#719e07">,</span> value<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//按格式拼装结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> sb<span style="color:#719e07">.</span>toString<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doInit</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> addMeterRegistry<span style="color:#719e07">(</span>meterRegistry<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doDestroy</span><span style="color:#719e07">()</span> <span style="color:#719e07">{}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>该实现使用的 SimpleMeterRegistry 本身只会存储指标数据的功能,而不像 PrometheusMeterRegistry 那样提供发布数据的方法。</p> |
| <p><strong>因此该指标报告器不会主动向外部发布数据,而是被动的通过 <code>getResponseWithName</code> 提供数据。</strong></p> |
| <p>而且,该报告器在任何情况下都会被初始化:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//DefaultApplicationDeployer#initMetricsReporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">//If the protocol is not the default protocol, the default protocol is also initialized. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>PROTOCOL_DEFAULT<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>metricsConfig<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> DefaultMetricsReporterFactory defaultMetricsReporterFactory <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DefaultMetricsReporterFactory<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> MetricsReporter defaultMetricsReporter <span style="color:#719e07">=</span> defaultMetricsReporterFactory<span style="color:#719e07">.</span>createMetricsReporter<span style="color:#719e07">(</span>metricsConfig<span style="color:#719e07">.</span>toUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> defaultMetricsReporter<span style="color:#719e07">.</span>init<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>registerBean<span style="color:#719e07">(</span>defaultMetricsReporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span></code></pre></div><p>实际上,该类主要在用户使用Qos命令查询时提供指定指标数据,而非提供给某个特定的外部指标中心。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//Qos命令 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DefaultMetricsReporterCmd</span> <span style="color:#268bd2">implements</span> BaseCommand <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> String <span style="color:#268bd2">getResponseByApplication</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">,</span> String metricsName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String response <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;DefaultMetricsReporter not init&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> MetricsReporter metricsReporter <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>DefaultMetricsReporter<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metricsReporter <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metricsReporter<span style="color:#719e07">.</span>refreshData<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取指定名称指标的数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> response <span style="color:#719e07">=</span> metricsReporter<span style="color:#719e07">.</span>getResponseWithName<span style="color:#719e07">(</span>metricsName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> response<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>*需要注意的是,PrometheusMetricsReporter 也支持使用 Qos 命令查询内部指标数据,同样有对应的 PrometheusMetricsReporterCmd 实现,它们的工作原理相似。</p> |
| <p>以上就是指标样本从收集完成到最终导出到外部指标中心的大致流程。</p></description></item><item><title>Blog: Dubbo 3 之 Triple 流控反压原理解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/12/28/dubbo-3-%E4%B9%8B-triple-%E6%B5%81%E6%8E%A7%E5%8F%8D%E5%8E%8B%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</link><pubDate>Wed, 28 Dec 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/12/28/dubbo-3-%E4%B9%8B-triple-%E6%B5%81%E6%8E%A7%E5%8F%8D%E5%8E%8B%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</guid><description> |
| <p>Triple 是 Dubbo 3 提出的基于 HTTP2 的开放协议, |
| 旨在解决 Dubbo 2 私有协议带来的互通性问题。 |
| Triple 基于 HTTP/2 定制自己的流控,支持通过特定的异常通知客户端业务层服务端负载高情况, |
| 保护了服务端被大流量击垮,提高系统高可用能力。</p> |
| <h2 id="一流控反压现状">一、流控反压现状</h2> |
| <p>客户端和服务器端在接收数据的时候有一个缓冲区来临时存储数据, |
| 但是缓冲区的大小是有限制的,所以有可能会出现缓冲区溢出的情况, |
| HTTP 通过流控保护数据溢出丢失风险。</p> |
| <h3 id="1http1-流控">1、HTTP/1 流控</h3> |
| <p>在 HTTP/1.1 中,流量的控制依赖的是底层TCP协议,在客户端和服务器端建立连接的时候, |
| 会使用系统默认的设置来建立缓冲区。在数据进行通信的时候,会告诉对方它的接收窗口的大小, |
| 这个接收窗口就是缓冲区中剩余的可用空间。如果接收窗口大小为零,则说明接收方缓冲区已满, |
| 则发送方将不再发送数据,直到客户端清除其内部缓冲区,然后请求恢复数据传输。</p> |
| <h3 id="2http2-流控">2、HTTP/2 流控</h3> |
| <p>HTTP/2 使用了多路复用机制,一个TCP连接可以有多个 HTTP/2 连接, |
| 故在 HTTP/2 中,有更加精细的流控制机制,允许服务端实现自己数据流和连接级的流控制。 |
| 服务端与客户端第一次连接时,会通过发送 <code>HTTP/2 SettingsFrame</code>设置初始化的流控窗口大小, |
| 用于 <code>Stream</code> 级别流控,默认为 65,535 字节。 |
| 定好流控窗口后,每次客户端发送数据就会减少流控窗口的大小, |
| 服务端收到数据后会发送窗口更新包(<code>WINDOW_UPDATE frame</code>)通知客户端更新窗口。 |
| 客户端收到窗口更新包后就会增加对应值的流控窗口,从而达到动态控制的目的。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/1.png" alt="1.png"></p> |
| <h2 id="二triple流控反压">二、Triple流控反压</h2> |
| <p>Netty 基于 HTTP/2 实现了基础的流控,当服务端负载过高,客户端发送窗口为 0 时, |
| 新增请求就无法被发送出去,会在缓存到客户端待发送请求队列中,缓存数据过大, |
| 就会造成客户端内存溢出,影响业务程序。</p> |
| <p>Triple 基于 Netty 实现了 HTTP/2 协议,通过 <code>HTTP/2 FlowController</code>接口统一封装, |
| 在实现分为进站(inbound)和出站(outbound)两个维度的实现。 |
| Triple 在 inbound 流量上使用了 Netty 的默认流控实现, |
| 在 outbound 上实现了自己流控,基于服务端负载, |
| 将服务端流量压力透传到客户端业务层,实现客户端的业务反压,暂停业务继续发送请求, |
| 保护服务端不被大流量击垮。</p> |
| <h3 id="1连接初始化">1、连接初始化</h3> |
| <p>Triple在初次建立连接时,通过 <code>TripleHttpProtocol</code> 初始化 HTTP/2 配置, |
| 默认流控窗口 <code>DEFAULT_WINDOW_INIT_SIZE = MIB_8</code>, |
| 并在服务端和客户端加入自己的 outbound 流控接口。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/2.png" alt="2.png"></p> |
| <h3 id="2inbound流控">2、Inbound流控</h3> |
| <p>Inbound 流量会通过 <code>DefaultHttpLocalFlowController</code> 的 <code>consumeBytes</code> 方法实现流控窗口更新与发送。</p> |
| <h4 id="1-入口传入http-流与更新数据大小">1) 入口传入HTTP 流与更新数据大小</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/3.png" alt="3.png"></p> |
| <h4 id="2-找到对应连接实现数据消费">2) 找到对应连接实现数据消费</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/4.png" alt="4.png"></p> |
| <h4 id="3-更新流控窗口">3) 更新流控窗口</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/5.png" alt="5.png"></p> |
| <h4 id="4-发送流控更新数据包window_update">4) 发送流控更新数据包(window_update)</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/6.png" alt="6.png"></p> |
| <h3 id="3outbound流控">3、Outbound流控</h3> |
| <p>Outbound 通过 Triple 自己的流控实现 <code>TriHttpRemoteFlowController</code>, |
| 将服务端压力反馈到业务层,保护服务端被大流量击垮。</p> |
| <h4 id="1-发送数据时判断是否还有窗口">1) 发送数据时判断是否还有窗口</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/7.png" alt="7.png"></p> |
| <h4 id="2-窗口为0时抛出特定异常">2) 窗口为0时抛出特定异常</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/8.png" alt="8.png"></p> |
| <h4 id="3-反馈客户端流控异常">3) 反馈客户端流控异常</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/9.png" alt="9.png"></p> |
| <h3 id="4总结">4、总结</h3> |
| <p>Triple 通过将底层客户端发送窗口为 0 场景封装为特定流控异常, |
| 透传至客户端上层业务,阻止客户端业务继续数据发送, |
| 有效的保护了服务端被大流量击垮和客户端的内存溢出的问题。</p> |
| <h2 id="三未来展望">三、未来展望</h2> |
| <p>目前 Triple 已经基本实现了流控反压能力,未来我们将深度联动业务, |
| 基于业务负载自适应调整反压流控, |
| 一是在 inbound 上将流控窗口包发送时机调整到服务端业务处理完成后, |
| 二是在 outbound 流量上关联客户端业务层,动态调整客户端发送速率。 |
| 从而实现基于服务端业务负载动态反压流控机制。</p></description></item><item><title>Blog: Triple 协议支持 Java 异常回传的设计与实现</title><link>https://dubbo.apache.org/zh-cn/blog/2022/12/19/triple-%E5%8D%8F%E8%AE%AE%E6%94%AF%E6%8C%81-java-%E5%BC%82%E5%B8%B8%E5%9B%9E%E4%BC%A0%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</link><pubDate>Mon, 19 Dec 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/12/19/triple-%E5%8D%8F%E8%AE%AE%E6%94%AF%E6%8C%81-java-%E5%BC%82%E5%B8%B8%E5%9B%9E%E4%BC%A0%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</guid><description> |
| <h2 id="背景">背景</h2> |
| <p>在一些业务场景, 往往需要自定义异常来满足特定的业务, 主流用法是在catch里抛出异常, 例如:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">deal</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span><span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//doSomething |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span><span style="color:#719e07">(</span>IGreeterException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>或者通过ExceptionBuilder,把相关的异常对象返回给consumer:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>provider<span style="color:#719e07">.</span>send<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ExceptionBuilders<span style="color:#719e07">.</span>IGreeterExceptionBuilder<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setDescription<span style="color:#719e07">(</span>&#39;异常描述信息&#39;<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>在抛出异常后, 通过捕获和instanceof来判断特定的异常, 然后做相应的业务处理,例如:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> greeterProxy<span style="color:#719e07">.</span>echo<span style="color:#719e07">(</span>REQUEST_MSG<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>IGreeterException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//做相应的处理 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在 Dubbo 2.x 版本,可以通过上述方法来捕获 Provider 端的异常。 |
| 而随着云原生时代的到来, Dubbo 也开启了 3.0 的里程碑。</p> |
| <p>Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生, |
| 在 3.0 的许多特性中,很重要的一个改动就是支持<strong>新的一代Rpc协议Triple</strong>。</p> |
| <p>Triple 协议基于 HTTP 2.0 进行构建,对网关的穿透性强,<strong>兼容 gRPC</strong>, |
| 提供 Request Response、Request Streaming、Response Streaming、 |
| Bi-directional Streaming 等通信模型; |
| 从 Triple 协议开始,Dubbo 还支持基于 IDL 的服务定义。</p> |
| <p>采用 Triple 协议的用户可以在 provider 端生成用户定义的异常信息, |
| 记录异常产生的堆栈,triple 协议可保证将用户在客户端获取到异常的message。</p> |
| <p>Triple 的回传异常会在 <code>AbstractInvoker</code> 的 <code>waitForResultIfSync</code> |
| 中把异常信息堆栈统一封装成 <code>RpcException</code>, |
| 所有来自 Provider 端的异常都会被封装成 <code>RpcException</code> 类型并抛出, |
| 这会导致用户无法根据特定的异常类型捕获来自 Provider 的异常, |
| 只能通过捕获 RpcException 异常来返回信息, |
| 且 Provider 携带的异常 message 也无法回传,只能获取打印的堆栈信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> greeterProxy<span style="color:#719e07">.</span>echo<span style="color:#719e07">(</span>REQUEST_MSG<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>RpcException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> e<span style="color:#719e07">.</span>printStackTrace<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>自定义异常信息在社区中的呼声也比较高, |
| 因此本次改动将支持自定义异常的功能, 使得服务端能抛出自定义异常后被客户端捕获到。</p> |
| <h2 id="dubbo异常处理简介">Dubbo异常处理简介</h2> |
| <p>我们从Consumer的角度看一下一次Triple协议 Unary请求的大致流程:</p> |
| <p>Dubbo Consumer 从 Spring 容器中获取 bean 时获取到的是一个代理接口, |
| 在调用接口的方法时会通过代理类远程调用接口并返回结果。</p> |
| <p>Dubbo提供的代理工厂类是 <code>ProxyFactory</code>,通过 SPI 机制默认实现的是 <code>JavassistProxyFactory</code>, |
| <code>JavassistProxyFactory</code> 创建了一个继承自 <code>AbstractProxyInvoker</code> 类的匿名对象, |
| 并重写了抽象方法 <code>doInvoke</code>。 |
| 重写后的 <code>doInvoke</code> 只是将调用请求转发给了 <code>Wrapper</code> 类的 <code>invokeMethod</code> 方法, |
| 并生成 <code>invokeMethod</code> 方法代码和其他一些方法代码。</p> |
| <p>代码生成完毕后,通过 <code>Javassist</code> 生成 <code>Class</code> 对象, |
| 最后再通过反射创建 <code>Wrapper</code> 实例,随后通过 <code>InvokerInvocationHandler</code> -&gt; <code>InvocationUtil</code> -&gt; <code>AbstractInvoker</code> -&gt; 具体实现类发送请求到Provider端。</p> |
| <p>Provider 进行相应的业务处理后返回相应的结果给 Consumer 端,来自 Provider 端的结果会被封装成 <code>AsyncResult</code> ,在 <code>AbstractInvoker</code> 的具体实现类里, |
| 接受到来自 Provider 的响应之后会调用 <code>appResponse</code> 到 <code>recreate</code> 方法,若 <code>appResponse</code> 里包含异常, |
| 则会抛出给用户,大体流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/1.jpeg" alt="1.jpeg"></p> |
| <p>上述的异常处理相关环节是在 Consumer 端,在 Provider 端则是由 <code>org.apache.dubbo.rpc.filter.ExceptionFilter</code> 进行处理, |
| 它是一系列责任链 Filter 中的一环,专门用来处理异常。</p> |
| <p>Dubbo 在 Provider 端的异常会在封装进 <code>appResponse</code> 中。下面的流程图揭示了 <code>ExceptionFilter</code> 源码的异常处理流程:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/2.jpeg" alt="2.jpeg"></p> |
| <p>而当 <code>appResponse</code> 回到了 Consumer 端,会在 <code>InvocationUtil</code> 里调用 <code>AppResponse</code> 的 <code>recreate</code> 方法抛出异常, |
| 最终可以在 Consumer 端捕获:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> Object <span style="color:#268bd2">recreate</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> Throwable <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>exception <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Object stackTrace <span style="color:#719e07">=</span> exception<span style="color:#719e07">.</span>getStackTrace<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>stackTrace <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exception<span style="color:#719e07">.</span>setStackTrace<span style="color:#719e07">(</span><span style="color:#719e07">new</span> StackTraceElement<span style="color:#719e07">[</span>0<span style="color:#719e07">]);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> exception<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="triple-通信原理">Triple 通信原理</h2> |
| <p>在上一节中,我们已经介绍了 Dubbo 在 Consumer 端大致发送数据的流程, |
| 可以看到最终依靠的是 <code>AbstractInvoker</code> 的实现类来发送数据。 |
| 在 Triple 协议中,<code>AbstractInvoker</code> 的具体实现类是 <code>TripleInvoker</code> , |
| <code>TripleInvoker</code> 在发送前会启动监听器,监听来自 Provider 端的响应结果, |
| 并调用 <code>ClientCallToObserverAdapter</code> 的 <code>onNext</code> 方法发送消息, |
| 最终会在底层封装成 Netty 请求发送数据。</p> |
| <p>在正式的请求发起前,TripleServer 会注册 <code>TripleHttp2FrameServerHandler</code>, |
| 它继承自 Netty 的 <code>ChannelDuplexHandler</code>, |
| 其作用是会在 <code>channelRead</code> 方法中不断读取 Header 和 Data 信息并解析, |
| 经过层层调用, |
| 会在 <code>AbstractServerCall</code> 的 <code>onMessage</code> 方法里把来自 consumer 的信息流进行反序列化, |
| 并最终由交由 <code>ServerCallToObserverAdapter</code> 的 <code>invoke</code> 方法进行处理。</p> |
| <p>在 <code>invoke</code> 方法中,根据 consumer 请求的数据调用服务端相应的方法,并异步等待结果;' |
| 若服务端抛出异常,则调用 <code>onError</code> 方法进行处理, |
| 否则,调用 <code>onReturn</code> 方法返回正常的结果,大致代码逻辑如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">invoke</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用invoke方法请求服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Result response <span style="color:#719e07">=</span> invoker<span style="color:#719e07">.</span>invoke<span style="color:#719e07">(</span>invocation<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//异步等待结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> response<span style="color:#719e07">.</span>whenCompleteWithContext<span style="color:#719e07">((</span>r<span style="color:#719e07">,</span> t<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//若异常不为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>t <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用方法过程出现异常,调用onError方法处理 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> responseObserver<span style="color:#719e07">.</span>onError<span style="color:#719e07">(</span>t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>response<span style="color:#719e07">.</span>hasException<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用onReturn方法处理业务异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onReturn<span style="color:#719e07">(</span>response<span style="color:#719e07">.</span>getException<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//正常返回结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onReturn<span style="color:#719e07">(</span>r<span style="color:#719e07">.</span>getValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>大体流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/3.jpeg" alt="3.jpeg"></p> |
| <h2 id="实现版本">实现版本</h2> |
| <p>了解了上述原理,我们就可以进行相应的改造了, |
| 能让 consumer 端捕获异常的<strong>关键在于把异常对象以及异常信息序列化后再发送给consumer端</strong>。 |
| 常见的序列化协议很多,例如 Dubbo/HSF 默认的 hessian2 序列化; |
| 还有使用广泛的 JSON 序列化;以及 gRPC 原生支持的 protobuf(PB) 序列化等等。 |
| Triple协议因为兼容grpc的原因,默认采用 Protobuf 进行序列化。 |
| 上述提到的这三种典型的序列化方案作用类似,但在实现和开发中略有不同。 |
| PB 不可由序列化后的字节流直接生成内存对象, |
| 而 Hessian 和 JSON 都是可以的。后两者反序列化的过程不依赖“二方包”, |
| 其序列化和反序列化的代码由 proto 文件相同,只要客户端和服务端用相同的 proto 文件进行通信, |
| 就可以构造出通信双方可解析的结构。</p> |
| <p>单一的 protobuf 无法序列化异常信息, |
| 因此我们采用 <code>Wrapper + PB</code> 的形式进行序列化异常信息, |
| 抽象出一个 <code>TripleExceptionWrapperUtils</code> 用于序列化异常, |
| 并在 <code>trailer</code> 中采用 <code>TripleExceptionWrapperUtils</code> 序列化异常,大致代码流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/4.jpeg" alt="4.jpeg"></p> |
| <p>上面的实现方案看似非常合理,已经能把 Provider 端的异常对象和信息回传, |
| 并在 Consumer 端进行捕获。但仔细想想还是有问题的: |
| 通常在 HTTP2 为基础的通信协议里会对 header 大小做一定的限制, |
| 太大的header size 会导致性能退化严重,为了保证性能, |
| 往往以 HTTP2 为基础的协议在建立连接的时候是要协商最大 header size 的, |
| 超过后会发送失败。对于 Triple 协议来说,在设计之初就是基于 HTTP 2.0, |
| 能无缝兼容 Grpc,而 Grpc header 头部只有 8KB 大小, |
| 异常对象大小可能超过限制,从而丢失异常信息; |
| 且多一个 header 携带序列化的异常信息意味着用户能加的 header 数量会减少, |
| 挤占了其他 header 所能占用的空间。</p> |
| <p>经过讨论,考虑将异常信息放置在 Body,将序列化后的异常从 trailer 挪至 body, |
| 采用 <code>TripleWrapper + protobuf</code> 进行序列化,把相关的异常信息序列化后回传。 |
| 社区围绕这个问题进行了一系列的争论,读者也可尝试先思考一下:</p> |
| <p>1.在 body 中携带回传的异常信息,其对应HTTP header状态码该设置为多少?</p> |
| <p>2.基于 http2 构建的协议,按照主流的 grpc 实现方案,相关的错误信息放在 <code>trailer</code>,理论上不存在body,上层协议也需要保持语义一致性,若此时在payload回传异常对象,且grpc并没有支持在Body回传序列化对象的功能, 会不会破坏Http和grpc协议的语义?从这个角度出发,异常信息更应该放在trailer里。</p> |
| <p>3.作为开源社区,不能一味满足用户的需求,非标准化的用法注定是会被淘汰的,应该尽量避免更改 Protobuf的语义,是否在Wrapper层去支持序列化异常就能满足需求?</p> |
| <p>首先回答第二、三个问题:HTTP 协议并没有约定在状态码非 2xx 的时候不能返回 body,返回之后是否读取取决于用户。grpc 采用protobuf进行序列化,所以无法返回 exception;且try catch机制为java独有,其他语言并没有对应的需求,但Grpc暂时不支持的功能并一定是unimplemented,Dubbo的设计目标之一是希望能和主流协议甚至架构进行对齐,但对于用户合理的需求也希望能进行一定程度的修改。且从throw本身的语义出发,throw 的数据不只是一个 error message,序列化的异常信息带有业务属性,根据这个角度,更不应该采用类似trailer的设计。至于单一的Wrapper层,也没办法和grpc进行互通。至于Http header状态码设置为200,因为其返回的异常信息已经带有一定的业务属性,不再是单纯的error,这个设计也与grpc保持一致,未来考虑网关采集可以增加新的triple-status。</p> |
| <p>更改后的版本只需在异常不为空时返回相关的异常信息,采用 <code>TripleWrapper + Protobuf</code> 进行序列化异常信息,并在consumer端进行解析和反序列化,大体流程如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/5.jpeg" alt="5.jpeg"></p> |
| <h2 id="总结">总结</h2> |
| <p>通过对 Dubbo 3.0 新增自定义异常的版本迭代中可以看出,尽管只能新增一个小小的特性,流程下并不复杂,但由于要考虑互通、兼容和协议的设计理念,因此思考和讨论的时间可能比写代码的时间更多。</p></description></item><item><title>Blog: 22-Dubbo3消费者自动感应决策应用级服务发现原理</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/22/22-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E8%87%AA%E5%8A%A8%E6%84%9F%E5%BA%94%E5%86%B3%E7%AD%96%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%8E%9F%E7%90%86/</link><pubDate>Mon, 22 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/22/22-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E8%87%AA%E5%8A%A8%E6%84%9F%E5%BA%94%E5%86%B3%E7%AD%96%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%8E%9F%E7%90%86/</guid><description> |
| <h1 id="22-dubbo3消费者自动感应决策应用级服务发现原理">22-Dubbo3消费者自动感应决策应用级服务发现原理</h1> |
| <h2 id="221-简介">22.1 简介</h2> |
| <p>这里要说的内容对Dubbo2迁移到Dubbo3的应用比较有帮助,消费者应用级服务发现做了一些自动决策的逻辑来决定当前消费者是应用级发现还是接口级服务发现,这里与前面说的提供者双注册的原理是对等的,提供者默认同时进行应用级注册和接口级注册,消费者对提供者注册的数据来决定使用应用级发现或者接口级发现。这些都是默认的行为,当然对于消费者来说还可以自定义其他的迁移规则,具体的需要我们详细来看逻辑。</p> |
| <p>如果说对于迁移过程比较感兴趣可以直接去官网看文档相对来说还是比较清晰:<a href="https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/">https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/</a></p> |
| <p>这里再借官网的图来用用,迁移过程主要如下所示: |
| 第一个图是提供者双注册的图: |
| <img src="https://dubbo.apache.org/imgs/v3/migration/provider-registration.png" alt="在这里插入图片描述"></p> |
| <p>第二个图是消费者订阅决策的图: |
| <img src="https://dubbo.apache.org/imgs/v3/migration/consumer-subscription.png" alt="在这里插入图片描述"></p> |
| <p>第三个图就是精确到消费者订阅的代码层的逻辑了,消费者服务间调用通过一个Invoker类型对象来进行对象,如下图所示消费者代理对象通过创建一个迁移容错的调用器对象来对应用级或者接口级订阅进行适配如下所示 |
| <img src="https://dubbo.apache.org/imgs/v3/migration/migration-cluster-invoker.png" alt="在这里插入图片描述"></p> |
| <p>第二个图和第三个图是重点要关注的这一个文章的内容主要就是说这里的逻辑。</p> |
| <p>关于代码位置如果不知道是如何调用到这一块逻辑的可以查看博文<a href="https://blog.elastic.link/2022/07/10/dubbo/21-dubbo-xiao-fei-zhe-yin-yong-fu-wu-de-ru-kou/">《21-Dubbo3消费者引用服务入口》</a></p> |
| <p>这里直接将代码位置定位到:RegistryProtocol类型的interceptInvoker方法中: |
| 如下所示:</p> |
| <p>RegistryProtocol类型的interceptInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">interceptInvoker</span><span style="color:#719e07">(</span>ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">,</span> URL url<span style="color:#719e07">,</span> URL consumerUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目前存在的扩展类型为RegistryProtocolListener监听器的实现类型MigrationRuleListener |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryProtocolListener<span style="color:#719e07">&gt;</span> listeners <span style="color:#719e07">=</span> findRegistryProtocolListeners<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>listeners<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>RegistryProtocolListener listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> listener<span style="color:#719e07">.</span>onRefer<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> invoker<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>该方法尝试加载所有RegistryProtocolListener定义,这些定义通过与定义的交互来控制调用器的行为,然后使用这些侦听器更改MigrationInvoker的状态和行为。当前可用的监听器是MigrationRuleListener,用于通过动态变化的规则控制迁移行为。</p> |
| <h2 id="222-migrationrulelistener-类型的onrefer方法">22.2 MigrationRuleListener 类型的onRefer方法</h2> |
| <p>直接来看代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onRefer</span><span style="color:#719e07">(</span>RegistryProtocol registryProtocol<span style="color:#719e07">,</span> ClusterInvoker<span style="color:#719e07">&lt;?&gt;</span> invoker<span style="color:#719e07">,</span> URL consumerUrl<span style="color:#719e07">,</span> URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个对应invoker对象的MigrationRuleHandler类型对象 然后将其存放在缓存Map&lt;MigrationInvoker, MigrationRuleHandler&gt;类型对象handles中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MigrationRuleHandler<span style="color:#719e07">&lt;?&gt;</span> migrationRuleHandler <span style="color:#719e07">=</span> handlers<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">((</span>MigrationInvoker<span style="color:#719e07">&lt;?&gt;)</span> invoker<span style="color:#719e07">,</span> _key <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">((</span>MigrationInvoker<span style="color:#719e07">&lt;?&gt;)</span> invoker<span style="color:#719e07">).</span>setMigrationRuleListener<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> MigrationRuleHandler<span style="color:#719e07">&lt;&gt;((</span>MigrationInvoker<span style="color:#719e07">&lt;?&gt;)</span> invoker<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//迁移规则执行 rule是封装了迁移的配置规则的信息对应类型MigrationRule类型,在初始化对象的时候进行了配置初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> migrationRuleHandler<span style="color:#719e07">.</span>doMigrate<span style="color:#719e07">(</span>rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>关于这个igrationRule的文可以直接看官方的文档比较详细:<a href="https://dubbo.apache.org/zh-cn/docs/advanced/migration-invoker/#1-%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%B8%8B%E5%8F%91%E6%8E%A8%E8%8D%90">地址迁移规则说明</a></p> |
| <p>这个迁移规则是为了更细粒度的迁移决策: |
| 相关配置可以参考下面这个样例:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">key</span>: 消费者应用名(必填) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">step</span>: 状态名(必填) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">threshold</span>: 决策阈值(默认1.0) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">proportion</span>: 灰度比例(默认100) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">delay</span>: 延迟决策时间(默认0) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">force</span>: 强制切换(默认 false) |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">interfaces</span>: 接口粒度配置(可选) |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">serviceKey: 接口名(接口 + </span>: + 版本号)(必填) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">threshold</span>: 决策阈值 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">proportion</span>: 灰度比例 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">delay</span>: 延迟决策时间 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">force</span>: 强制切换 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">step</span>: 状态名(必填) |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">serviceKey: 接口名(接口 + </span>: + 版本号) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">step</span>: 状态名 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">applications</span>: 应用粒度配置(可选) |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">serviceKey</span>: 应用名(消费的上游应用名)(必填) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">threshold</span>: 决策阈值 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">proportion</span>: 灰度比例 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">delay</span>: 延迟决策时间 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">force</span>: 强制切换 |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">step</span>: 状态名(必填) |
| </span></span></code></pre></div><p>不过为了简单起见暂时先不详细说这个配置细节,我们继续往下看</p> |
| <h2 id="223-迁移规则处理器执行迁移规则migrationrulehandler类型的domigrate方法">22.3 迁移规则处理器执行迁移规则MigrationRuleHandler类型的doMigrate方法</h2> |
| <h3 id="2231-迁移规则的模版方法">22.3.1 迁移规则的模版方法:</h3> |
| <p>MigrationRuleHandler类型的doMigrate方法代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doMigrate</span><span style="color:#719e07">(</span>MigrationRule rule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认情况下这个类型是MigrationInvoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>migrationInvoker <span style="color:#719e07">instanceof</span> ServiceDiscoveryMigrationInvoker<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> refreshInvoker<span style="color:#719e07">(</span>MigrationStep<span style="color:#719e07">.</span>FORCE_APPLICATION<span style="color:#719e07">,</span> 1<span style="color:#719e07">.</span>0f<span style="color:#719e07">,</span> rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//迁移步骤,MigrationStep 一共有3种枚举情况:FORCE_INTERFACE, APPLICATION_FIRST, FORCE_APPLICATION |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// initial step : APPLICATION_FIRST |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MigrationStep step <span style="color:#719e07">=</span> MigrationStep<span style="color:#719e07">.</span>APPLICATION_FIRST<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">float</span> threshold <span style="color:#719e07">=</span> <span style="color:#719e07">-</span>1f<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置的类型 默认走APPLICATION_FIRST |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> step <span style="color:#719e07">=</span> rule<span style="color:#719e07">.</span>getStep<span style="color:#719e07">(</span>consumerURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//threshold: 决策阈值(默认-1.0)计算与获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> threshold <span style="color:#719e07">=</span> rule<span style="color:#719e07">.</span>getThreshold<span style="color:#719e07">(</span>consumerURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to get step and threshold info from rule: &#34;</span> <span style="color:#719e07">+</span> rule<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷洗调用器对象 来进行决策服务发现模式 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>refreshInvoker<span style="color:#719e07">(</span>step<span style="color:#719e07">,</span> threshold<span style="color:#719e07">,</span> rule<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// refresh success, update rule |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setMigrationRule<span style="color:#719e07">(</span>rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2232-服务发现调用器对象的选择决策服务发现策略">22.3.2 服务发现调用器对象的选择(决策服务发现策略)</h3> |
| <p>这里就是关键代码了:通过迁移配置和当前提供者注册信息来决定创建什么类型的调用器对象(Invoker)来为后续服务调用做准备</p> |
| <p>MigrationRuleHandler的refreshInvoker,注意默认情况下这个step参数为APPLICATION_FIRST</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">refreshInvoker</span><span style="color:#719e07">(</span>MigrationStep step<span style="color:#719e07">,</span> Float threshold<span style="color:#719e07">,</span> MigrationRule newRule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>step <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> threshold <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Step or threshold of migration rule cannot be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> MigrationStep originStep <span style="color:#719e07">=</span> currentStep<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>currentStep <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> currentStep <span style="color:#719e07">!=</span> step<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> <span style="color:#719e07">!</span>currentThreshold<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>threshold<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> success <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">switch</span> <span style="color:#719e07">(</span>step<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> APPLICATION_FIRST<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认和配置了应用级优先的服务发现则走这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> migrationInvoker<span style="color:#719e07">.</span>migrateToApplicationFirstInvoker<span style="color:#719e07">(</span>newRule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> FORCE_APPLICATION<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置了应用级服务发现则走这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> success <span style="color:#719e07">=</span> migrationInvoker<span style="color:#719e07">.</span>migrateToForceApplicationInvoker<span style="color:#719e07">(</span>newRule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> FORCE_INTERFACE<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置了接口级服务发现则走这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">default</span><span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> success <span style="color:#719e07">=</span> migrationInvoker<span style="color:#719e07">.</span>migrateToForceInterfaceInvoker<span style="color:#719e07">(</span>newRule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>success<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> setCurrentStepAndThreshold<span style="color:#719e07">(</span>step<span style="color:#719e07">,</span> threshold<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Succeed Migrated to &#34;</span> <span style="color:#719e07">+</span> step <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; mode. Service Name: &#34;</span> <span style="color:#719e07">+</span> consumerURL<span style="color:#719e07">.</span>getDisplayServiceKey<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> report<span style="color:#719e07">(</span>step<span style="color:#719e07">,</span> originStep<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// migrate failed, do not save new step and rule |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Migrate to &#34;</span> <span style="color:#719e07">+</span> step <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; mode failed. Probably not satisfy the threshold you set &#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> threshold <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;. Please try re-publish configuration if you still after check.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> report<span style="color:#719e07">(</span>step<span style="color:#719e07">,</span> originStep<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;false&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> success<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore if step is same with previous, will continue override rule for MigrationInvoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看到这个代码做了判断的逻辑分别对应了Dubbo3消费者迁移的一个状态逻辑: |
| 三种状态分别如下枚举类型: |
| 当前共存在三种状态,</p> |
| <ul> |
| <li>FORCE_INTERFACE(强制接口级)</li> |
| <li>APPLICATION_FIRST(应用级优先)</li> |
| <li>FORCE_APPLICATION(强制应用级)</li> |
| </ul> |
| <p>通过代码我们可以看到默认情况下都会走APPLICATION_FIRST(应用级优先)的策略,这里我们也重点来说 APPLICATION_FIRST(应用级优先)来看下Dubbo3是如何决策使用接口级还是应用级发现模型来兼容迁移的服务的。</p> |
| <h3 id="2233-应用级优先的服务发现规则逻辑">22.3.3 应用级优先的服务发现规则逻辑</h3> |
| <p>这个规则就是智能选择应用级还是接口级的代码了,对应类型为MigrationInvoker的migrateToApplicationFirstInvoker方法,接下来我们详细看下:</p> |
| <p>MigrationInvoker类型的migrateToApplicationFirstInvoker方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">migrateToApplicationFirstInvoker</span><span style="color:#719e07">(</span>MigrationRule newRule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> CountDownLatch latch <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CountDownLatch<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新接口级服务发现Invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> refreshInterfaceInvoker<span style="color:#719e07">(</span>latch<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新应用级服务发现Invoker类型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> refreshServiceDiscoveryInvoker<span style="color:#719e07">(</span>latch<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// directly calculate preferred invoker, will not wait until address notify |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// calculation will re-occurred when address notify later |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//计算当前使用应用级还是接口级服务发现的Invoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> calcPreferredInvoker<span style="color:#719e07">(</span>newRule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2234-刷新接口级服务发现invoker">22.3.4 刷新接口级服务发现Invoker</h3> |
| <p>MigrationInvoker类型的refreshInterfaceInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshInterfaceInvoker</span><span style="color:#719e07">(</span>CountDownLatch latch<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> clearListener<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>needRefresh<span style="color:#719e07">(</span>invoker<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isDebugEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>debug<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Re-subscribing interface addresses for interface &#34;</span> <span style="color:#719e07">+</span> type<span style="color:#719e07">.</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>invoker <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> invoker<span style="color:#719e07">.</span>destroy<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> registryProtocol<span style="color:#719e07">.</span>getInvoker<span style="color:#719e07">(</span>cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> setListener<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> <span style="color:#719e07">()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> latch<span style="color:#719e07">.</span>countDown<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>reportService<span style="color:#719e07">.</span>hasReporter<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>reportConsumptionStatus<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>createConsumptionReport<span style="color:#719e07">(</span>consumerUrl<span style="color:#719e07">.</span>getServiceInterface<span style="color:#719e07">(),</span> consumerUrl<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">(),</span> consumerUrl<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;interface&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>step <span style="color:#719e07">==</span> APPLICATION_FIRST<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> calcPreferredInvoker<span style="color:#719e07">(</span>rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2235-刷新应用级服务发现invoker类型对象">22.3.5 刷新应用级服务发现Invoker类型对象</h3> |
| <p>MigrationInvoker类型的refreshServiceDiscoveryInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshServiceDiscoveryInvoker</span><span style="color:#719e07">(</span>CountDownLatch latch<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> clearListener<span style="color:#719e07">(</span>serviceDiscoveryInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>needRefresh<span style="color:#719e07">(</span>serviceDiscoveryInvoker<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isDebugEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>debug<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Re-subscribing instance addresses, current interface &#34;</span> <span style="color:#719e07">+</span> type<span style="color:#719e07">.</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>serviceDiscoveryInvoker <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceDiscoveryInvoker<span style="color:#719e07">.</span>destroy<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> serviceDiscoveryInvoker <span style="color:#719e07">=</span> registryProtocol<span style="color:#719e07">.</span>getServiceDiscoveryInvoker<span style="color:#719e07">(</span>cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> setListener<span style="color:#719e07">(</span>serviceDiscoveryInvoker<span style="color:#719e07">,</span> <span style="color:#719e07">()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> latch<span style="color:#719e07">.</span>countDown<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>reportService<span style="color:#719e07">.</span>hasReporter<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>reportConsumptionStatus<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>createConsumptionReport<span style="color:#719e07">(</span>consumerUrl<span style="color:#719e07">.</span>getServiceInterface<span style="color:#719e07">(),</span> consumerUrl<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">(),</span> consumerUrl<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;app&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>step <span style="color:#719e07">==</span> APPLICATION_FIRST<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> calcPreferredInvoker<span style="color:#719e07">(</span>rule<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2236-计算当前使用应用级还是接口级服务发现的invoker对象">22.3.6 计算当前使用应用级还是接口级服务发现的Invoker对象</h3> |
| <p>MigrationInvoker类型的的calcPreferredInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">calcPreferredInvoker</span><span style="color:#719e07">(</span>MigrationRule migrationRule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>serviceDiscoveryInvoker <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> invoker <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>MigrationAddressComparator<span style="color:#719e07">&gt;</span> detectors <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">(</span>consumerUrl <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">:</span> consumerUrl<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>MigrationAddressComparator<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>detectors<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// pick preferred invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// the real invoker choice in invocation will be affected by promotion |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>detectors<span style="color:#719e07">.</span>stream<span style="color:#719e07">().</span>allMatch<span style="color:#719e07">(</span>comparator <span style="color:#719e07">-&gt;</span> comparator<span style="color:#719e07">.</span>shouldMigrate<span style="color:#719e07">(</span>serviceDiscoveryInvoker<span style="color:#719e07">,</span> invoker<span style="color:#719e07">,</span> migrationRule<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>currentAvailableInvoker <span style="color:#719e07">=</span> serviceDiscoveryInvoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>currentAvailableInvoker <span style="color:#719e07">=</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>currentAvailableInvoker是后期服务调用使用的Invoker对象</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/22-dubbo3-xiao-fei-zhe-zi-dong-gan-ying-jue-ce-ying-yong-ji-fu-wu-fa-xian-yuan-li/">22-Dubbo3消费者自动感应决策应用级服务发现原理</a></p></description></item><item><title>Blog: 21-Dubbo3消费者引用服务入口</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/21/21-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E5%BC%95%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%85%A5%E5%8F%A3/</link><pubDate>Sun, 21 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/21/21-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E5%BC%95%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%85%A5%E5%8F%A3/</guid><description> |
| <h1 id="21-dubbo3消费者引用服务入口">21-Dubbo3消费者引用服务入口</h1> |
| <h2 id="211-简介">21.1 简介</h2> |
| <p>前面我们通过Demo说了一个服务引用配置的创建。另外也在前面的文章说了服务提供者的启动完整过程,不过在说服务提供者启动的过程中并未提到服务消费者是如何发现服务,如果调用服务的,这里先就不再说关于服务消费者启动的一个细节了,直接来看前面未提到的服务消费者是如何引用到服务提供者提供的服务的。 |
| 先来回顾下样例代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ConsumerApplication</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> runWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">runWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ReferenceConfig<span style="color:#719e07">&lt;</span>DemoService<span style="color:#719e07">&gt;</span> reference <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ReferenceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setGeneric<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-consumer&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setQosEnable<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setQosPort<span style="color:#719e07">(-</span>1<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://8.131.79.126:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>reference<span style="color:#719e07">(</span>reference<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DemoService demoService <span style="color:#719e07">=</span> bootstrap<span style="color:#719e07">.</span>getCache<span style="color:#719e07">().</span>get<span style="color:#719e07">(</span>reference<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> String message <span style="color:#719e07">=</span> demoService<span style="color:#719e07">.</span>sayHello<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span>message<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generic invoke |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> GenericService genericService <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>GenericService<span style="color:#719e07">)</span> demoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> Object genericInvokeResult <span style="color:#719e07">=</span> genericService<span style="color:#719e07">.</span>$invoke<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;sayHello&#34;</span><span style="color:#719e07">,</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[]{</span>String<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">()},</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> Object<span style="color:#719e07">[]{</span><span style="color:#2aa198">&#34;dubbo generic invoke&#34;</span><span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span>genericInvokeResult<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这段代码我们前面详细说了服务引用的配置ReferenceConfig和Dubbo启动器启动应用的过程DubboBootstrap,后面我们直接定位到消费者引用服务的代码位置来看。</p> |
| <h2 id="212-入口代码">21.2 入口代码</h2> |
| <h3 id="2121-defaultmoduledeployer的start方法">21.2.1 DefaultModuleDeployer的start方法</h3> |
| <p>第一个要关注的就是模块发布器DefaultModuleDeployer的start方法,这个start方法包含了Dubbo应用启动的过程</p> |
| <p>DefaultModuleDeployer的start方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> Future <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span>省略掉若干代码 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> onModuleStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// initialize |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// prepare application instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// exclude internal module to avoid wait itself |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModel <span style="color:#719e07">!=</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getInternalModule<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer<span style="color:#719e07">.</span>prepareInternalModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// refer services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span>省略掉若干代码 |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法大部分代码已经省略,也不会详细去说了,感兴趣的可以看之前讲到的博客,这里主要来看引用服务方法referServices</p> |
| <h3 id="2122-defaultmoduledeployer的referservices方法">21.2.2 DefaultModuleDeployer的referServices方法</h3> |
| <p>下面就要来看消费者应用如何引用的服务的入口了,这个方法主要从大的方面做了一些服务引用生命周期的代码,看懂了这个方法我们就可以不依赖Dubbo负载的启动逻辑可以单独调用ReferenceConfigBase类型的对应方法来刷新,启动,销毁引用的服务了这里先来看下代码再详细介绍内容:</p> |
| <p>DefaultModuleDeployer的referServices方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">referServices</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个是获取配置的所有的ReferenceConfigBase类型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>getReferences<span style="color:#719e07">().</span>forEach<span style="color:#719e07">(</span>rc <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ReferenceConfig<span style="color:#719e07">&lt;?&gt;</span> referenceConfig <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ReferenceConfig<span style="color:#719e07">&lt;?&gt;)</span> rc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>referenceConfig<span style="color:#719e07">.</span>isRefreshed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新引用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referenceConfig<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>rc<span style="color:#719e07">.</span>shouldInit<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>referAsync <span style="color:#719e07">||</span> rc<span style="color:#719e07">.</span>shouldReferAsync<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ExecutorService executor <span style="color:#719e07">=</span> executorRepository<span style="color:#719e07">.</span>getServiceReferExecutor<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> CompletableFuture<span style="color:#719e07">&lt;</span>Void<span style="color:#719e07">&gt;</span> future <span style="color:#719e07">=</span> CompletableFuture<span style="color:#719e07">.</span>runAsync<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//间接的通过缓存对象来引用服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referenceCache<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; refer async catch error : &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> executor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> asyncReferringFutures<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>future<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//间接的通过缓存对象来引用服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referenceCache<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; refer catch error.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//出现异常销毁引用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referenceCache<span style="color:#719e07">.</span>destroy<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> t<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在这个代码中我们核心需要关心的就是SimpleReferenceCache类型的get方法了,在获取服务对象之外包装了一层缓存。</p> |
| <p>如果出现了异常则执行referenceCache的destroy方法进行销毁引用配置。</p> |
| <h2 id="213-开始引用服务">21.3 开始引用服务</h2> |
| <h3 id="2131-simplereferencecache是什么">21.3.1 SimpleReferenceCache是什么?</h3> |
| <p>一个用于缓存引用ReferenceConfigBase的util工具类。 |
| ReferenceConfigBase是一个重对象,对于频繁创建ReferenceConfigBase的框架来说,有必要缓存这些对象。 |
| 如果需要使用复杂的策略,可以实现并使用自己的ReferenceConfigBase缓存 |
| 这个Cache是引用服务的开始如果我们想在代码中自定义一些服务引用的逻辑,可以直接创建SimpleReferenceCache类型对象然后调用其get方法进行引用服务。那这个缓存对象是和缓存与引用服务的可以继续往下看。</p> |
| <h3 id="2132-引用服务之前的缓存处理逻辑">21.3.2 引用服务之前的缓存处理逻辑?</h3> |
| <p>关于逻辑的处理,看代码有时候比文字更清晰明了,这里可以直接来看 SimpleReferenceCache类型的get方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@SuppressWarnings</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;unchecked&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T <span style="color:#268bd2">get</span><span style="color:#719e07">(</span>ReferenceConfigBase<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> rc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个生成的key规则是这样的 服务分组/服务接口:版本号 详细的代码就不看了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//例如: group/link.elastic.dubbo.entity.DemoService:1.0 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> generator<span style="color:#719e07">.</span>generateKey<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务类型 如果是泛化调用则这个类型为GenericService |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Class<span style="color:#719e07">&lt;?&gt;</span> type <span style="color:#719e07">=</span> rc<span style="color:#719e07">.</span>getInterfaceClass<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务是否为单例的这里默认值都为空,为单例模式 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> singleton <span style="color:#719e07">=</span> rc<span style="color:#719e07">.</span>getSingleton<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> rc<span style="color:#719e07">.</span>getSingleton<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> T proxy <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Check existing proxy of the same &#39;key&#39; and &#39;type&#39; first. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>singleton<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一般为单例的 这个方法是从缓存中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> proxy <span style="color:#719e07">=</span> get<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//非单例容易造成内存泄露,无法从缓存中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Using non-singleton ReferenceConfig and ReferenceCache at the same time may cause memory leak. &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;Call ReferenceConfig#get() directly for non-singleton ReferenceConfig instead of using ReferenceCache#get(ReferenceConfig)&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//前面是从缓存中拿,如果缓存中获取不到则开始引用服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>proxy <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取或者创建值,为引用类型referencesOfType对象(类型为Map&lt;Class&lt;?&gt;, List&lt;ReferenceConfigBase&lt;?&gt;&gt;&gt;)缓存对象生成值(值不存咋时候会生成一个) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>ReferenceConfigBase<span style="color:#719e07">&lt;?&gt;&gt;</span> referencesOfType <span style="color:#719e07">=</span> referenceTypeMap<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> _t <span style="color:#719e07">-&gt;</span> Collections<span style="color:#719e07">.</span>synchronizedList<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//每次走到这里都会添加一个ReferenceConfigBase 引用配置对象(单例的从缓存中拿到就可以直接返回了) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referencesOfType<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//与前面一样 前面是类型映射,这里是key映射 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>ReferenceConfigBase<span style="color:#719e07">&lt;?&gt;&gt;</span> referenceConfigList <span style="color:#719e07">=</span> referenceKeyMap<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> _k <span style="color:#719e07">-&gt;</span> Collections<span style="color:#719e07">.</span>synchronizedList<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;()));</span> |
| </span></span><span style="display:flex;"><span> referenceConfigList<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>rc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//开始引用服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> proxy <span style="color:#719e07">=</span> rc<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> proxy<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看到这个逻辑使用了享元模式(其实就是先查缓存,缓存不存在则创建对象存入缓存)来进行引用对象的管理这样一个过程,这里一共有两个缓存对象referencesOfType和referenceConfigList |
| key分别为引用类型和引用的服务的key,值是引用服务的基础配置对象列表List&lt;ReferenceConfigBase<?>&gt;</p> |
| <p>后面可以详细看下如果借助ReferenceConfigBase类型对象来进行具体类型的引用。</p> |
| <h2 id="214-初始化引用服务的过程">21.4 初始化引用服务的过程</h2> |
| <h3 id="2141-初始化引用服务的调用入口">21.4.1 初始化引用服务的调用入口</h3> |
| <p>引用服务的逻辑其实是相对复杂一点的,包含了服务发现,引用对象的创建等等,接下来就让我们详细看下:</p> |
| <p>ReferenceConfig类型的get方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> T <span style="color:#268bd2">get</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>destroyed<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The invoker of ReferenceConfig(&#34;</span> <span style="color:#719e07">+</span> url <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) has already destroyed!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ref类型为 transient volatile T ref; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ref <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ensure start module, compatible with old api usage |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这个前面已经调用了模块发布器启动过了,这里有这么一行代码是有一定作用的,如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化下面执行逻辑的时候出现问题,这个代码其实就是启动模块进行一些基础配置的初始化操作 比如元数据中心默认配置选择,注册中心默认配置选择这些都是比较重要的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getScopeModel<span style="color:#719e07">().</span>getDeployer<span style="color:#719e07">().</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ref <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> init<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> ref<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里有一段代码是:getScopeModel().getDeployer().start(); |
| 这个前面已经调用了模块发布器启动过了,这里有这么一行代码是有一定作用的,如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化下面执行逻辑的时候出现问题,这个代码其实就是启动模块进行一些基础配置的初始化操作 比如元数据中心默认配置选择,注册中心默认配置选择这些都是比较重要的。</p> |
| <p>另外可以看到的是这里使用了双重校验锁来保证单例对象的创建,发现Dubbo种大量的使用了双重校验锁的逻辑。</p> |
| <h3 id="2142-初始化引用服务">21.4.2 初始化引用服务</h3> |
| <p>这个就直接看代码了这,初始化过程相对复杂一点,我们一点点来看 |
| ReferenceConfig类型init()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化标记变量保证只初始化一次,这里又是加锁🔐又是加标记变量的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>isRefreshed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init serviceMetadata |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//初始化ServiceMetadata类型对象serviceMetadata 为其设置服务基本属性比如版本号,分组,服务接口名 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initServiceMetadata<span style="color:#719e07">(</span>consumer<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//继续初始化元数据信息 服务接口类型和key |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>setServiceType<span style="color:#719e07">(</span>getServiceInterfaceClass<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// TODO, uncomment this line once service key is unified |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>setServiceKey<span style="color:#719e07">(</span>URL<span style="color:#719e07">.</span>buildKey<span style="color:#719e07">(</span>interfaceName<span style="color:#719e07">,</span> group<span style="color:#719e07">,</span> version<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置转Map类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> referenceParameters <span style="color:#719e07">=</span> appendConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init service-application mapping |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//来自本地存储和url参数的初始化映射。 参数转URL配置初始化 Dubbo中喜欢用url作为配置的一种处理方式 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initServiceAppsMapping<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地内存模块服务存储库 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ModuleServiceRepository repository <span style="color:#719e07">=</span> getScopeModel<span style="color:#719e07">().</span>getServiceRepository<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ServiceModel和ServiceMetadata在某种程度上是相互重复的。我们将来应该合并它们。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceDescriptor serviceDescriptor<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>NATIVE_STUB<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>getProxy<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceDescriptor <span style="color:#719e07">=</span> StubSuppliers<span style="color:#719e07">.</span>getServiceDescriptor<span style="color:#719e07">(</span>interfaceName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>serviceDescriptor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地存储库注册服务接口类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceDescriptor <span style="color:#719e07">=</span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>interfaceClass<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//消费者模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> consumerModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConsumerModel<span style="color:#719e07">(</span>serviceMetadata<span style="color:#719e07">.</span>getServiceKey<span style="color:#719e07">(),</span> proxy<span style="color:#719e07">,</span> serviceDescriptor<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> getScopeModel<span style="color:#719e07">(),</span> serviceMetadata<span style="color:#719e07">,</span> createAsyncMethodInfo<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地存储库注册消费者模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> repository<span style="color:#719e07">.</span>registerConsumer<span style="color:#719e07">(</span>consumerModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//与前面代码一样基础初始化服务元数据对象为其设置附加参数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>getAttachments<span style="color:#719e07">().</span>putAll<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务的代理对象 !!!核心代码在这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ref <span style="color:#719e07">=</span> createProxy<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为服务元数据对象设置代理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>setTarget<span style="color:#719e07">(</span>ref<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceMetadata<span style="color:#719e07">.</span>addAttribute<span style="color:#719e07">(</span>PROXY_CLASS_REF<span style="color:#719e07">,</span> ref<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> consumerModel<span style="color:#719e07">.</span>setProxyObject<span style="color:#719e07">(</span>ref<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> consumerModel<span style="color:#719e07">.</span>initMethodModels<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查invoker对象初始结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkInvokerAvailable<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="215-referenceconfig创建服务引用代理对象的原理">21.5 ReferenceConfig创建服务引用代理对象的原理</h2> |
| <h3 id="2151-代理对象的创建过程">21.5.1 代理对象的创建过程</h3> |
| <p>这里就要继续看 ReferenceConfig类型的创建代理方法createProxy了 |
| 直接贴一下源码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> T <span style="color:#268bd2">createProxy</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> referenceParameters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地引用 这里为false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>shouldJvmRefer<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> createInvokerForLocal<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> urls<span style="color:#719e07">.</span>clear<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//url存在则为点对点引用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// user specified URL, could be peer-to-peer address, or register center&#39;s address. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> parseUrl<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if protocols not in jvm checkRegistry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里不是local协议默认这里为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>LOCAL_PROTOCOL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从注册表中获取URL并将其聚合。这个其实就是初始化一下注册中心的url配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> aggregateUrlFromRegistry<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个代码非常重要 创建远程引用,创建远程引用调用器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> createInvokerForRemote<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Referred dubbo service: [&#34;</span> <span style="color:#719e07">+</span> referenceParameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;].&#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">(</span>Boolean<span style="color:#719e07">.</span>parseBoolean<span style="color:#719e07">(</span>referenceParameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>GENERIC_KEY<span style="color:#719e07">))</span> <span style="color:#719e07">?</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34; it&#39;s GenericService reference&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34; it&#39;s not GenericService reference&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> URL consumerUrl <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfigURL<span style="color:#719e07">(</span>CONSUMER_PROTOCOL<span style="color:#719e07">,</span> referenceParameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>REGISTER_IP_KEY<span style="color:#719e07">),</span> 0<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> referenceParameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">),</span> referenceParameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> consumerUrl <span style="color:#719e07">=</span> consumerUrl<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>getScopeModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> consumerUrl <span style="color:#719e07">=</span> consumerUrl<span style="color:#719e07">.</span>setServiceModel<span style="color:#719e07">(</span>consumerModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> MetadataUtils<span style="color:#719e07">.</span>publishServiceDefinition<span style="color:#719e07">(</span>consumerUrl<span style="color:#719e07">,</span> consumerModel<span style="color:#719e07">.</span>getServiceModel<span style="color:#719e07">(),</span> getApplicationModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// create service proxy |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> proxyFactory<span style="color:#719e07">.</span>getProxy<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> ProtocolUtils<span style="color:#719e07">.</span>isGeneric<span style="color:#719e07">(</span>generic<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2152-创建远程引用创建远程引用调用器">21.5.2 创建远程引用,创建远程引用调用器</h3> |
| <p>ReferenceConfig类型的createInvokerForRemote方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">createInvokerForRemote</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个url 为注册协议如registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&amp;dubbo=2.0.2&amp;pid=6204&amp;qos.enable=false&amp;qos.port=-1&amp;registry=zookeeper&amp;release=3.0.9&amp;timestamp=1657439419495 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>urls<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> URL curUrl <span style="color:#719e07">=</span> urls<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个SPI对象是由字节码动态生成的自适应对象Protocol$Adaptie直接看看不到源码,后续可以解析一个字节码生成的类型,这里后续来调用链路即可 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invoker <span style="color:#719e07">=</span> protocolSPI<span style="color:#719e07">.</span>refer<span style="color:#719e07">(</span>interfaceClass<span style="color:#719e07">,</span> curUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>curUrl<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;?&gt;&gt;</span> invokers <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> invokers<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> Cluster<span style="color:#719e07">.</span>DEFAULT<span style="color:#719e07">).</span>join<span style="color:#719e07">(</span><span style="color:#719e07">new</span> StaticDirectory<span style="color:#719e07">(</span>curUrl<span style="color:#719e07">,</span> invokers<span style="color:#719e07">),</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;?&gt;&gt;</span> invokers <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> URL registryUrl <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>URL url <span style="color:#719e07">:</span> urls<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// For multi-registry scenarios, it is not checked whether each referInvoker is available. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// Because this invoker may become available later. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invokers<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>protocolSPI<span style="color:#719e07">.</span>refer<span style="color:#719e07">(</span>interfaceClass<span style="color:#719e07">,</span> url<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// use last registry url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryUrl <span style="color:#719e07">=</span> url<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registryUrl <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// registry url is available |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// for multi-subscription scenario, use &#39;zone-aware&#39; policy by default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String cluster <span style="color:#719e07">=</span> registryUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CLUSTER_KEY<span style="color:#719e07">,</span> ZoneAwareCluster<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -&gt; FailoverClusterInvoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// (RegistryDirectory, routing happens here) -&gt; Invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invoker <span style="color:#719e07">=</span> Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">(),</span> cluster<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">).</span>join<span style="color:#719e07">(</span><span style="color:#719e07">new</span> StaticDirectory<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">,</span> invokers<span style="color:#719e07">),</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// not a registry url, must be direct invoke. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>invokers<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;invokers == null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> URL curUrl <span style="color:#719e07">=</span> invokers<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">).</span>getUrl<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String cluster <span style="color:#719e07">=</span> curUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CLUSTER_KEY<span style="color:#719e07">,</span> Cluster<span style="color:#719e07">.</span>DEFAULT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> cluster<span style="color:#719e07">).</span>join<span style="color:#719e07">(</span><span style="color:#719e07">new</span> StaticDirectory<span style="color:#719e07">(</span>curUrl<span style="color:#719e07">,</span> invokers<span style="color:#719e07">),</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="2153-invoker对象创建的全过程">21.5.3 Invoker对象创建的全过程</h3> |
| <p>为了更好理解Protocol$Adaptie内部的引用执行过程这里我把Debug的链路截图了过来 |
| 按照固定的顺序先执行AOP的逻辑再执行具体的逻辑:</p> |
| <ul> |
| <li>Protocol$Adaptie的refer方法</li> |
| <li>ProtocolSerializationWrapper AOP类型的协议序列化器refer方法</li> |
| <li>ProtocolFilterWrapper AOP类型的协议过滤器的refer方法</li> |
| <li>QosProtocolWrapper AOP类型的QOS协议包装器的refer方法</li> |
| <li>ProtocolListenerWrapper APO类型监听器包装器的refer方法</li> |
| <li>RegistryProtocol 注册协议的refer方法 (会添加容错逻辑)</li> |
| <li>RegistryProtocol 注册协议的doRefer方法(调用方法创建Invoker对象)</li> |
| </ul> |
| <p><a href="https://dubbo.apache.org/imgs/blog/source-blog/21-createInvokerRemote.png"></a></p> |
| <p>这里我们不再详细说这个引用链的具体过程直接定位到RegistryProtocol中创建Invoker类型的地方。 |
| 先来看RegistryProtocol类型的refer方法,如下代码所示:</p> |
| <p>RegistryProtocol类型的refer方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@SuppressWarnings</span><span style="color:#719e07">(</span><span style="color:#2aa198">&#34;unchecked&#34;</span><span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">refer</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个url已经被转换为具体的注册中心协议类型了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&amp;dubbo=2.0.2&amp;pid=7944&amp;qos.enable=false&amp;qos.port=-1&amp;release=3.0.9&amp;timestamp=1657440673100 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> getRegistryUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取用于操作Zookeeper的Registry类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>RegistryService<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>type<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> proxyFactory<span style="color:#719e07">.</span>getInvoker<span style="color:#719e07">((</span>T<span style="color:#719e07">)</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// group=&#34;a,b&#34; or group=&#34;*&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> qs <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;)</span> url<span style="color:#719e07">.</span>getAttribute<span style="color:#719e07">(</span>REFER_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> String group <span style="color:#719e07">=</span> qs<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>GROUP_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>group<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>COMMA_SPLIT_PATTERN<span style="color:#719e07">.</span>split<span style="color:#719e07">(</span>group<span style="color:#719e07">)).</span>length <span style="color:#719e07">&gt;</span> 1 <span style="color:#719e07">||</span> <span style="color:#2aa198">&#34;*&#34;</span><span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>group<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> doRefer<span style="color:#719e07">(</span>Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">(),</span> MergeableCluster<span style="color:#719e07">.</span>NAME<span style="color:#719e07">),</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> qs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//降级容错的逻辑处理对象 类型为Cluster 实际类型为MockClusterWrapper 内部包装的是FailoverCluster |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//后续调用服务失败时候会先失效转移再降级 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Cluster cluster <span style="color:#719e07">=</span> Cluster<span style="color:#719e07">.</span>getCluster<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">(),</span> qs<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>CLUSTER_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里才是具体的Invoker对象的创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> doRefer<span style="color:#719e07">(</span>cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> qs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>RegistryProtocol类型的doRefer方法创建Invoker对象 |
| 直接来看代码了</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">doRefer</span><span style="color:#719e07">(</span>Cluster cluster<span style="color:#719e07">,</span> Registry registry<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> URL url<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> parameters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> consumerAttribute <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;(</span>url<span style="color:#719e07">.</span>getAttributes<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> consumerAttribute<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>REFER_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> String p <span style="color:#719e07">=</span> isEmpty<span style="color:#719e07">(</span>parameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>PROTOCOL_KEY<span style="color:#719e07">))</span> <span style="color:#719e07">?</span> CONSUMER <span style="color:#719e07">:</span> parameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>PROTOCOL_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> URL consumerUrl <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfigURL <span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> p<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> parameters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>REGISTER_IP_KEY<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> 0<span style="color:#719e07">,</span> getPath<span style="color:#719e07">(</span>parameters<span style="color:#719e07">,</span> type<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> parameters<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> consumerAttribute |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>putAttribute<span style="color:#719e07">(</span>CONSUMER_URL_KEY<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重点看这一行 带迁移性质的Invoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> migrationInvoker <span style="color:#719e07">=</span> getMigrationInvoker<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一行回来执行迁移规则创建应用级优先的服务发现Invoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> interceptInvoker<span style="color:#719e07">(</span>migrationInvoker<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里代码比较重要的其实只有两行getMigrationInvoker和interceptInvoker方法 |
| 比较核心也是Dubbo3比较重要的消费者启动逻辑基本都在这个方法里面interceptInvoker,这个方法执行了消费者应用级发现和接口级发现迁移的逻辑,会自动帮忙决策一个Invoker类型对象,不过这个逻辑这里先简单看下,后续单独整个文章来说。</p> |
| <p>这里我们先来看 ClusterInvoker对象的创建,下面先看代码:</p> |
| <p>RegistryProtocol类型的getMigrationInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getMigrationInvoker</span><span style="color:#719e07">(</span>RegistryProtocol registryProtocol<span style="color:#719e07">,</span> Cluster cluster<span style="color:#719e07">,</span> Registry registry<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> URL url<span style="color:#719e07">,</span> URL consumerUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ServiceDiscoveryMigrationInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>registryProtocol<span style="color:#719e07">,</span> cluster<span style="color:#719e07">,</span> registry<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>详细的逻辑这里就不再看了,我们继续看RegistryProtocol类型的interceptInvoker方法:</p> |
| <p>具体代码如下: |
| RegistryProtocol类型的interceptInvoker方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">interceptInvoker</span><span style="color:#719e07">(</span>ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">,</span> URL url<span style="color:#719e07">,</span> URL consumerUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取激活的注册协议监听器扩展里面registry.protocol.listener,这里激活的类型为MigrationRuleListener |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryProtocolListener<span style="color:#719e07">&gt;</span> listeners <span style="color:#719e07">=</span> findRegistryProtocolListeners<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>listeners<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>RegistryProtocolListener listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里执行MigrationRuleListener类型的onRefer方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> listener<span style="color:#719e07">.</span>onRefer<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> invoker<span style="color:#719e07">,</span> consumerUrl<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>该方法尝试加载所有RegistryProtocolListener定义,这些定义通过与定义的交互来控制调用器的行为,然后使用这些侦听器更改MigrationInvoker的状态和行为。 |
| 当前可用的监听器是MigrationRuleListener,用于通过动态变化的规则控制迁移行为。</p> |
| <p>可以看到核心的逻辑集中在了这个位置MigrationRuleListener类型的onRefer方法,这个这里就不深入往下说了,后续会有个文章专门来看Dubbo2迁移Dubbo3时候处理的逻辑。</p> |
| <p>Invoker对象的创建完成其实就代表了服务引用执行完成,不过这里核心的协议并没有来说</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/21-dubbo-xiao-fei-zhe-yin-yong-fu-wu-de-ru-kou/">21-Dubbo3消费者引用服务入口</a></p></description></item><item><title>Blog: 20-Dubbo3服务引用配置ReferenceConfig</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/20/20-dubbo3%E6%9C%8D%E5%8A%A1%E5%BC%95%E7%94%A8%E9%85%8D%E7%BD%AEreferenceconfig/</link><pubDate>Sat, 20 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/20/20-dubbo3%E6%9C%8D%E5%8A%A1%E5%BC%95%E7%94%A8%E9%85%8D%E7%BD%AEreferenceconfig/</guid><description> |
| <h1 id="20-dubbo3服务引用配置referenceconfig">20-Dubbo3服务引用配置ReferenceConfig</h1> |
| <h2 id="201-简介">20.1 简介</h2> |
| <p>前面简单介绍了一下消费者的例子,消费者创建的第一步就是先进行消费者信息的配置对应类型为ReferenceConfig,这里详细来看ReferenceConfig包含哪些信息?先简单了解下消费者配置的类型关系如下图所示:引用配置与服务配置类型都是通过继承接口配置来扩展的,在分析生产者的时候详细介绍过服务相关的配置,这里来详细看消费者引用者的相关配置信息. |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/20-refe.png" alt="在这里插入图片描述"></p> |
| <p>前面例子说了消费者配置对象的创建主要是通过如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ReferenceConfig<span style="color:#719e07">&lt;</span>DemoService<span style="color:#719e07">&gt;</span> reference <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ReferenceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span></code></pre></div><p>这个配置类型的对象创建过程并没有太多的逻辑这里主要来说下各种配置信息: |
| 服务消费者引用服务配置。对应的配置类: <code>org.apache.dubbo.config.ReferenceConfig</code></p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>id</td> |
| <td></td> |
| <td>string</td> |
| <td><strong>必填</strong></td> |
| <td></td> |
| <td>配置关联</td> |
| <td>服务引用BeanId</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>interface</td> |
| <td></td> |
| <td>class</td> |
| <td><strong>必填</strong></td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务接口名</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>version</td> |
| <td>version</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务版本,与服务提供者的版本一致</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>group</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务分组,当一个接口有多个实现,可以用分组区分,必需和服务提供方一致</td> |
| <td>1.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>timeout</td> |
| <td>long</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的timeout</td> |
| <td>性能调优</td> |
| <td>服务方法调用超时时间(毫秒)</td> |
| <td>1.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>retries</td> |
| <td>retries</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的retries</td> |
| <td>性能调优</td> |
| <td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>connections</td> |
| <td>connections</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的connections</td> |
| <td>性能调优</td> |
| <td>对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>loadbalance</td> |
| <td>loadbalance</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的loadbalance</td> |
| <td>性能调优</td> |
| <td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>async</td> |
| <td>async</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的async</td> |
| <td>性能调优</td> |
| <td>是否异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>generic</td> |
| <td>generic</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的generic</td> |
| <td>服务治理</td> |
| <td>是否缺省泛化接口,如果为泛化接口,将返回GenericService</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>check</td> |
| <td>check</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>缺省使用<a href="dubbo:consumer">dubbo:consumer</a>的check</td> |
| <td>服务治理</td> |
| <td>启动时检查提供者是否存在,true报错,false忽略</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>url</td> |
| <td>url</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>点对点直连服务提供者地址,将绕过注册中心</td> |
| <td>1.0.6以上版本</td> |
| </tr> |
| <tr> |
| <td>stub</td> |
| <td>stub</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>mock</td> |
| <td>mock</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务接口调用失败Mock实现类名,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。</td> |
| <td>Dubbo1.0.13及其以上版本支持</td> |
| </tr> |
| <tr> |
| <td>cache</td> |
| <td>cache</td> |
| <td>string/boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等</td> |
| <td>Dubbo2.1.0及其以上版本支持</td> |
| </tr> |
| <tr> |
| <td>validation</td> |
| <td>validation</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验</td> |
| <td>Dubbo2.1.0及其以上版本支持</td> |
| </tr> |
| <tr> |
| <td>proxy</td> |
| <td>proxy</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>javassist</td> |
| <td>性能调优</td> |
| <td>选择动态代理实现策略,可选:javassist, jdk</td> |
| <td>2.0.2以上版本</td> |
| </tr> |
| <tr> |
| <td>client</td> |
| <td>client</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>性能调优</td> |
| <td>客户端传输类型设置,如Dubbo协议的netty或mina。</td> |
| <td>Dubbo2.0.0以上版本支持</td> |
| </tr> |
| <tr> |
| <td>registry</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省将从所有注册中心获服务列表后合并结果</td> |
| <td>配置关联</td> |
| <td>从指定注册中心注册获取服务列表,在多个注册中心时使用,值为<a href="dubbo:registry">dubbo:registry</a>的id属性,多个注册中心ID用逗号分隔</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>owner</td> |
| <td>owner</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>调用服务负责人,用于服务治理,请填写负责人公司邮箱前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>actives</td> |
| <td>actives</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>每服务消费者每服务每方法最大并发调用数</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>cluster</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>failover</td> |
| <td>性能调优</td> |
| <td>集群方式,可选:failover/failfast/failsafe/failback/forking</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>filter</td> |
| <td>reference.filter</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>default</td> |
| <td>性能调优</td> |
| <td>服务消费方远程调用过程拦截器名称,多个名称用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>listener</td> |
| <td>invoker.listener</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>default</td> |
| <td>性能调优</td> |
| <td>服务消费方引用服务监听器名称,多个名称用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>layer</td> |
| <td>layer</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务调用者所在的分层。如:biz、dao、intl:web、china:acton。</td> |
| <td>2.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>init</td> |
| <td>init</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>性能调优</td> |
| <td>是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化。</td> |
| <td>2.0.10以上版本</td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td>protocol</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>只调用指定协议的服务提供方,其它协议忽略。</td> |
| <td></td> |
| </tr> |
| </tbody> |
| </table> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/20-dubbo3-fu-wu-yin-yong-pei-zhi-referenceconfig/">20-Dubbo3服务引用配置ReferenceConfig</a></p></description></item><item><title>Blog: 19 重新来过从一个服务消费者的Demo说起</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/19/19-%E9%87%8D%E6%96%B0%E6%9D%A5%E8%BF%87%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%B6%88%E8%B4%B9%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</link><pubDate>Fri, 19 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/19/19-%E9%87%8D%E6%96%B0%E6%9D%A5%E8%BF%87%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%B6%88%E8%B4%B9%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</guid><description> |
| <h1 id="19-重新来过从一个服务消费者的demo说起">19 重新来过从一个服务消费者的Demo说起</h1> |
| <p>为了更方便了解原理,我们先来编写一个Demo,从例子中来看源码实现:,前面说了提供者现在已经有服务注册上去了,那接下来我们编写一个消费者的例子来进行服务发现与服务RPC调用。</p> |
| <h2 id="191-启动zookeeper">19.1 启动Zookeeper</h2> |
| <p>为了Demo可以正常启动,需要我们先在本地启动一个Zookeeper如下图所示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/19-zk.png" alt="在这里插入图片描述"></p> |
| <h2 id="192-服务消费者">19.2 服务消费者</h2> |
| <p>接下来给大家贴一下示例源码,这个源码来源于Dubbo源码目录的 dubbo-demo/dubbo-demo-api 目录下面的dubbo-demo-api-consumer子项目,这里我做了删减,方便看核心代码: |
| 首先我们定义一个服务接口如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">import</span> java.util.concurrent.CompletableFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">DemoService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 同步处理的服务方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 用于异步处理的服务方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture<span style="color:#719e07">.</span>completedFuture<span style="color:#719e07">(</span>sayHello<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>服务实现类如下: |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.rpc.RpcContext<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.Logger<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.LoggerFactory<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> java.util.concurrent.CompletableFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceImpl</span> <span style="color:#268bd2">implements</span> DemoService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> Logger logger <span style="color:#719e07">=</span> LoggerFactory<span style="color:#719e07">.</span>getLogger<span style="color:#719e07">(</span>DemoServiceImpl<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, request from consumer: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getServiceContext<span style="color:#719e07">().</span>getRemoteAddress<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, response from provider: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getServiceContext<span style="color:#719e07">().</span>getLocalAddress<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="193-启用服务消费者">19.3 启用服务消费者</h2> |
| <p>有了服务接口之后我们来启用服务,启用服务的源码如下: |
| 这里如果要启动消费者,主要要修改QOS端口这里我已经配置可以直接复用</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">package</span> link.elastic.dubbo.consumer<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> link.elastic.dubbo.entity.DemoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.common.constants.CommonConstants<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ApplicationConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.MetadataReportConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ProtocolConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ReferenceConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.RegistryConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.bootstrap.DubboBootstrap<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.rpc.service.GenericService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ConsumerApplication</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> runWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">runWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ReferenceConfig<span style="color:#719e07">&lt;</span>DemoService<span style="color:#719e07">&gt;</span> reference <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ReferenceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setGeneric<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reference<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-consumer&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setQosEnable<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> applicationConfig<span style="color:#719e07">.</span>setQosPort<span style="color:#719e07">(-</span>1<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://8.131.79.126:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>reference<span style="color:#719e07">(</span>reference<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DemoService demoService <span style="color:#719e07">=</span> bootstrap<span style="color:#719e07">.</span>getCache<span style="color:#719e07">().</span>get<span style="color:#719e07">(</span>reference<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> String message <span style="color:#719e07">=</span> demoService<span style="color:#719e07">.</span>sayHello<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span>message<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generic invoke |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> GenericService genericService <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>GenericService<span style="color:#719e07">)</span> demoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> Object genericInvokeResult <span style="color:#719e07">=</span> genericService<span style="color:#719e07">.</span>$invoke<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;sayHello&#34;</span><span style="color:#719e07">,</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[]{</span>String<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">()},</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> Object<span style="color:#719e07">[]{</span><span style="color:#2aa198">&#34;dubbo generic invoke&#34;</span><span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> System<span style="color:#719e07">.</span>out<span style="color:#719e07">.</span>println<span style="color:#719e07">(</span>genericInvokeResult<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="14-启用服务后写入zookeeper的节点数据">1.4 启用服务后写入Zookeeper的节点数据</h2> |
| <p>启动服务,这个时候我们打开Zookeeper图形化客户端来看看这个服务在Zookeeper上面写入来哪些数据,如下图: |
| 在这里插入图片描述 |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/19-zk2.png" alt="在这里插入图片描述"></p> |
| <p>写入Zookeper上的节点用于服务在分布式场景下的协调,这些节点是比较重要的。</p> |
| <p>如果了解过Dubbo的同学,应该会知道Dubbo在低版本的时候会向注册中心中写入服务接口,具体路径在上面的 <strong>dubbo目录下</strong> ,然后在 <strong>/dubbo/服务接口/</strong> 路径下写入如下信息:</p> |
| <ul> |
| <li><strong>服务提供者</strong>配置信息URL形式</li> |
| <li><strong>服务消费者</strong>的配置信息URL形式</li> |
| <li>服务<strong>路由信息</strong></li> |
| <li><strong>配置信息</strong></li> |
| </ul> |
| <p>上面这个图就是Dubbo3的注册信息了,后面我们也会围绕细节来说明下,这里可以看下新增了:</p> |
| <ul> |
| <li>/dubbo/metadata <strong>元数据信息</strong></li> |
| <li>/dubbo/mapping 服务和应用的<strong>映射信息</strong></li> |
| <li>/dubbo/config <strong>注册中心配置</strong></li> |
| <li>/services目录<strong>应用信息</strong></li> |
| </ul> |
| <p>在这里可以大致了解下,在后面会有更详细的源码解析这个示例代码.通过透析代码来看透Dubbo3服务注册原理,服务提供原理。</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/19-chong-xin-lai-guo-cong-yi-ge-fu-wu-xiao-fei-zhe-de-demo-shuo-qi/">19-重新来过从一个服务消费者的Demo说起</a></p></description></item><item><title>Blog: 18-Dubbo3元数据服务MetadataService的导出</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/18/18-dubbo3%E5%85%83%E6%95%B0%E6%8D%AE%E6%9C%8D%E5%8A%A1metadataservice%E7%9A%84%E5%AF%BC%E5%87%BA/</link><pubDate>Thu, 18 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/18/18-dubbo3%E5%85%83%E6%95%B0%E6%8D%AE%E6%9C%8D%E5%8A%A1metadataservice%E7%9A%84%E5%AF%BC%E5%87%BA/</guid><description> |
| <h1 id="18-dubbo3元数据服务metadataservice的导出">18-Dubbo3元数据服务MetadataService的导出</h1> |
| <h2 id="181-简介">18.1 简介</h2> |
| <p>MetadataService |
| 此服务用于公开Dubbo进程内的元数据信息。典型用途包括:</p> |
| <ul> |
| <li>使用者查询提供者的元数据信息,以列出接口和每个接口的配置</li> |
| <li>控制台(dubbo admin)查询特定进程的元数据,或聚合所有进程的数据。在Dubbo2.x的时候,所有的服务数据都是以接口的形式注册在注册中心.</li> |
| </ul> |
| <p>Dubbo3将部分数据抽象为元数据的形式来将数据存放在元数据中心,然后元数据由服务提供者提供给消费者而不是再由注册中心进行推送,如下图所示:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/18-metadata.png" alt="在这里插入图片描述"></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/18-metadata3.png" alt="在这里插入图片描述"> |
| 引入 MetadataService 元数据服务服务的好处 |
| • 由中心化推送转向点对点拉取(Consumer - Proroder) |
| • 易于扩展更多的参数 |
| • 更多的数据量 |
| • 对外暴露更多的治理数据</p> |
| <h2 id="182-metadataservice的导出过程">18.2 MetadataService的导出过程</h2> |
| <p>了解元数据的到处过程,这个就要继续前面博客往后的代码了前面博客说了一个服务发布之后的服务信息的双注册数据,这里继续看下导出服务之后的代码: |
| 先来简单回顾下模块发布的启动生命周期方法:</p> |
| <p>DefaultModuleDeployer类型的start方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> Future <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> onModuleStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// initialize |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// prepare application instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// exclude internal module to avoid wait itself |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModel <span style="color:#719e07">!=</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getInternalModule<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer<span style="color:#719e07">.</span>prepareInternalModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// refer services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if no async export/refer services, just set started |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>asyncExportingFutures<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> asyncReferringFutures<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> onModuleStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">....</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面的博客我们已经说了服务提供者导出服务的方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">// export services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportServices<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>在导出服务之后如果代码中配置了引用服务的代码将会执行引用服务的功能,调用代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>referServices<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>不过我们样例代码并没有介绍引用服务的功能,这里先不说,等服务提供者完全启动成功之后我们再来看消费者的逻辑。</p> |
| <p>接下来我们要看的是模块启动成功之后的方法 onModuleStarted();,在这个方法中会去发布服务元数据信息。</p> |
| <h2 id="183-模块启动成功时候的逻辑-onmodulestarted">18.3 模块启动成功时候的逻辑 onModuleStarted();</h2> |
| <p>这里我们直接先看代码再来分析下逻辑:</p> |
| <p>DefaultModuleDeployer类型的onModuleStarted方法如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onModuleStarted</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//状态判断是否为启动中如果是则将状态设置为STARTED |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStarting<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//先修改状态 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has started.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//状态修改成功之后开始通知应用程序发布器模块发布器启动成功了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>notifyModuleChanged<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">,</span> DeployState<span style="color:#719e07">.</span>STARTED<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// complete module start future after application state changed |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> completeStartFuture<span style="color:#719e07">(</span><span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>应用程序发布器处理启动成功的逻辑: |
| DefaultApplicationDeployer类型的notifyModuleChanged方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">notifyModuleChanged</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">,</span> DeployState state<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据所有模块的状态来判断应用发布器的状态 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkState<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">,</span> state<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// notify module state changed or module changed |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//通知所有模块状态更新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>stateLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> stateLock<span style="color:#719e07">.</span>notifyAll<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>应用发布器模型DefaultApplicationDeployer检查状态方法checkState代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">checkState</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">,</span> DeployState moduleState<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在写操作 先加个锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>stateLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//非内部模块,并且模块的状态是发布成功了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>moduleModel<span style="color:#719e07">.</span>isInternal<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> moduleState <span style="color:#719e07">==</span> DeployState<span style="color:#719e07">.</span>STARTED<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> prepareApplicationInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用下所有模块状态进行汇总计算 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DeployState newState <span style="color:#719e07">=</span> calculateState<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">switch</span> <span style="color:#719e07">(</span>newState<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STARTED<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> onStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STARTING<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> onStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STOPPING<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> onStopping<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STOPPED<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> onStopped<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> FAILED<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> Throwable error <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> ModuleModel errorModule <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ModuleModel module <span style="color:#719e07">:</span> applicationModel<span style="color:#719e07">.</span>getModuleModels<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ModuleDeployer deployer <span style="color:#719e07">=</span> module<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>deployer<span style="color:#719e07">.</span>isFailed<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> deployer<span style="color:#719e07">.</span>getError<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> error <span style="color:#719e07">=</span> deployer<span style="color:#719e07">.</span>getError<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> errorModule <span style="color:#719e07">=</span> module<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> onFailed<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; found failed module: &#34;</span> <span style="color:#719e07">+</span> errorModule<span style="color:#719e07">.</span>getDesc<span style="color:#719e07">(),</span> error<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> PENDING<span style="color:#719e07">:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// cannot change to pending from other state |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// setPending(); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="184-准备发布元数据信息和应用实例信息">18.4 准备发布元数据信息和应用实例信息</h2> |
| <p>前面有个代码调用比较重要:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>prepareApplicationInstance<span style="color:#719e07">()</span> |
| </span></span></code></pre></div><p>DefaultApplicationDeployer类型的prepareApplicationInstance方法如下所示</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">prepareApplicationInstance</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//已经注册过应用实例数据了 直接返回 (下面CAS逻辑判断了) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>hasPreparedApplicationInstance<span style="color:#719e07">.</span>get<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册开关控制默认为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//通过将registerConsumer默认设置为“false”来关闭纯使用者进程实例的注册。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isRegisterConsumerInstance<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportMetadataService<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>hasPreparedApplicationInstance<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register the local ServiceInstance if required |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerServiceInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1841-导出元数据服务方法exportmetadataservice">18.4.1 导出元数据服务方法exportMetadataService</h3> |
| <p>这里我们就先直接来贴一下代码:</p> |
| <p>DefaultApplicationDeployer类型的exportMetadataService方法如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportMetadataService</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isStarting<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里监听器我们主要关注的类型是ExporterDeployListener类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>DeployListener<span style="color:#719e07">&lt;</span>ApplicationModel<span style="color:#719e07">&gt;</span> listener <span style="color:#719e07">:</span> listeners<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>listener <span style="color:#719e07">instanceof</span> ApplicationDeployListener<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 回调监听器的模块启动成功方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">((</span>ApplicationDeployListener<span style="color:#719e07">)</span> listener<span style="color:#719e07">).</span>onModuleStarted<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; an exception occurred when handle starting event&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面我们主要关注ExporterDeployListener类型的监听器的回调方法,这里我贴一下代码: |
| ExporterDeployListener类型的onModuleStarted方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onModuleStarted</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// start metadata service exporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//MetadataServiceDelegation类型为实现提供远程RPC服务以方便元数据信息的查询功能的类型。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataServiceDelegation metadataService <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getOrRegisterBean<span style="color:#719e07">(</span>MetadataServiceDelegation<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataServiceExporter <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metadataServiceExporter <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConfigurableMetadataServiceExporter<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> metadataService<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fixme, let&#39;s disable local metadata service export at this moment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//默认我们是没有配置这个元数据类型的这里元数据类型默认为local 条件是不是remote则开始导出,在前面的博客&lt;&lt;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&gt;&gt; 中有提到这个配置下面我再说下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>REMOTE_METADATA_STORAGE_TYPE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>getMetadataType<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metadataServiceExporter<span style="color:#719e07">.</span>export<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在前面的博客<a href="https://blog.elastic.link/2022/07/10/dubbo/9-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-ying-yong-cheng-xu-de-pei-zhi-xin-xi-applicationconfig/">&laquo;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&raquo;</a> 中有提到这个配置下面我再说下</p> |
| <p>metadata-type</p> |
| <p>metadata 传递方式,是以 Provider 视角而言的,Consumer 侧配置无效,可选值有:</p> |
| <ul> |
| <li>remote - Provider 把 metadata 放到远端<strong>注册中心</strong>,Consumer 从<strong>注册中心获取</strong>。</li> |
| <li>local - Provider <strong>把 metadata 放在本地</strong>,<strong>Consumer 从 Provider 处直接获取</strong> 。</li> |
| </ul> |
| <p>可以看到默认的local配置元数据信息的获取是由消费者从提供者拉的,那提供者怎么拉取对应服务的元数据信息那就要要用到这个博客说到的MetadataService服务,传递方式为remote的方式其实就要依赖注册中心了相对来说增加了注册中心的压力。</p> |
| <h3 id="1842-可配置元数据服务的导出configurablemetadataserviceexporter的export">18.4.2 可配置元数据服务的导出ConfigurableMetadataServiceExporter的export</h3> |
| <p>前面了解了导出服务的调用链路,这里详细看下ConfigurableMetadataServiceExporter的export过程源码如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> ConfigurableMetadataServiceExporter <span style="color:#268bd2">export</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据服务配置已经存在或者已经导出或者不可导出情况下是无需导出的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>serviceConfig <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> <span style="color:#719e07">!</span>isExported<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceConfig <span style="color:#719e07">=</span> buildServiceConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//导出服务 ,导出服务的具体过程这里就不再说了可以看上一个博客,这个导出服务的过程会绑定端口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>export<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> metadataService<span style="color:#719e07">.</span>setMetadataURL<span style="color:#719e07">(</span>serviceConfig<span style="color:#719e07">.</span>getExportedUrls<span style="color:#719e07">().</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The MetadataService exports urls : &#34;</span> <span style="color:#719e07">+</span> serviceConfig<span style="color:#719e07">.</span>getExportedUrls<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isWarnEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The MetadataService has been exported : &#34;</span> <span style="color:#719e07">+</span> serviceConfig<span style="color:#719e07">.</span>getExportedUrls<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1843-元数据服务配置对象的创建">18.4.3 元数据服务配置对象的创建</h3> |
| <p>前面我们看到了构建元数据服务对象的代码调用ServiceConfig<MetadataService>,接下来我们详细看下构建源码如下所示: |
| ConfigurableMetadataServiceExporter类型的buildServiceConfig构建元数据服务配置对象方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> ServiceConfig<span style="color:#719e07">&lt;</span>MetadataService<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">buildServiceConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//1 获取当前的应用配置 然后初始化应用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> getApplicationConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceConfig<span style="color:#719e07">&lt;</span>MetadataService<span style="color:#719e07">&gt;</span> serviceConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置域模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getInternalModule<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setApplication<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//2 创建注册中心配置对象 然后并初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> RegistryConfig registryConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;N/A&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setId<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;internal-metadata-registry&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//3 创建服务配置对象,并初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setRegistry<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setRegister<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//4 生成协议配置 ,这里会配置一下元数据使用的服务端口号默认使用其他服务的端口20880 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>generateMetadataProtocol<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>MetadataService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setDelay<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里也是需要注意的地方服务引用的类型为MetadataServiceDelegation |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span>metadataService<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setGroup<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">.</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setVersion<span style="color:#719e07">(</span>MetadataService<span style="color:#719e07">.</span>VERSION<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//5 生成方法配置 这里目前提供的服务方法为getAndListenInstanceMetadata方法 后续可以看下这个方法的视线 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setMethods<span style="color:#719e07">(</span>generateMethodConfig<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>setConnections<span style="color:#719e07">(</span>1<span style="color:#719e07">);</span> <span style="color:#586e75">// separate connection |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceConfig<span style="color:#719e07">.</span>setExecutes<span style="color:#719e07">(</span>100<span style="color:#719e07">);</span> <span style="color:#586e75">// max tasks running at the same time |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> serviceConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个服务配置对象的创建非常像我们第一个博客提到的服务配置过程,不过这个元数据服务对象有几个比较特殊的配置</p> |
| <ul> |
| <li>注册中心的配置register设置为了false 则为不向注册中心注册具体的服务配置信息</li> |
| <li>对每个提供者的最大连接数connections为1</li> |
| <li>服务提供者每服务每方法最大可并行执行请求数executes为100</li> |
| </ul> |
| <p>在使用过程中可以知道上面这几个配置值</p> |
| <h2 id="185-应用级数据注册---registerserviceinstance">18.5 应用级数据注册 registerServiceInstance()</h2> |
| <p>在前面导出元数据服务之后也会调用一行代码来注册应用级数据来保证应用上线</p> |
| <p>主要涉及到的代码为DefaultApplicationDeployer类型中的registerServiceInstance方法如下所示</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerServiceInstance</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//标记变量设置为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registered <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> ServiceInstanceMetadataUtils<span style="color:#719e07">.</span>registerMetadataAndInstance<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Register instance error&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registered<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// scheduled task for updating Metadata and ServiceInstance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> asyncMetadataFuture <span style="color:#719e07">=</span> frameworkExecutorRepository<span style="color:#719e07">.</span>getSharedScheduledExecutor<span style="color:#719e07">().</span>scheduleWithFixedDelay<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore refresh metadata on stopping |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>applicationModel<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> registered<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ServiceInstanceMetadataUtils<span style="color:#719e07">.</span>refreshMetadataAndInstance<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>applicationModel<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Refresh instance and metadata error&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> 0<span style="color:#719e07">,</span> ConfigurationUtils<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> METADATA_PUBLISH_DELAY_KEY<span style="color:#719e07">,</span> DEFAULT_METADATA_PUBLISH_DELAY<span style="color:#719e07">),</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法先将应用元数据注册到注册中心,然后开始开启定时器每隔30秒同步一次元数据向注册中心。</p> |
| <h3 id="1851-服务实例元数据工具类注册服务发现的元数据信息">18.5.1 服务实例元数据工具类注册服务发现的元数据信息</h3> |
| <p>前面通过调用类型ServiceInstanceMetadataUtils工具类的registerMetadataAndInstance方法来进行服务实例数据和元数据的注册这里我们详细看下代码如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerMetadataAndInstance</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Start registering instance address to registry.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> RegistryManager registryManager <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>RegistryManager<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register service instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">//注意这里服务发现的类型只有ServiceDiscoveryRegistry类型的注册协议才满足 registryManager.getServiceDiscoveries().forEach(ServiceDiscovery::register); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1852-abstractservicediscovery中的服务发现数据注册的模版方法">18.5.2 AbstractServiceDiscovery中的服务发现数据注册的模版方法</h3> |
| <p>AbstractServiceDiscovery类型的注册方法register()方法这个是一个模版方法,真正执行的注册逻辑封装在了doRegister方法中由扩展的服务发现子类来完成</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">register</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> RuntimeException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一步创建应用的实例信息等待下面注册到注册中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance <span style="color:#719e07">=</span> createServiceInstance<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isValidInstance<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No valid instance found, stop registering instance address to registry.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否需要更新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> revisionUpdated <span style="color:#719e07">=</span> calOrUpdateInstanceRevision<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>revisionUpdated<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportMetadata<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用的实例信息注册到注册中心之上 ,这个 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doRegister<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1853-应用级实例对象创建">18.5.3 应用级实例对象创建</h3> |
| <p>可以看到在AbstractServiceDiscovery服务发现的第一步创建应用的实例信息等待下面注册到注册中心</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceInstance <span style="color:#719e07">=</span> createServiceInstance<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataInfo<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>最终创建的serviceInstance类型为ServiceInstance 这个是Dubbo封装的一个接口,具体实现类型为DefaultServiceInstance,我们可以看下应用级的元数据有哪些</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> ServiceInstance <span style="color:#268bd2">createServiceInstance</span><span style="color:#719e07">(</span>MetadataInfo metadataInfo<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里的服务名字为:dubbo-demo-api-provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DefaultServiceInstance instance <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DefaultServiceInstance<span style="color:#719e07">(</span>serviceName<span style="color:#719e07">,</span> applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用服务的元数据 ,可以看下面debug的数据信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance<span style="color:#719e07">.</span>setServiceMetadata<span style="color:#719e07">(</span>metadataInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//metadataType的值为local 这个方法是将元数据类型存储到英勇的元数据对象中 对应内容为dubbo.metadata.storage-type:local |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setMetadataStorageType<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> metadataType<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 这个是自定义元数据数据 我们也可以通过实现扩展ServiceInstanceCustomizer来自定义一些元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceInstanceMetadataUtils<span style="color:#719e07">.</span>customizeInstance<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法的主要目的就是将应用的元数据信息都封装到ServiceInstance类型中,不过额外提供了一个扩展性比较好的方法可以自定义元数据信息</p> |
| <p>前面的metadataInfo对象的信息如下图所示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/18-metadata2.png" alt="在这里插入图片描述"></p> |
| <p>自定义元数据类型Dubbo官方提供了一个默认的实现类型为:ServiceInstanceMetadataCustomizer</p> |
| <p>最终封装好的元数据信息如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>DefaultServiceInstance<span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span>serviceName<span style="color:#719e07">=</span>&#39;dubbo<span style="color:#719e07">-</span>demo<span style="color:#719e07">-</span>api<span style="color:#719e07">-</span>provider&#39;<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span>host<span style="color:#719e07">=</span>&#39;192<span style="color:#719e07">.</span>168<span style="color:#719e07">.</span>1<span style="color:#719e07">.</span>169&#39;<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span>port<span style="color:#719e07">=</span>20880<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span>enabled<span style="color:#719e07">=</span><span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span>healthy<span style="color:#719e07">=</span><span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> metadata<span style="color:#719e07">={</span> |
| </span></span><span style="display:flex;"><span> dubbo<span style="color:#719e07">.</span>metadata<span style="color:#719e07">-</span>service<span style="color:#719e07">.</span>url<span style="color:#719e07">-</span>params<span style="color:#719e07">={</span><span style="color:#2aa198">&#34;connections&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;1&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;version&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;1.0.0&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;2.0.2&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;release&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;3.0.9&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;side&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;provider&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;port&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;20880&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;protocol&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;dubbo&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> |
| </span></span><span style="display:flex;"><span> dubbo<span style="color:#719e07">.</span>endpoints<span style="color:#719e07">=[</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">{</span><span style="color:#2aa198">&#34;port&#34;</span><span style="color:#719e07">:</span>20880<span style="color:#719e07">,</span><span style="color:#2aa198">&#34;protocol&#34;</span><span style="color:#719e07">:</span><span style="color:#2aa198">&#34;dubbo&#34;</span><span style="color:#719e07">}],</span> |
| </span></span><span style="display:flex;"><span> dubbo<span style="color:#719e07">.</span>metadata<span style="color:#719e07">.</span>storage<span style="color:#719e07">-</span>type<span style="color:#719e07">=</span>local<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> timestamp<span style="color:#719e07">=</span>1656227493387<span style="color:#719e07">}}</span> |
| </span></span></code></pre></div><h3 id="1854-应用级实例数据配置变更的的版本号获取">18.5.4 应用级实例数据配置变更的的版本号获取</h3> |
| <p>前面创建元应用的实例信息后开始创建版本号来判断是否需要更新,对应AbstractServiceDiscovery类型的calOrUpdateInstanceRevision</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">calOrUpdateInstanceRevision</span><span style="color:#719e07">(</span>ServiceInstance instance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取元数据版本号对应字段dubbo.metadata.revision |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String existingInstanceRevision <span style="color:#719e07">=</span> getExportedServicesRevision<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取实例的服务元数据信息:metadata{app=&#39;dubbo-demo-api-provider&#39;,revision=&#39;null&#39;,size=1,services=[link.elastic.dubbo.entity.DemoService:dubbo]} |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataInfo metadataInfo <span style="color:#719e07">=</span> instance<span style="color:#719e07">.</span>getServiceMetadata<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//必须在不同线程之间同步计算此实例的状态,如同一实例的修订和修改。此方法的使用仅限于某些点,例如在注册期间。始终尝试使用此选项。改为getRevision()。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String newRevision <span style="color:#719e07">=</span> metadataInfo<span style="color:#719e07">.</span>calAndGetRevision<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//版本号发生了变更(元数据发生了变更)版本号是md5元数据信息计算出来HASH验证 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>newRevision<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>existingInstanceRevision<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//版本号添加到dubbo.metadata.revision字段中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance<span style="color:#719e07">.</span>getMetadata<span style="color:#719e07">().</span>put<span style="color:#719e07">(</span>EXPORTED_SERVICES_REVISION_PROPERTY_NAME<span style="color:#719e07">,</span> metadataInfo<span style="color:#719e07">.</span>getRevision<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="18541-元数据版本号的计算与hash校验-calandgetrevision">18.5.4.1 元数据版本号的计算与HASH校验 calAndGetRevision</h4> |
| <p>这个方法其实比较重要,决定了什么时候会更新元数据,Dubbo使用了一种Hash验证的方式将元数据转MD5值与之前的存在的版本号(也是元数据转MD5得到的) 如果数据发生了变更则MD5值会发生变化 以此来更新元数据,不过发生了MD5冲突的话就会导致配置不更新这个冲突的概率非常小。 |
| 好了直接来看代码吧: |
| MetadataInfo类型的calAndGetRevision方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> String <span style="color:#268bd2">calAndGetRevision</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>revision <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>updated<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> revision<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> updated <span style="color:#719e07">=</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用下没有服务则使用一个空的版本号 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmptyMap<span style="color:#719e07">(</span>services<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>revision <span style="color:#719e07">=</span> EMPTY_REVISION<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> StringBuilder sb <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> StringBuilder<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//app是应用名 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> sb<span style="color:#719e07">.</span>append<span style="color:#719e07">(</span>app<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> ServiceInfo<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> <span style="color:#719e07">new</span> TreeMap<span style="color:#719e07">&lt;&gt;(</span>services<span style="color:#719e07">).</span>entrySet<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> sb<span style="color:#719e07">.</span>append<span style="color:#719e07">(</span>entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">().</span>toDescString<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> String tempRevision <span style="color:#719e07">=</span> RevisionResolver<span style="color:#719e07">.</span>calRevision<span style="color:#719e07">(</span>sb<span style="color:#719e07">.</span>toString<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>StringUtils<span style="color:#719e07">.</span>isEquals<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>revision<span style="color:#719e07">,</span> tempRevision<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据重新注册的话我们可以看看这个日志metadata revision change |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;metadata revision changed: %s -&gt; %s, app: %s, services: %d&#34;</span><span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>revision<span style="color:#719e07">,</span> tempRevision<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>app<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>services<span style="color:#719e07">.</span>size<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>revision <span style="color:#719e07">=</span> tempRevision<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>rawMetadataInfo <span style="color:#719e07">=</span> JsonUtils<span style="color:#719e07">.</span>getJson<span style="color:#719e07">().</span>toJson<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> revision<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>RevisionResolver类型的Md5运算计算版本号</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>md5Utils<span style="color:#719e07">.</span>getMd5<span style="color:#719e07">(</span>metadata<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><h3 id="1855-reportmetadata">18.5.5 reportMetadata</h3> |
| <p>回到18.5.2 AbstractServiceDiscovery中的模版方法register,这里我们来看下reportMetadata方法,不过这个方法目前并不会走到,因为我们默认的配置元数据是local不会直接把应用的元数据注册在元数据中心</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">reportMetadata</span><span style="color:#719e07">(</span>MetadataInfo metadataInfo<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//订阅元数据的标识符 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> SubscriberMetadataIdentifier identifier <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SubscriberMetadataIdentifier<span style="color:#719e07">(</span>serviceName<span style="color:#719e07">,</span> metadataInfo<span style="color:#719e07">.</span>getRevision<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否远程发布元数据,这里我们是本地注册这个就不会在元数据中心发布这个元数据信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>DEFAULT_METADATA_STORAGE_TYPE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>metadataType<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> metadataReport<span style="color:#719e07">.</span>shouldReportMetadata<span style="color:#719e07">())</span> <span style="color:#719e07">||</span> REMOTE_METADATA_STORAGE_TYPE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>metadataType<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metadataReport<span style="color:#719e07">.</span>publishAppMetadata<span style="color:#719e07">(</span>identifier<span style="color:#719e07">,</span> metadataInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1856-扩展的注册中心来注册应用级服务发现数据doregister方法">18.5.6 扩展的注册中心来注册应用级服务发现数据doRegister方法</h3> |
| <p>前面我们说了AbstractServiceDiscovery中的模版方法register,在register会调用一个doRegister方法来注册应用级数据,这个方法是需要扩展注册中心的服务发现来自行实现的,我们这里以官方实现的Zookeeper服务发现模型为例:</p> |
| <p>ZookeeperServiceDiscovery中的doRegister方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doRegister</span><span style="color:#719e07">(</span>ServiceInstance serviceInstance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceDiscovery<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>build<span style="color:#719e07">(</span>serviceInstance<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException<span style="color:#719e07">(</span>REGISTRY_EXCEPTION<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;Failed register instance &#34;</span> <span style="color:#719e07">+</span> serviceInstance<span style="color:#719e07">.</span>toString<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面我们介绍了ZookeeperServiceDiscovery发现的构造器连接注册中心,这里来看下服务注册, |
| 应用级实例数据注册一共分为两步 |
| 第一步是:Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance |
| 第二步是:执行registerService方法将数据注册到注册中心</p> |
| <p>先来看第一步:Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance |
| 关于Curator的服务发现原理可以参考官网的文章博客<a href="https://curator.apache.org/docs/service-discovery/index.html">curator-x-discovery</a></p> |
| <p><strong>什么是发现服务?</strong> |
| 在 SOA/分布式系统中,服务需要找到彼此。即,Web 服务可能需要找到缓存服务等。DNS 可以用于此,但对于不断变化的服务来说,它远不够灵活。服务发现系统提供了一种机制:</p> |
| <ul> |
| <li>注册其可用性的服务</li> |
| <li>定位特定服务的单个实例</li> |
| <li>在服务实例更改时通知</li> |
| </ul> |
| <p>服务实例由类表示:ServiceInstance。ServiceInstances 具有名称、id、地址、端口和/或 ssl 端口,以及可选的有效负载(用户定义)。ServiceInstances 通过以下方式序列化并存储在 ZooKeeper 中:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>base path |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>_______ service A name |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 1 id <span style="color:#719e07">--&gt;</span> <span style="color:#719e07">(</span>serialized ServiceInstance<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 2 id <span style="color:#719e07">--&gt;</span> <span style="color:#719e07">(</span>serialized ServiceInstance<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>_______ service B name |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 1 id <span style="color:#719e07">--&gt;</span> <span style="color:#719e07">(</span>serialized ServiceInstance<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 2 id <span style="color:#719e07">--&gt;</span> <span style="color:#719e07">(</span>serialized ServiceInstance<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>_______ <span style="color:#719e07">...</span> |
| </span></span></code></pre></div><p>这个应用最终注册应用级服务数据如下: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/18-metadata4.png" alt="在这里插入图片描述"> |
| 这里需要注意的是这个 应用的IP+端口的服务元数据信息是临时节点 |
| build方法内容对应着上图的JSON数据 可以看菜build方法封装的过程:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>curator<span style="color:#719e07">.</span>x<span style="color:#719e07">.</span>discovery<span style="color:#719e07">.</span>ServiceInstance<span style="color:#719e07">&lt;</span>ZookeeperInstance<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">build</span><span style="color:#719e07">(</span>ServiceInstance serviceInstance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ServiceInstanceBuilder builder<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String serviceName <span style="color:#719e07">=</span> serviceInstance<span style="color:#719e07">.</span>getServiceName<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String host <span style="color:#719e07">=</span> serviceInstance<span style="color:#719e07">.</span>getHost<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> port <span style="color:#719e07">=</span> serviceInstance<span style="color:#719e07">.</span>getPort<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> metadata <span style="color:#719e07">=</span> serviceInstance<span style="color:#719e07">.</span>getSortedMetadata<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String id <span style="color:#719e07">=</span> generateId<span style="color:#719e07">(</span>host<span style="color:#719e07">,</span> port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ZookeeperInstance是Dubbo封装的用于存放payload数据 包含服务id,服务名字和元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ZookeeperInstance zookeeperInstance <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ZookeeperInstance<span style="color:#719e07">(</span>id<span style="color:#719e07">,</span> serviceName<span style="color:#719e07">,</span> metadata<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> builder <span style="color:#719e07">=</span> builder<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>id<span style="color:#719e07">(</span>id<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>name<span style="color:#719e07">(</span>serviceName<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>address<span style="color:#719e07">(</span>host<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>port<span style="color:#719e07">(</span>port<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>payload<span style="color:#719e07">(</span>zookeeperInstance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RuntimeException<span style="color:#719e07">(</span>e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> builder<span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在《18.5 应用级数据注册 registerServiceInstance() 》 小节中介绍了应用元数据信息的注册调用代码,其实后面还有个update的逻辑定期30秒同步元数据到元数据中心,这里就不详细介绍了。</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/18-dubbo3-yuan-shu-ju-fu-wu-metadataservice-de-dao-chu/">18-Dubbo3元数据服务MetadataService的导出</a></p></description></item><item><title>Blog: 17-Dubbo服务提供者的双注册原理</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/17/17-dubbo%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E8%80%85%E7%9A%84%E5%8F%8C%E6%B3%A8%E5%86%8C%E5%8E%9F%E7%90%86/</link><pubDate>Wed, 17 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/17/17-dubbo%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E8%80%85%E7%9A%84%E5%8F%8C%E6%B3%A8%E5%86%8C%E5%8E%9F%E7%90%86/</guid><description> |
| <h1 id="17-dubbo服务提供者的双注册原理">17-Dubbo服务提供者的双注册原理</h1> |
| <h2 id="171-简介">17.1 简介</h2> |
| <p>上个博客<a href="https://blog.elastic.link/2022/07/10/dubbo/15-dubbo-de-san-da-zhong-xin-zhi-yuan-shu-ju-zhong-xin-yuan-ma-jie-xi/">《15-Dubbo的三大中心之元数据中心源码解析》</a>导出服务端的时候多次提到了元数据中心,注册信息的注册。 |
| Dubbo3出来时间不太长,对于现在的用户来说大部分使用的仍旧是Dubbo2.x, |
| Dubbo3 比较有特色也是会直接使用到的功能就是<strong>应用级服务发现</strong>:</p> |
| <ul> |
| <li>应用级服务发现 |
| <em>从服务/接口粒度到应用粒度的升级,使得 Dubbo 在集群可伸缩性、连接异构微服务体系上更具优势。应用粒度能以更低的资源消耗支持超百万实例规模集群程; 实现与 Spring Cloud、Kubernetes Service 等异构微服务体系的互联互通。</em></li> |
| </ul> |
| <p>对于直接使用Dubbo3的用户还好,可以仅仅开启应用级注册,但是对于Dubbo2.x的用户升级到Dubbo3的用户来说前期都是要开启双注册来慢慢迁移的,既注册传统的接口信息到注册中心,又注册应用信息到注册中心,同时注册应用与接口关系的元数据信息。 |
| 关于双注册与服务迁移的过程的使用可以参考官网: |
| <a href="https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/">应用级地址发现迁移指南</a></p> |
| <p>关于官网提供者双注册的图我这里贴一下,方便了解: |
| <img src="https://dubbo.apache.org/imgs/v3/migration/provider-registration.png" alt="在这里插入图片描述"></p> |
| <h2 id="172-双注册配置的读取">17.2 双注册配置的读取</h2> |
| <h3 id="1721-注册中心地址作为元数据中心">17.2.1 注册中心地址作为元数据中心</h3> |
| <p>这个配置的解析过程在前面的博客介绍元数据中心的时候很详细的说了相关链接:<a href="https://blog.elastic.link/2022/07/10/dubbo/15-dubbo-de-san-da-zhong-xin-zhi-yuan-shu-ju-zhong-xin-yuan-ma-jie-xi/">15-Dubbo的三大中心之元数据中心源码解析</a></p> |
| <p>对应代码位于:DefaultApplicationDeployer类型的startMetadataCenter()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startMetadataCenter</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> useRegistryAsMetadataCenterIfNecessary<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...省略掉其他代码防止受到干扰 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>具体逻辑是这个方法: |
| useRegistryAsMetadataCenterIfNecessary</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">useRegistryAsMetadataCenterIfNecessary</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置缓存中查询元数据配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataConfigs <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getMetadataConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...省略掉空判断 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> defaultRegistries <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getDefaultRegistries<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultRegistries<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多注册中心遍历 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultRegistries |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>stream<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>filter<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>isUsedRegistryAsMetadataCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心配置映射为元数据中心 映射就是获取需要的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>registryAsMetadataCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将元数据中心配置存储在配置缓存中方便后续使用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>metadataReportConfig <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...省略掉具体的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>关于元数据中心地址的获取,主要经过如下逻辑:</p> |
| <ul> |
| <li><strong>查询:</strong> 所有可用的默认注册中心列表</li> |
| <li><strong>遍历:</strong> 多注册中心遍历</li> |
| <li><strong>筛选:</strong> 选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)</li> |
| <li><strong>转化:</strong> 注册中心配置RegistryConfig映射转换为元数据中心配置类型MetadataReportConfig</li> |
| </ul> |
| <p>MetadataReportConfig 映射就是获取需要的配置。</p> |
| <p>最后会把查询到的元数据中心配置存储在配置缓存中方便后续使用。</p> |
| <h3 id="1722-双注册模式配置">17.2.2 双注册模式配置</h3> |
| <p>双注册配置类型是这个</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>dubbo<span style="color:#719e07">.</span>application<span style="color:#719e07">.</span>register<span style="color:#719e07">-</span>mode<span style="color:#719e07">=</span>all |
| </span></span></code></pre></div><p>默认值为all代表应用级注册和接口级注册,当前在完全迁移到应用级注册之后可以将服务直接迁移到应用级配置上去。 |
| 配置值解释:</p> |
| <ul> |
| <li>all 双注册</li> |
| <li>instance 应用级注册</li> |
| <li>interface 接口级注册</li> |
| </ul> |
| <p>后面的代码如果想要看更详细的代码可以看博客<a href="https://blog.elastic.link/2022/07/10/dubbo/16-mo-kuai-fa-bu-qi-fa-bu-fu-wu-quan-guo-cheng/">《16-模块发布器发布服务全过程》</a> |
| 关于这个配置的使用我们详细来看下,在Dubbo服务注册时候会先通过此配置查询需要注册服务地址,具体代码位于ServiceConfig的doExportUrls()方法中:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrls</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//省略掉前面的代码... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs <span style="color:#719e07">=</span> ConfigValidationUtils<span style="color:#719e07">.</span>loadRegistries<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//省略掉后面的代码... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>然后就是具体注册中心地址的获取过程我们看下: |
| ConfigValidationUtils的加载注册中心地址方法loadRegistries</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">loadRegistries</span><span style="color:#719e07">(</span>AbstractInterfaceConfig interfaceConfig<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// check &amp;&amp; override if necessary |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//省略掉前面的代码... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里会获取到一个接口配置注册地址例如:registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> registries <span style="color:#719e07">=</span> interfaceConfig<span style="color:#719e07">.</span>getRegistries<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//省略掉中间的的代码... |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> genCompatibleRegistries<span style="color:#719e07">(</span>interfaceConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">(),</span> registryList<span style="color:#719e07">,</span> provider<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigValidationUtils的双注册地址的获取genCompatibleRegistries方法. |
| 前面代码获取到了一个注册中心地址列表例如:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><p>下面可以看下如果根据配置来转换为应用级注册地址+接口级注册地址</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">genCompatibleRegistries</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryList<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> result <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>registryList<span style="color:#719e07">.</span>size<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> registryList<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>registryURL <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// for registries enabled service discovery, automatically register interface compatible addresses. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String registerMode<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为了更好理解这里简化掉服务发现注册地址配置的逻辑判断过程仅仅看当前例子提供的值走的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双注册模式配置查询 对应参数为dubbo.application.register-mode 默认值为all |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerMode <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_MODE_KEY<span style="color:#719e07">,</span> ConfigurationUtils<span style="color:#719e07">.</span>getCachedDynamicProperty<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> DUBBO_REGISTER_MODE_DEFAULT_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果用户配置了一个错误的注册模式配置则只走接口级配置 这里默认值为interface |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isValidRegisterMode<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> DEFAULT_REGISTER_MODE_INTERFACE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个逻辑是满足应用级注册就添加一个应用级注册的地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>DEFAULT_REGISTER_MODE_INSTANCE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> registryNotExists<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">,</span> registryList<span style="color:#719e07">,</span> SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> URL serviceDiscoveryRegistryURL <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_TYPE_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>serviceDiscoveryRegistryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个逻辑是满足接口级注册配置就添加一个接口级注册地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>DEFAULT_REGISTER_MODE_INTERFACE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//省略掉若干代码和括号 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看到这里简化的配置比较容易理解了</p> |
| <ul> |
| <li>双注册模式配置查询 对应参数为dubbo.application.register-mode ,默认值为all</li> |
| <li>如果用户配置了一个错误的注册模式配置则只走接口级配置 这里默认值为interface</li> |
| <li>满足应用级注册就添加一个应用级注册的地址</li> |
| <li>满足接口级注册配置就添加一个接口级注册地址</li> |
| </ul> |
| <p>这个方法是根据服务注册模式来判断使用接口级注册地址还是应用级注册地址分别如下所示: |
| 配置信息: |
| dubbo.application.register-mode |
| 配置值:</p> |
| <ul> |
| <li>interface |
| <ul> |
| <li>接口级注册</li> |
| </ul> |
| </li> |
| <li>instance |
| <ul> |
| <li>应用级注册</li> |
| </ul> |
| </li> |
| <li>all |
| <ul> |
| <li>接口级别和应用级都注册</li> |
| </ul> |
| </li> |
| </ul> |
| <p>最终的注册地址配置如下: |
| 接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><p>应用级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><h2 id="173-双注册服务数据的注册">17.3 双注册服务数据的注册</h2> |
| <h3 id="1731-双注册代码逻辑调用简介">17.3.1 双注册代码逻辑调用简介</h3> |
| <p>前面说了这个注册服务的配置地址会由Dubbo内部进行判断如果判断是all的话会自动将一个配置的注册地址转变为两个一个是传统的接口级注册,一个是应用级注册使用的配置地址</p> |
| <p>然后我们先看注册中心,注册服务数据的源码 |
| 如果想要查看源码细节可以在RegistryProtocol类型的export(final Invoker<T> originInvoker) 方法的如下代码位置打断点:</p> |
| <p>RegistryProtocol的export方法的注册中心注册数据代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">// url to registry 注册服务对外的接口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果url为service-discovery-registry发现则这个实现类型为ServiceDiscoveryRegistry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务发现的提供者url: dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=19559&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1654938441023 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> URL registeredProviderUrl <span style="color:#719e07">=</span> getUrlToRegistry<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// decide if we need to delay publish (provider itself and registry should both need to register) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//register参数是否 注册数据到注册中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> register <span style="color:#719e07">=</span> providerUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> registryUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>register<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里有两种情况 接口级注册会将接口级服务提供者数据直接注册到Zookeper上面,服务发现(应用级注册)这里仅仅会将注册数据转换为服务元数据等后面来发布元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> register<span style="color:#719e07">(</span>registry<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register stated url on provider model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//向提供者模型注册提供者配置ProviderModel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerStatedUrl<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">,</span> register<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> exporter<span style="color:#719e07">.</span>setRegisterUrl<span style="color:#719e07">(</span>registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exporter<span style="color:#719e07">.</span>setSubscribeUrl<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>registry<span style="color:#719e07">.</span>isServiceDiscovery<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Deprecated! Subscribe to override rules in 2.6.x or before. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registry<span style="color:#719e07">.</span>subscribe<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">,</span> overrideSubscribeListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在上个博客中我们整体说了下服务注册时候的一个流程,关于数据向注册中心的注册细节这里可以详细看下</p> |
| <h3 id="1732--注册中心领域对象的初始化">17.3.2 注册中心领域对象的初始化</h3> |
| <p>前面的代码使用url来获取注册中心操作对象如下调用代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// url to registry 注册服务对外的接口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>对应代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> Registry <span style="color:#268bd2">getRegistry</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> URL registryUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为两步先获取注册中心工厂对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> RegistryFactory registryFactory <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>RegistryFactory<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用注册中心工厂对象获取注册中心操作对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> registryFactory<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>关于参数URL有两个在前面已经说过,url信息如下:</p> |
| <p>接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><p>应用级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><p>注册中心工厂对象与注册中心操作对象的获取与执行我们通过Debug来看比较麻烦,这里涉及到很多扩展机制动态生成的代码我们无法看到,这里我直接来贴一下比较关键的一些类型,以Zookeeper注册中心来举例子:</p> |
| <p>先来看下注册工厂相关的类型: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register.png" alt="在这里插入图片描述"></p> |
| <ul> |
| <li>RegistryFactory 注册中心对象获取</li> |
| <li>AbstractRegistryFactory 模板类型封装注册中心对象获取的基本逻辑,比如缓存和基本的逻辑判断</li> |
| <li>ServiceDiscoveryRegistryFactory 用于创建服务发现注册中心工厂对象 用于创建ServiceDiscoveryRegistry对象</li> |
| <li>ZookeeperRegistryFactory 用于创建ZookeeperRegistry类型对象</li> |
| <li>NacosRegistryFactory Nacos注册中心工厂对象 用于创建NacosRegistry</li> |
| </ul> |
| <p>接下来看封装了注册中心操作逻辑的注册中心领域对象:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register2.png" alt="在这里插入图片描述"></p> |
| <ul> |
| <li>Node 节点信息开放接口 比如节点 url的获取 ,销毁</li> |
| <li>RegistryService 注册服务接口,比如注册,订阅,查询等操作</li> |
| <li>Registry 注册中心接口,是否服务发现查询,注册,取消注册方法</li> |
| <li>AbstractRegistry 注册中心逻辑抽象模板类型,封装了注册,订阅,通知的基本逻辑,和本地缓存注册中心信息的基本逻辑</li> |
| <li>FailbackRegistry 封装了失败重试的逻辑</li> |
| <li>NacosRegistry 封装了以nacos作为注册中心的基本逻辑</li> |
| <li>ServiceDiscoveryRegistry 应用级服务发现注册中心逻辑,现在不需要这种网桥实现,协议可以直接与服务发现交互。ServiceDiscoveryRegistry是一种非常特殊的注册表实现,用于以兼容的方式将旧的接口级服务发现模型与3.0中引入的新服务发现模型连接起来。 |
| 它完全符合注册表SPI的扩展规范,但与zookeeper和Nacos的具体实现不同,因为它不与任何真正的第三方注册表交互,而只与过程中ServiceDiscovery的相关组件交互。简而言之,它架起了旧接口模型和新服务发现模型之间的桥梁:register()方法主要通过与MetadataService交互,将接口级数据聚合到MetadataInfo中subscribe() 触发应用程序级服务发现模型的整个订阅过程。-根据ServiceNameMapping将接口映射到应用程序。-启动新的服务发现侦听器(InstanceListener),并使NotifierListener成为InstanceListener的一部分。</li> |
| <li>CacheableFailbackRegistry 提供了一些本地内存缓存的逻辑 对注册中心有用,注册中心的sdk将原始字符串作为提供程序实例返回,例如zookeeper和etcd</li> |
| <li>ZookeeperRegistry Zookeeper作为注册中心的基本操作逻辑封装</li> |
| </ul> |
| <p>了解了这几个领域对象这里我们回到代码逻辑,这里直接看将会执行的一些核心逻辑:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> Registry <span style="color:#268bd2">getRegistry</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> URL registryUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为两步先获取注册中心工厂对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> RegistryFactory registryFactory <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>RegistryFactory<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用注册中心工厂对象获取注册中心操作对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> registryFactory<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面注册中心工厂不论那种协议的地址信息获取到的都是一个RegistryFactory$Adaptive类型(由扩展机制的字节码工具自动生成的代码)</p> |
| <p>如果getRegistry参数为应用级注册地址。如下所示将获取到的类型为ServiceDiscoveryRegistryFactory逻辑来获取注册中心: |
| (这个逻辑是@Adaptive注解产生的了逻辑具体原理可以看扩展机制中@Adaptive的实现)</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><p>getRegistry方法优先走的逻辑是这里:AbstractRegistryFactory模板类型中的getRegistry方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Registry <span style="color:#268bd2">getRegistry</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registryManager <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unable to fetch RegistryManager from ApplicationModel BeanFactory. &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;Please check if `setApplicationModel` has been override.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//销毁状态直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Registry defaultNopRegistry <span style="color:#719e07">=</span> registryManager<span style="color:#719e07">.</span>getDefaultNopRegistryIfDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#cb4b16">null</span> <span style="color:#719e07">!=</span> defaultNopRegistry<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> defaultNopRegistry<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setPath<span style="color:#719e07">(</span>RegistryService<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>addParameter<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">,</span> RegistryService<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>TIMESTAMP_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeAttribute<span style="color:#719e07">(</span>EXPORT_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeAttribute<span style="color:#719e07">(</span>REFER_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个key为 service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> createRegistryCacheKey<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Registry registry <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check配置 是否检查注册中心连通 默认为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> check <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CHECK_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> url<span style="color:#719e07">.</span>getPort<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> 0<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Lock the registry access process to ensure a single instance of the registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//给写操作加锁方式并发写问题 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryManager<span style="color:#719e07">.</span>getRegistryLock<span style="color:#719e07">().</span>lock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//锁内检查是否销毁的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// double check |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// fix https://github.com/apache/dubbo/issues/7265. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultNopRegistry <span style="color:#719e07">=</span> registryManager<span style="color:#719e07">.</span>getDefaultNopRegistryIfDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#cb4b16">null</span> <span style="color:#719e07">!=</span> defaultNopRegistry<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> defaultNopRegistry<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//锁内检查是否缓存中存在存在则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registry <span style="color:#719e07">=</span> registryManager<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registry <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> registry<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//create registry by spi/ioc |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//使用url创建注册中心操作的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registry <span style="color:#719e07">=</span> createRegistry<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check配置检查 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>check<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RuntimeException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can not create registry &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to obtain or create registry &#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Release the lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryManager<span style="color:#719e07">.</span>getRegistryLock<span style="color:#719e07">().</span>unlock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>check <span style="color:#719e07">&amp;&amp;</span> registry <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can not create registry &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registry <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registryManager<span style="color:#719e07">.</span>putRegistry<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> registry<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> registry<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>逻辑其实吧比较简单,概括下上面的逻辑:</p> |
| <ul> |
| <li>销毁逻辑判断</li> |
| <li>缓存获取,存在则直接返回</li> |
| <li>根据注册中心url配置,创建注册中心操作对象</li> |
| <li>注册中心连接失败的check配置逻辑处理</li> |
| <li>将注册中心操作对象存入缓存</li> |
| </ul> |
| <p>上面比较重要的逻辑是createRegistry这个 |
| 整个调用过程我给大家看下Debug的详情,这里很多逻辑由扩展机制产生的这里直接看下逻辑调用栈,有几个需要关注的地方我圈了起来: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register3.png" alt="在这里插入图片描述"> |
| 我们继续看服务发现的注册中心工厂对象的获取,代码如下: |
| ServiceDiscoveryRegistryFactory类型的createRegistry方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> Registry <span style="color:#268bd2">createRegistry</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//判断url是否是这个前缀:service-discovery-registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>hasServiceDiscoveryRegistryProtocol<span style="color:#719e07">(</span>url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//切换下协议:将服务发现协议切换为配置的注册中心协议这里是Zookeeper如下: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.RegistryService&amp;pid=39884&amp;release=3.0.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String protocol <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTRY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>protocol<span style="color:#719e07">).</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务发现注册中心对象对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ServiceDiscoveryRegistry<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>通过以上代码可以看到其实最终创建的是一个ServiceDiscoveryRegistry注册中心对象,这个url协议被转换为了对应注册中心的协议,也就是说双注册会有两个协议一个是原先的接口级注册注册中心对象(这个还未说到)和这里对应注册中心协议的服务发现注册中心对象ServiceDiscoveryRegistry</p> |
| <h3 id="1733-servicediscoveryregistry">17.3.3 ServiceDiscoveryRegistry</h3> |
| <p>ServiceDiscoveryRegistry服务发现注册中心对象的初始化过程:</p> |
| <h4 id="17331-servicediscoveryregistry的构造器">17.3.3.1 ServiceDiscoveryRegistry的构造器:</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ServiceDiscoveryRegistry</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">,</span> ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据url创建一个服务发现对象类型为ServiceDiscovery |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceDiscovery <span style="color:#719e07">=</span> createServiceDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个类型为是serviceNameMapping类型是MetadataServiceNameMapping类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceNameMapping <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>AbstractServiceNameMapping<span style="color:#719e07">)</span> ServiceNameMapping<span style="color:#719e07">.</span>getDefaultExtension<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ServiceDiscoveryRegistry中创建服务发现对象createServiceDiscovery方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> ServiceDiscovery <span style="color:#268bd2">createServiceDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getServiceDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>addParameter<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">,</span> ServiceDiscovery<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_TYPE_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ServiceDiscoveryRegistry中创建服务发现对象getServiceDiscovery方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> ServiceDiscovery <span style="color:#268bd2">getServiceDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//服务发现工厂对象的获取这里是ServiceDiscoveryFactory类型, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceDiscoveryFactory factory <span style="color:#719e07">=</span> getExtension<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务发现工厂对象获取服务发现对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> factory<span style="color:#719e07">.</span>getServiceDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ServiceDiscoveryFactory和ServiceDiscovery类型可以往后看</p> |
| <h4 id="17332-父类型failbackregistry的构造器">17.3.3.2 父类型FailbackRegistry的构造器</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">FailbackRegistry</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重试间隔配置retry.period ,默认为5秒 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>retryPeriod <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_RETRY_PERIOD_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTRY_RETRY_PERIOD<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// since the retry task will not be very much. 128 ticks is enough. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//因为重试任务不会太多。128个刻度就足够了。Dubbo封装的时间轮用于高效率的重试,这个在Kafka也自定义实现了后续可以单独来看看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> retryTimer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashedWheelTimer<span style="color:#719e07">(</span><span style="color:#719e07">new</span> NamedThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;DubboRegistryRetryTimer&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">),</span> retryPeriod<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">,</span> 128<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="17333-abstractregistry的构造器">17.3.3.3 AbstractRegistry的构造器</h4> |
| <p>参数url如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>zookeeper:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.RegistryService&amp;pid=39884&amp;release=3.0.8 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractRegistry</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> setUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryManager <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getOrDefaultApplicationModel<span style="color:#719e07">().</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>RegistryManager<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否本地缓存默认为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> localCacheEnabled <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_LOCAL_FILE_CACHE_ENABLED<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> registryCacheExecutor <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getOrDefaultFrameworkModel<span style="color:#719e07">().</span>getBeanFactory<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span>FrameworkExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSharedExecutor<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>localCacheEnabled<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Start file save timer 是否同步缓存默认为false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> syncSaveFile <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_FILESAVE_SYNC_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认缓存的文件路径与文件名字为:/Users/song/.dubbo/dubbo-registry-dubbo-demo-api-provider-127.0.0.1-2181.cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String defaultFilename <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>USER_HOME<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> DUBBO_REGISTRY <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getApplication<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">().</span>replaceAll<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;:&#34;</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;-&#34;</span><span style="color:#719e07">)</span> <span style="color:#719e07">+</span> CACHE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//未指定缓存的文件名字则用默认的文件名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String filename <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>FILE_KEY<span style="color:#719e07">,</span> defaultFilename<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> File file <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//父目录创建,保证目录存在 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ConfigUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>filename<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> file <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File<span style="color:#719e07">(</span>filename<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>exists<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">().</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">().</span>mkdirs<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Invalid registry cache file &#34;</span> <span style="color:#719e07">+</span> file <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: Failed to create directory &#34;</span> <span style="color:#719e07">+</span> file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>file <span style="color:#719e07">=</span> file<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// When starting the subscription center, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// we need to read the local cache file for future Registry fault tolerance processing. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载本地磁盘文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadProperties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//变更推送 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> notify<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getBackupUrls<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1734-将服务提供者数据转换到本地内存的元数据信息中">17.3.4 将服务提供者数据转换到本地内存的元数据信息中</h3> |
| <p>在前面我们看到了RegistryProtocol中调用register来注册服务提供者的数据到注册的中心,接下来详细看下实现原理: |
| 下面参数为ServiceDiscoveryRegistry为情况下举例子:ServiceDiscoveryRegistry类型的register方法与ZookeeperRegister注册不一样传统的接口级注册在这个方法里面就将服务数据注册到注册中心了,服务发现的数据注册分为了两步,这里仅仅将数据封装到内存中如下: |
| url例子为:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>dubbo:<span style="color:#586e75">//192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=19559&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1654938441023 |
| </span></span></span></code></pre></div><p>RegistryProtocol中的register方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">register</span><span style="color:#719e07">(</span>Registry registry<span style="color:#719e07">,</span> URL registeredProviderUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registry<span style="color:#719e07">.</span>register<span style="color:#719e07">(</span>registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面这个代码会优先走ListenerRegistryWrapper的一些逻辑来执行register方法来触发一些监听器的逻辑,我们直接跳到ServiceDiscoveryRegistry中的register方法来看</p> |
| <p>ServiceDiscoveryRegistry的register方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">register</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//逻辑判断比如只有side为提供者时候才能注册 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>shouldRegister<span style="color:#719e07">(</span>url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> <span style="color:#586e75">// Should Not Register |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> doRegister<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ServiceDiscoveryRegistry的doRegister方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doRegister</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fixme, add registry-cluster is not necessary anymore |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> addRegistryClusterKey<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serviceDiscovery<span style="color:#719e07">.</span>register<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractServiceDiscovery的register方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">register</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//metadaInfo类型为MetadataInfo类型,用来操作元数据的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataInfo<span style="color:#719e07">.</span>addService<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>MetadataInfo 类型的addService方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addService</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fixme, pass in application mode context during initialization of MetadataInfo. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//元数据参数过滤器扩展获取:MetadataParamsFilter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>loader <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getOrDefaultApplicationModel<span style="color:#719e07">().</span>getExtensionLoader<span style="color:#719e07">(</span>MetadataParamsFilter<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据参数过滤器获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>MetadataParamsFilter<span style="color:#719e07">&gt;</span> filters <span style="color:#719e07">=</span> loader<span style="color:#719e07">.</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;params-filter&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generate service level metadata |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//生成服务级别的元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceInfo serviceInfo <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceInfo<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> filters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>services<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>serviceInfo<span style="color:#719e07">.</span>getMatchKey<span style="color:#719e07">(),</span> serviceInfo<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// extract common instance level params |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extractInstanceParams<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> filters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>exportedServiceURLs <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportedServiceURLs <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentSkipListMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> addURL<span style="color:#719e07">(</span>exportedServiceURLs<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> updated <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1735-接口级服务提供者配置的注册">17.3.5 接口级服务提供者配置的注册</h3> |
| <p>前面我们通过服务发现的的url进行了举例子,其实在RegistryProtocol协议的export方法中还会注册接口级信息: |
| 例如如下关键代码: |
| 当registryUrl参数不是服务发现协议service-discovery-registry配置而是zookeeper如下时候获取到的扩展类型将是与Zookeeper相关的扩展对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>zookeeper:<span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=29386&amp;release=3.0.8&amp;timestamp=1655023329438 |
| </span></span></span></code></pre></div><p>RegistryProtocol协议的export方法中接口级数据注册的核心代码如下: |
| 如下代码的操作类型可以看注释</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// url to registry 这里registry对象的类型为ZookeeperRegistry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> URL registeredProviderUrl <span style="color:#719e07">=</span> getUrlToRegistry<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// decide if we need to delay publish (provider itself and registry should both need to register) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> register <span style="color:#719e07">=</span> providerUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> registryUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一个方法里面会将提供者的url配置写入Zookeeper的provider节点下面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>register<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> register<span style="color:#719e07">(</span>registry<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>如上代码是获取Zookeeper操作对象和向Zookeeper中写入服务提供者信息的代码,关于与Zookeeper连接和注册数据本地缓存的代码可以看ZookeeperRegistry类型和它的几个父类型比如:CacheableFailbackRegistry类型,关于接口级数据的注册可以看register方法,这个就不详细说了,下面我贴一下接口级数据注册的Zookeeper信息可以了解下就行: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register4.png" alt="在这里插入图片描述"> |
| 接口信息如下,上面我们需要注意的是这个 url配置为临时节点,当与Zookeeper断开连接或者Session超时的时候这个信息会被移除:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">/</span>dubbo<span style="color:#719e07">/</span>link<span style="color:#719e07">.</span>elastic<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>entity<span style="color:#719e07">.</span>DemoService<span style="color:#719e07">/</span>providers<span style="color:#719e07">/</span>dubbo<span style="color:#719e07">%</span>3A<span style="color:#719e07">%</span>2F<span style="color:#719e07">%</span>2F192<span style="color:#719e07">.</span>168<span style="color:#719e07">.</span>1<span style="color:#719e07">.</span>9<span style="color:#719e07">%</span>3A20880<span style="color:#719e07">%</span>2Flink<span style="color:#719e07">.</span>elastic<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>entity<span style="color:#719e07">.</span>DemoService<span style="color:#719e07">%</span>3Fanyhost<span style="color:#719e07">%</span>3Dtrue<span style="color:#719e07">%</span>26application<span style="color:#719e07">%</span>3Ddubbo<span style="color:#719e07">-</span>demo<span style="color:#719e07">-</span>api<span style="color:#719e07">-</span>provider<span style="color:#719e07">%</span>26background<span style="color:#719e07">%</span>3Dfalse<span style="color:#719e07">%</span>26deprecated<span style="color:#719e07">%</span>3Dfalse<span style="color:#719e07">%</span>26dubbo<span style="color:#719e07">%</span>3D2<span style="color:#719e07">.</span>0<span style="color:#719e07">.</span>2<span style="color:#719e07">%</span>26dynamic<span style="color:#719e07">%</span>3Dtrue<span style="color:#719e07">%</span>26generic<span style="color:#719e07">%</span>3Dfalse<span style="color:#719e07">%</span>26interface<span style="color:#719e07">%</span>3Dlink<span style="color:#719e07">.</span>elastic<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>entity<span style="color:#719e07">.</span>DemoService<span style="color:#719e07">%</span>26methods<span style="color:#719e07">%</span>3DsayHello<span style="color:#719e07">%</span>2CsayHelloAsync<span style="color:#719e07">%</span>26pid<span style="color:#719e07">%</span>3D29386<span style="color:#719e07">%</span>26release<span style="color:#719e07">%</span>3D3<span style="color:#719e07">.</span>0<span style="color:#719e07">.</span>8<span style="color:#719e07">%</span>26service<span style="color:#719e07">-</span>name<span style="color:#719e07">-</span>mapping<span style="color:#719e07">%</span>3Dtrue<span style="color:#719e07">%</span>26side<span style="color:#719e07">%</span>3Dprovider<span style="color:#719e07">%</span>26timestamp<span style="color:#719e07">%</span>3D1655023329514 |
| </span></span></code></pre></div><h2 id="174-应用级服务发现功能的实现servicediscovery">17.4 应用级服务发现功能的实现ServiceDiscovery</h2> |
| <p>在说这个实现之前我们先看看相关类型,这个服务发现相关的类型与注册中心相关的类型有点类似:</p> |
| <p>服务发现工厂类型: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register5.png" alt="在这里插入图片描述"> |
| 服务发现类型: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register6.png" alt="在这里插入图片描述"></p> |
| <p>刚刚在 ServiceDiscoveryRegistry中创建服务发现对象getServiceDiscovery方法看到了两个类型一个是服务发现工厂类型ServiceDiscoveryFactory,一个是服务发现类型ServiceDiscovery</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> ServiceDiscovery <span style="color:#268bd2">getServiceDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//服务发现工厂对象的获取这里是ServiceDiscoveryFactory类型,这里对应ZookeeperServiceDiscoveryFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceDiscoveryFactory factory <span style="color:#719e07">=</span> getExtension<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务发现工厂对象获取服务发现对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> factory<span style="color:#719e07">.</span>getServiceDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractServiceDiscoveryFactory类型的getServiceDiscovery方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> ServiceDiscovery <span style="color:#268bd2">getServiceDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个key是 zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.client.ServiceDiscovery |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//一个地址需要创建一个服务发现对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>toServiceStringWithoutResolving<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> discoveries<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> k <span style="color:#719e07">-&gt;</span> createDiscovery<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>createDiscovery方法对应ZookeeperServiceDiscoveryFactory类型中的createDiscovery方法</p> |
| <p>如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> ServiceDiscovery <span style="color:#268bd2">createDiscovery</span><span style="color:#719e07">(</span>URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ZookeeperServiceDiscovery<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><h3 id="1741-zookeeperservicediscovery">17.4.1 ZookeeperServiceDiscovery</h3> |
| <p>ZookeeperServiceDiscovery的构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ZookeeperServiceDiscovery</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">,</span> URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//先调用父类AbstractServiceDiscovery 模板类构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建 创建CuratorFramework 类型对象用于操作Zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>curatorFramework <span style="color:#719e07">=</span> buildCuratorFramework<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用级服务发现的根路径 值为/services 这个可以在Zookeeper上面看到 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>rootPath <span style="color:#719e07">=</span> ROOT_PATH<span style="color:#719e07">.</span>getParameterValue<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务发现对象 实现类型为ServiceDiscoveryImpl 这个实现来源于Curator框架中的discovery模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceDiscovery <span style="color:#719e07">=</span> buildServiceDiscovery<span style="color:#719e07">(</span>curatorFramework<span style="color:#719e07">,</span> rootPath<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动服务发现 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceDiscovery<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Create zookeeper service discovery failed.&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法比较重要是应用级服务发现的实现,这里主要关注下serviceDiscovery类型的创建与启动,这个应用级服务发现的实现其实是Dubbo使用了Curator来做的,Dubbo只是在这里封装了一些方法来进行调用Curator的实现: |
| 关于Curator的官方文档可以看<a href="https://curator.apache.org/">curator官网</a></p> |
| <p>关于Zookeeper上面注册服务应用级服务注册信息可以看如下图所示(后面会具体讲到数据注册时的调用): |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register7.png" alt="在这里插入图片描述"> |
| 我这个服务提供者注册的应用数据如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;name&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;id&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;192.168.1.9:20880&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;address&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;192.168.1.9&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;port&#34;</span> <span style="color:#719e07">:</span> 20880<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;sslPort&#34;</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;payload&#34;</span> <span style="color:#719e07">:</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;@class&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;org.apache.dubbo.registry.zookeeper.ZookeeperInstance&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;id&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;192.168.1.9:20880&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;name&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;metadata&#34;</span> <span style="color:#719e07">:</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.endpoints&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;[{\&#34;port\&#34;:20880,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}]&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.metadata-service.url-params&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;{\&#34;connections\&#34;:\&#34;1\&#34;,\&#34;version\&#34;:\&#34;1.0.0\&#34;,\&#34;dubbo\&#34;:\&#34;2.0.2\&#34;,\&#34;release\&#34;:\&#34;3.0.8\&#34;,\&#34;side\&#34;:\&#34;provider\&#34;,\&#34;port\&#34;:\&#34;20880\&#34;,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.metadata.revision&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;a662fd2213a8a49dc6ff43a4c2ae7b9e&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.metadata.storage-type&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;local&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;timestamp&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;1654916298616&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;registrationTimeUTC&#34;</span> <span style="color:#719e07">:</span> 1654917265499<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;serviceType&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;DYNAMIC&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;uriSpec&#34;</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>如果感兴趣的话可以看更详细的curator服务发现文档<a href="https://curator.apache.org/docs/service-discovery/index.html">curator-x-discovery</a></p> |
| <h3 id="1742-abstractservicediscovery的构造器">17.4.2 AbstractServiceDiscovery的构造器</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractServiceDiscovery</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">,</span> URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用重载的构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">(),</span> registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> MetadataReportInstance metadataReportInstance <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>MetadataReportInstance<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> metadataType <span style="color:#719e07">=</span> metadataReportInstance<span style="color:#719e07">.</span>getMetadataType<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReport <span style="color:#719e07">=</span> metadataReportInstance<span style="color:#719e07">.</span>getMetadataReport<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTRY_CLUSTER_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataReportInstance.getMetadataType())) { |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// this.metadataReport = metadataReportInstance.getMetadataReport(registryURL.getParameter(REGISTRY_CLUSTER_KEY)); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// } else { |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// this.metadataReport = metadataReportInstance.getNopMetadataReport(); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// } |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>重载的构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractServiceDiscovery</span><span style="color:#719e07">(</span>String serviceName<span style="color:#719e07">,</span> URL registryURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个url参考:zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.client.ServiceDiscovery&amp;pid=4570&amp;release=3.0.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>registryURL <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个serviceName参考dubbo-demo-api-provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceName <span style="color:#719e07">=</span> serviceName<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//MetadataInfo 用来封装元数据信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataInfo <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetadataInfo<span style="color:#719e07">(</span>serviceName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个是元数据缓存信息管理的类型 缓存文件使用LRU策略 感兴趣的可以详细看看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//对应缓存路径为:/Users/song/.dubbo/.metadata.zookeeper127.0.0.1%003a2181.dubbo.cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metaCacheManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetaCacheManager<span style="color:#719e07">(</span>getCacheNameSuffix<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getFrameworkModel<span style="color:#719e07">().</span>getBeanFactory<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span>FrameworkExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getCacheRefreshingScheduledExecutor<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="175-服务映射类型abstractservicenamemapping">17.5 服务映射类型AbstractServiceNameMapping</h2> |
| <p>服务映射主要是通过服务名字来反查应用信息的应用名字如下图所示 |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register8.png" alt="在这里插入图片描述"> |
| 这里我们来看下服务映射相关的类型主要通过如下代码来获取扩展对象:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceNameMapping <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>AbstractServiceNameMapping<span style="color:#719e07">)</span> ServiceNameMapping<span style="color:#719e07">.</span>getDefaultExtension<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">());</span> |
| </span></span></code></pre></div><p>对应类型如下: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register9.png" alt="在这里插入图片描述"></p> |
| <p>最终获取的扩展实现类型为:MetadataServiceNameMapping |
| 构造器如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetadataServiceNameMapping</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> metadataReportInstance <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>MetadataReportInstance<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>服务映射元数据父类型AbstractServiceNameMapping如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractServiceNameMapping</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//LRU缓存保存服务映射数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>mappingCacheManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MappingCacheManager<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getFrameworkModel<span style="color:#719e07">().</span>getBeanFactory<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getBean<span style="color:#719e07">(</span>FrameworkExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getCacheRefreshingScheduledExecutor<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="174-双注册元数据信息发布到注册中心">17.4 双注册元数据信息发布到注册中心</h2> |
| <h3 id="1741-回顾简介">17.4.1 回顾简介</h3> |
| <p>前面注册数据的时候并没有把服务配置的元数据直接注册在注册中心而是需要在导出服务之后在ServiceConfig中来发布元数据,这个就需要我们回到ServiceConfig的exportUrl方法来看了如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportUrl</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String scope <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>SCOPE_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// don&#39;t export when none is configured |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">...</span>省略到若干代码 |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>SCOPE_LOCAL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>scope<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> exportRemote<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isGeneric<span style="color:#719e07">(</span>generic<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>getScopeModel<span style="color:#719e07">().</span>isInternal<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetadataUtils<span style="color:#719e07">.</span>publishServiceDefinition<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> providerModel<span style="color:#719e07">.</span>getServiceModel<span style="color:#719e07">(),</span> getApplicationModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>urls<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1742-元数据服务定义数据的发布">17.4.2 元数据服务定义数据的发布</h3> |
| <p>在exportRemote之后单独调用发布元数据的方法来发布,通过调用元数据工具类来发布元数据信息接下来我们详细看下: |
| MetadataUtils类型的publishServiceDefinition方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishServiceDefinition</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> ServiceDescriptor serviceDescriptor<span style="color:#719e07">,</span> ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//查询是否存在元数据存储对象 对应接口MetadataReport 这里对应实现类 ZookeeperMetadataReport |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>getMetadataReports<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">).</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String msg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Remote Metadata Report Server is not provided or unavailable, will stop registering service definition to remote center!&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>msg<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String side <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getSide<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务提供者走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>PROVIDER_SIDE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>side<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String serviceKey <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getServiceKey<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取当前服务元数据信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FullServiceDefinition serviceDefinition <span style="color:#719e07">=</span> serviceDescriptor<span style="color:#719e07">.</span>getFullServiceDefinition<span style="color:#719e07">(</span>serviceKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>serviceKey<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> serviceDefinition <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceDefinition<span style="color:#719e07">.</span>setParameters<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> MetadataReport<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> getMetadataReports<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">).</span>entrySet<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetadataReport metadataReport <span style="color:#719e07">=</span> entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>metadataReport<span style="color:#719e07">.</span>shouldReportDefinition<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Report of service definition is disabled for &#34;</span> <span style="color:#719e07">+</span> entry<span style="color:#719e07">.</span>getKey<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存储服务提供者的元数据 metadataReport类型为ZookeeperMetadataReport 方法来源于父类模板方法: AbstractMetadataReport类型的storeProviderMetadata模板方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReport<span style="color:#719e07">.</span>storeProviderMetadata<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> MetadataIdentifier<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getServiceInterface<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">:</span> url<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">:</span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> PROVIDER_SIDE<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务消费者走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> MetadataReport<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> getMetadataReports<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">).</span>entrySet<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetadataReport metadataReport <span style="color:#719e07">=</span> entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>metadataReport<span style="color:#719e07">.</span>shouldReportDefinition<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Report of service definition is disabled for &#34;</span> <span style="color:#719e07">+</span> entry<span style="color:#719e07">.</span>getKey<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> metadataReport<span style="color:#719e07">.</span>storeConsumerMetadata<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> MetadataIdentifier<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getServiceInterface<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">:</span> url<span style="color:#719e07">.</span>getVersion<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">:</span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> CONSUMER_SIDE<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">()),</span> |
| </span></span><span style="display:flex;"><span> url<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ignore error |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;publish service definition metadata error.&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractMetadataReport的storeProviderMetadata方法如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否同步配置对应sync-report 默认为异步 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>syncReport<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> storeProviderMetadataTask<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportCacheExecutor<span style="color:#719e07">.</span>execute<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> storeProviderMetadataTask<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractMetadataReport的存储元数据方法storeProviderMetadataTask</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadataTask</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;store provider metadata. Identifier : &#34;</span> <span style="color:#719e07">+</span> providerMetadataIdentifier <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;; definition: &#34;</span> <span style="color:#719e07">+</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> allMetadataReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> failedReports<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Gson gson <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Gson<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String data <span style="color:#719e07">=</span> gson<span style="color:#719e07">.</span>toJson<span style="color:#719e07">(</span>serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> doStoreProviderMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> saveProperties<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> <span style="color:#719e07">!</span>syncReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// retry again. If failed again, throw exception. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> failedReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> metadataReportRetry<span style="color:#719e07">.</span>startRetryTask<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to put provider metadata &#34;</span> <span style="color:#719e07">+</span> providerMetadataIdentifier <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in &#34;</span> <span style="color:#719e07">+</span> serviceDefinition <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/17-register10.png" alt="在这里插入图片描述"></p> |
| <p>元数据信息如下:可以分为两类 应用元数据,服务元数据</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameters&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;side&#34;</span>: <span style="color:#2aa198">&#34;provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;interface&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;pid&#34;</span>: <span style="color:#2aa198">&#34;22099&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;application&#34;</span>: <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo&#34;</span>: <span style="color:#2aa198">&#34;2.0.2&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;release&#34;</span>: <span style="color:#2aa198">&#34;3.0.8&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;anyhost&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.ip&#34;</span>: <span style="color:#2aa198">&#34;192.168.1.9&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: <span style="color:#2aa198">&#34;sayHello,sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;background&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;deprecated&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dynamic&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;service-name-mapping&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;generic&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.port&#34;</span>: <span style="color:#2aa198">&#34;20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;timestamp&#34;</span>: <span style="color:#2aa198">&#34;1654942353902&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;canonicalName&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;codeSource&#34;</span>: <span style="color:#2aa198">&#34;file:/Users/song/Desktop/dubbo-test/target/classes/&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: [{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [<span style="color:#2aa198">&#34;java.lang.String&#34;</span>], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [] |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHello&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [<span style="color:#2aa198">&#34;java.lang.String&#34;</span>], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [] |
| </span></span><span style="display:flex;"><span> }], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;types&#34;</span>: [{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;result&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;stack&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span> |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;next&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;status&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> }], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [] |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>Zookeeper扩展类型ZookeeperMetadataReport实现的存储方法如下所示doStoreProviderMetadata:</p> |
| <p>如果我们自己实现一套元数据就可以重写这个方法来进行元数据的额存储</p> |
| <p>ZookeeperMetadataReport的doStoreProviderMetadata</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doStoreProviderMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> String serviceDefinitions<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> storeMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinitions<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ZookeeperMetadataReport的storeMetadata</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier metadataIdentifier<span style="color:#719e07">,</span> String v<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//参数false为非临时节点,这个元数据为持久节点,这个细节就暂时不看了就是将刚刚的json元数据存储到对应路径上面:路径为:/dubbo/metadata/link.elastic.dubbo.entity.DemoService/provider/dubbo-demo-api-provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> zkClient<span style="color:#719e07">.</span>create<span style="color:#719e07">(</span>getNodePath<span style="color:#719e07">(</span>metadataIdentifier<span style="color:#719e07">),</span> v<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/17-dubbo3-ying-yong-ji-zhu-ce-zhi-fu-wu-ti-gong-zhe-de-shuang-zhu-ce-yuan-li/">17-Dubbo服务提供者的双注册原理</a></p></description></item><item><title>Blog: 16-模块发布器发布服务全过程</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/16/16-%E6%A8%A1%E5%9D%97%E5%8F%91%E5%B8%83%E5%99%A8%E5%8F%91%E5%B8%83%E6%9C%8D%E5%8A%A1%E5%85%A8%E8%BF%87%E7%A8%8B/</link><pubDate>Tue, 16 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/16/16-%E6%A8%A1%E5%9D%97%E5%8F%91%E5%B8%83%E5%99%A8%E5%8F%91%E5%B8%83%E6%9C%8D%E5%8A%A1%E5%85%A8%E8%BF%87%E7%A8%8B/</guid><description> |
| <h1 id="16-模块发布器发布服务全过程">16-模块发布器发布服务全过程</h1> |
| <h2 id="161-简介">16.1 简介</h2> |
| <p>Dubbo做为服务治理框架,比较核心的就是服务相关的概念,这里我先贴个找到的关于Dubbo工作原理的架构图: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/16-deploy.png" alt="在这里插入图片描述"> |
| 如果按完整服务启动与订阅的顺序我们可以归结为以下6点:</p> |
| <ul> |
| <li>导出服务(提供者) |
| <ul> |
| <li>服务提供方通过指定端口对外暴露服务</li> |
| </ul> |
| </li> |
| <li>注册服务(提供者) |
| <ul> |
| <li>提供方向注册中心注册自己的信息</li> |
| </ul> |
| </li> |
| <li>(服务发现)-订阅服务(消费者) |
| <ul> |
| <li>服务调用方通过注册中心订阅自己感兴趣的服务</li> |
| </ul> |
| </li> |
| <li>(服务发现)-服务推送(消费者) |
| <ul> |
| <li>注册中心向调用方推送地址列表</li> |
| </ul> |
| </li> |
| <li>调用服务(消费者调用提供者) |
| <ul> |
| <li>调用方选择一个地址发起RPC调用</li> |
| </ul> |
| </li> |
| <li>监控服务 |
| <ul> |
| <li>服务提供方和调用方的统计数据由监控模块收集展示</li> |
| </ul> |
| </li> |
| </ul> |
| <p>上面的完整的服务启动订阅与调用流程不仅仅适用于Dubbo 同样也适用于其他服务治理与发现的模型, 一般服务发现与服务调用的思路就是这样的,我们将以上内容扩展,暴漏服务可以使用http,tcp,udp等各种协议,注册服务可以注册到Redis,Dns,Etcd,Zookeeper等注册中心中,订阅服务可以主动去注册中心查询服务列表,服务发现可以让注册中心将服务数据动态推送给消费者.Dubbo其实就是基于这种简单的服务模型来扩展出各种功能的支持,来满足服务治理的各种场景,了解了这里可能各位同学就想着自行开发一个简单的微服务框架了。</p> |
| <p>回到主题,从以上的服务完整发布调用流程可以看到,所有的功能都是由导出服务(提供者)开始的,只有提供者先提供了服务才可以有真正的服务让消费者调用。</p> |
| <p>之前的博客内容 链接:<a href="https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/">&laquo;12-全局视野来看Dubbo3.0.8的服务启动生命周期&raquo;</a> 我们了解了 DefaultModuleDeployer模块器启动的流程,其中在start代码的模版方法中开始了导出服务的功能,这里我们来详细看下服务发布的全过程:</p> |
| <p>入口代码: DefaultModuleDeployer的发布服务方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportServices</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ServiceConfigBase sc <span style="color:#719e07">:</span> configManager<span style="color:#719e07">.</span>getServices<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportServiceInternal<span style="color:#719e07">(</span>sc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="162-导出服务的入口">16.2 导出服务的入口</h2> |
| <p>入口代码: DefaultModuleDeployer的发布服务方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportServices</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ServiceConfigBase sc <span style="color:#719e07">:</span> configManager<span style="color:#719e07">.</span>getServices<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportServiceInternal<span style="color:#719e07">(</span>sc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>主要流程为遍历初始化的服务配置列表然后逐个服务开始到处 |
| 内部导出服务代码: |
| exportServiceInternal方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportServiceInternal</span><span style="color:#719e07">(</span>ServiceConfigBase sc<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ServiceConfig<span style="color:#719e07">&lt;?&gt;</span> serviceConfig <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ServiceConfig<span style="color:#719e07">&lt;?&gt;)</span> sc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务配置刷新 配置优先级覆盖 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>serviceConfig<span style="color:#719e07">.</span>isRefreshed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceConfig<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务已经导出过了就直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>sc<span style="color:#719e07">.</span>isExported<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否异步方式导出 全局配置或者服务级其中一个配置了异步则异步处理 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>exportAsync <span style="color:#719e07">||</span> sc<span style="color:#719e07">.</span>shouldExportAsync<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//异步其实就是使用线程来导出服务serviceExportExecutor |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExecutorService executor <span style="color:#719e07">=</span> executorRepository<span style="color:#719e07">.</span>getServiceExportExecutor<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> CompletableFuture<span style="color:#719e07">&lt;</span>Void<span style="color:#719e07">&gt;</span> future <span style="color:#719e07">=</span> CompletableFuture<span style="color:#719e07">.</span>runAsync<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>sc<span style="color:#719e07">.</span>isExported<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> sc<span style="color:#719e07">.</span>export<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> exportedServices<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>sc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; export async catch error : &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">},</span> executor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> asyncExportingFutures<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>future<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//同步导出服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>sc<span style="color:#719e07">.</span>isExported<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> sc<span style="color:#719e07">.</span>export<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> exportedServices<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>sc<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个逻辑里面做了一些基本的操作,可以直接看注释然后调用ServiceConfig的export的来导出服务,继续往后看服务配置的导出服务方法。</p> |
| <h2 id="163-服务配置导出服务模板方法">16.3 服务配置导出服务模板方法</h2> |
| <p>核心的服务导出代码是在服务配置中来做的ServiceConfig的 export() 方法 |
| ServiceConfig的 export() 方法代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">export</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//已经导出过服务直接放那会 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>exported<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ensure start module, compatible with old api usage |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//确保模块启动了(基本的初始化操作执行了) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getScopeModel<span style="color:#719e07">().</span>getDeployer<span style="color:#719e07">().</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//悲观锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>exported<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置是否刷新 前面初始化时候已经刷新过配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>isRefreshed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务导出配置配置为false则不导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>shouldExport<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务发布前初始化一下元数据对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>init<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>shouldDelay<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置了服务的延迟发布配置则走延迟发布逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doDelayExport<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//导出服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doExport<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1631-服务配置导出服务前的初始化方法">16.3.1 服务配置导出服务前的初始化方法</h3> |
| <p>ServiceConfig 导出服务之前的初始化方法init</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载服务监听器 这里暂时没有服务监听器扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// load ServiceListeners from extension |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ServiceListener<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ServiceListener<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceListeners<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>extensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务提供者配置传递给元数据配置对象 一个服务提供者配置会有一个元数据配置,服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initServiceMetadata<span style="color:#719e07">(</span>provider<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>setServiceType<span style="color:#719e07">(</span>getInterfaceClass<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> serviceMetadata<span style="color:#719e07">.</span>setTarget<span style="color:#719e07">(</span>getRef<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据的key格式为 group/服务接口:版本号 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>generateServiceKey<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="164-服务配置导出服务模板方法2">16.4 服务配置导出服务模板方法2</h2> |
| <p>ServiceConfig 导出服务核心逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExport</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//取消发布 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>unexported<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has already unexported!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//已经发布 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>exported<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务路径 为空则设置为接口名,本例子中为link.elastic.dubbo.entity.DemoService |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> interfaceName<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//导出URL |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doExportUrls<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exported<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1641-导出服务的url配置逻辑">16.4.1 导出服务的URL配置逻辑</h3> |
| <p>ServiceConfig 导出URL核心逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrls</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块服务存储库 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ModuleServiceRepository repository <span style="color:#719e07">=</span> getScopeModel<span style="color:#719e07">().</span>getServiceRepository<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ServiceDescriptor serviceDescriptor<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ref为服务实现类型 这里对应我们例子的DemoServiceImpl |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">boolean</span> serverService <span style="color:#719e07">=</span> ref <span style="color:#719e07">instanceof</span> ServerService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span><span style="color:#719e07">(</span>serverService<span style="color:#719e07">){</span> |
| </span></span><span style="display:flex;"><span> serviceDescriptor<span style="color:#719e07">=((</span>ServerService<span style="color:#719e07">)</span> ref<span style="color:#719e07">).</span>getServiceDescriptor<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>serviceDescriptor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span><span style="color:#719e07">else</span><span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//我们代码走这个逻辑 注册服务 这个注册不是向注册中心注册 这个是解析服务接口将服务方法等描述信息存放在了服务存储ModuleServiceRepository类型对象的成员变量services中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceDescriptor <span style="color:#719e07">=</span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>getInterfaceClass<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供者领域模型, 提供者领域模型 封装了一些提供者需要的就基本属性同时内部解析封装方法信息 ProviderMethodModel 列表 , 服务标识符 格式group/服务接:版本号 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> providerModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ProviderModel<span style="color:#719e07">(</span>getUniqueServiceName<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务实现类DemoServiceImpl |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ref<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务描述符 描述符里面包含了服务接口的方法信息,不过服务接口通过反射也可以拿到方法信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceDescriptor<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前所处模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getScopeModel<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前服务接口的元数据对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块服务存储库存储提供者模型对象ModuleServiceRepository |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> repository<span style="color:#719e07">.</span>registerProvider<span style="color:#719e07">(</span>providerModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置的注册中心列表 ,同时将注册中心配置转URL (在Dubbo中URL就是配置信息的一种形式) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里会获取到两个 由dubbo.application.register-mode 双注册配置决定 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//注册中心 registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//service-discovery-registry://8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//参数dubbo是dubbo协议的版本不是Dubbo版本 Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里后面详细说下 服务双注册 dubbo.application.register-mode |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs <span style="color:#719e07">=</span> ConfigValidationUtils<span style="color:#719e07">.</span>loadRegistries<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ProtocolConfig protocolConfig <span style="color:#719e07">:</span> protocols<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String pathKey <span style="color:#719e07">=</span> URL<span style="color:#719e07">.</span>buildKey<span style="color:#719e07">(</span>getContextPath<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span>p <span style="color:#719e07">-&gt;</span> p <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;/&#34;</span> <span style="color:#719e07">+</span> path<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>orElse<span style="color:#719e07">(</span>path<span style="color:#719e07">),</span> group<span style="color:#719e07">,</span> version<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// stub service will use generated service name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span><span style="color:#719e07">(!</span>serverService<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// In case user specified path, register service one more time to map it to path. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//模块服务存储库ModuleServiceRepository存储服务接口信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> repository<span style="color:#719e07">.</span>registerService<span style="color:#719e07">(</span>pathKey<span style="color:#719e07">,</span> interfaceClass<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//导出根据协议导出配置到注册中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doExportUrlsFor1Protocol<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1642-应用级和接口级服务注册地址获取">16.4.2 应用级和接口级服务注册地址获取</h3> |
| <p>这里主要看下注册中心的获取,这里涉及到服务的双注册配置</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs <span style="color:#719e07">=</span> ConfigValidationUtils<span style="color:#719e07">.</span>loadRegistries<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>关于loadRegistries方法的详情我们就不看了主要看loadRegistries方法中调用的genCompatibleRegistries添加服务发现注册中心</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param scopeModel 域模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param registryList 配置的注册中心列表 例如:registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param provider 是否为服务提供者 这里Demo为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">genCompatibleRegistries</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryList<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> result <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>registryList<span style="color:#719e07">.</span>size<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的注册中心 为每个注册中心增加兼容的服务发现注册中心地址配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryList<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>registryURL <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否为提供者 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>provider<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// for registries enabled service discovery, automatically register interface compatible addresses. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> String registerMode<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册协议配置了service-discovery-registry 走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//前面这个逻辑是直接接给result结果中添加应用级注册,如果是all配置则增加接口级注册信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_MODE_KEY<span style="color:#719e07">,</span> ConfigurationUtils<span style="color:#719e07">.</span>getCachedDynamicProperty<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> DUBBO_REGISTER_MODE_DEFAULT_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTER_MODE_INSTANCE<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isValidRegisterMode<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> DEFAULT_REGISTER_MODE_INSTANCE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里配置的就是应用级配置 则先添加应用级地址,再根据条件判断是否添加接口级注册中心地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> registryNotExists<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">,</span> registryList<span style="color:#719e07">,</span> REGISTRY_PROTOCOL<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> URL interfaceCompatibleRegistryURL <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>REGISTRY_PROTOCOL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_TYPE_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>interfaceCompatibleRegistryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//正常情况下我们的配置会走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 获取服务注册的注册模式 配置为dubbo.application.register-mode 默认值为all 既注册接口数据又注册应用级信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerMode <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_MODE_KEY<span style="color:#719e07">,</span> ConfigurationUtils<span style="color:#719e07">.</span>getCachedDynamicProperty<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">,</span> DUBBO_REGISTER_MODE_DEFAULT_KEY<span style="color:#719e07">,</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isValidRegisterMode<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> DEFAULT_REGISTER_MODE_INTERFACE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据逻辑条件判断是否添加应用级注册中心地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>DEFAULT_REGISTER_MODE_INSTANCE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> registryNotExists<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">,</span> registryList<span style="color:#719e07">,</span> SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> URL serviceDiscoveryRegistryURL <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>REGISTRY_TYPE_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>serviceDiscoveryRegistryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据逻辑条件判断是否添加接口级注册中心地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>DEFAULT_REGISTER_MODE_INTERFACE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> FrameworkStatusReportService reportService <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">).</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>FrameworkStatusReportService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> reportService<span style="color:#719e07">.</span>reportRegistrationStatus<span style="color:#719e07">(</span>reportService<span style="color:#719e07">.</span>createRegistrationReport<span style="color:#719e07">(</span>registerMode<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> result<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> result<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个方法是根据服务注册模式来判断使用接口级注册地址还是应用级注册地址分别如下所示: |
| 配置信息: |
| dubbo.application.register-mode |
| 配置值:</p> |
| <ul> |
| <li>interface |
| <ul> |
| <li>接口级注册</li> |
| </ul> |
| </li> |
| <li>instance |
| <ul> |
| <li>应用级注册</li> |
| </ul> |
| </li> |
| <li>all |
| <ul> |
| <li>接口级别和应用级都注册</li> |
| </ul> |
| </li> |
| </ul> |
| <p>接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><p>应用级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><h2 id="165-导出服务配置到本地和注册中心">16.5 导出服务配置到本地和注册中心</h2> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> doExportUrlsFor1Protocol<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>protocolConfig为:dubbo协议的配置 |
| &lt;dubbo:protocol port=&quot;-1&quot; name=&ldquo;dubbo&rdquo; /&gt;</p> |
| <p>registryURLs目前有两个 应用级注册地址和接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><h3 id="1651-导出服务配置的doexporturlsfor1protocol方法">16.5.1 导出服务配置的doExportUrlsFor1Protocol方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrlsFor1Protocol</span><span style="color:#719e07">(</span>ProtocolConfig protocolConfig<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成协议配置具体可见下图中的元数据配置中的attachments |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> map <span style="color:#719e07">=</span> buildAttributes<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// remove null key and null value |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//移除空值 简化配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> map<span style="color:#719e07">.</span>keySet<span style="color:#719e07">().</span>removeIf<span style="color:#719e07">(</span>key <span style="color:#719e07">-&gt;</span> key <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> map<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">)</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init serviceMetadata attachments |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//协议配置放到元数据对象中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>getAttachments<span style="color:#719e07">().</span>putAll<span style="color:#719e07">(</span>map<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//协议配置 + 默认协议配置转URL类型的配置存储 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> buildUrl<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">,</span> map<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//导出url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/16-deploy2.png" alt="在这里插入图片描述"></p> |
| <h3 id="1652-导出服务配置模板方法">16.5.2 导出服务配置模板方法</h3> |
| <p>继续看导出服务的模板方法,分为本地导出和注册中心导出 |
| //参数url为协议配置url可以参考:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>dubbo:<span style="color:#586e75">//192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=10953&amp;release=3.0.8&amp;side=provider&amp;timestamp=1653705630518 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportUrl</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String scope <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>SCOPE_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// don&#39;t export when none is configured |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>SCOPE_NONE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>scope<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export to local if the config is not remote (export to remote only when config is remote) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//未明确指定远程导出 则开启本地导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>SCOPE_REMOTE<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>scope<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> exportLocal<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//未明确指定本地导出 则开启远程导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// export to remote if the config is not local (export to local only when config is local) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>SCOPE_LOCAL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>scope<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> exportRemote<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> registryURLs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isGeneric<span style="color:#719e07">(</span>generic<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>getScopeModel<span style="color:#719e07">().</span>isInternal<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> MetadataUtils<span style="color:#719e07">.</span>publishServiceDefinition<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> providerModel<span style="color:#719e07">.</span>getServiceModel<span style="color:#719e07">(),</span> getApplicationModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>urls<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="166-导出服务到本地">16.6 导出服务到本地</h2> |
| <p>本地调用使用了 injvm 协议,是一个伪协议,它不开启端口,不发起远程调用,只在 JVM 内直接关联,但执行 Dubbo 的 Filter 链。</p> |
| <p>直接通过代码来看吧</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">exportLocal</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//协议转为injvm 代表本地导出 host为127.0.0.1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL local <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>LOCAL_PROTOCOL<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setHost<span style="color:#719e07">(</span>LOCALHOST_VALUE<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setPort<span style="color:#719e07">(</span>0<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> local <span style="color:#719e07">=</span> local<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setServiceModel<span style="color:#719e07">(</span>providerModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> doExportUrl<span style="color:#719e07">(</span>local<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Export dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to local registry url : &#34;</span> <span style="color:#719e07">+</span> local<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1661-doexporturl方法">16.6.1 doExportUrl方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrl</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> withMetaData<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里是由adaptor扩展类型处理过的 我们直接看默认的类型javassist 对应JavassistProxyFactory代理工厂 获取调用对象 ( |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Invoker<span style="color:#719e07">&lt;?&gt;</span> invoker <span style="color:#719e07">=</span> proxyFactory<span style="color:#719e07">.</span>getInvoker<span style="color:#719e07">(</span>ref<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>Class<span style="color:#719e07">)</span> interfaceClass<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>withMetaData<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DelegateProviderMetaDataInvoker<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Exporter<span style="color:#719e07">&lt;?&gt;</span> exporter <span style="color:#719e07">=</span> protocolSPI<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exporters<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>exporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1662-javassistproxyfactory类型的getinvoker方法">16.6.2 JavassistProxyFactory类型的getInvoker方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getInvoker</span><span style="color:#719e07">(</span>T proxy<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// TODO Wrapper cannot handle this scenario correctly: the classname contains &#39;$&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 创建实际服务提供者的代理类型,代理类型后缀为DubboWrap在这里类型为 link.elastic.dubbo.entity.DemoServiceImplDubboWrap0 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Wrapper wrapper <span style="color:#719e07">=</span> Wrapper<span style="color:#719e07">.</span>getWrapper<span style="color:#719e07">(</span>proxy<span style="color:#719e07">.</span>getClass<span style="color:#719e07">().</span>getName<span style="color:#719e07">().</span>indexOf<span style="color:#719e07">(</span><span style="color:#2aa198">&#39;$&#39;</span><span style="color:#719e07">)</span> <span style="color:#719e07">&lt;</span> 0 <span style="color:#719e07">?</span> proxy<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个匿名内部类对象 继承自AbstractProxyInvoker的Invoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> AbstractProxyInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>proxy<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> Object <span style="color:#268bd2">doInvoke</span><span style="color:#719e07">(</span>T proxy<span style="color:#719e07">,</span> String methodName<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;[]</span> parameterTypes<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> Object<span style="color:#719e07">[]</span> arguments<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Throwable <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> wrapper<span style="color:#719e07">.</span>invokeMethod<span style="color:#719e07">(</span>proxy<span style="color:#719e07">,</span> methodName<span style="color:#719e07">,</span> parameterTypes<span style="color:#719e07">,</span> arguments<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">};</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable fromJavassist<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// try fall back to JDK proxy factory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1663-使用协议导出调用对象-export">16.6.3 使用协议导出调用对象 export</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> Exporter<span style="color:#719e07">&lt;?&gt;</span> exporter <span style="color:#719e07">=</span> protocolSPI<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这个使用了Adaptor扩展和Wrapper机制Debug起来不太方便这里贴一下调用堆栈<img src="https://dubbo.apache.org/imgs/blog/source-blog/16-deploy3.png" alt="在这里插入图片描述"></p> |
| <h3 id="16631-协议序列化机制protocolserializationwrapper">16.6.3.1 协议序列化机制ProtocolSerializationWrapper</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里主要逻辑是将服务提供者url添加到服务存储仓库中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getFrameworkModel<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">().</span>getScopeModel<span style="color:#719e07">()).</span>getServiceRepository<span style="color:#719e07">().</span>registerProviderUrl<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="16632-协议过滤器wrapper-protocolfilterwrapper">16.6.3.2 协议过滤器Wrapper ProtocolFilterWrapper</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心的协议导出直接执行 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//过滤器调用链FilterChainBuilder的扩展对象查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FilterChainBuilder builder <span style="color:#719e07">=</span> getFilterChainBuilder<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为2步 生成过滤器调用链 然后使用链表中的节点调用 这里值查询provider类型的过滤器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>builder<span style="color:#719e07">.</span>buildInvokerChain<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> SERVICE_FILTER_KEY<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>PROVIDER<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>过滤器调用链的生成 对用DefaultFilterChainBuilder类型的buildInvokerChain方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">buildInvokerChain</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> originalInvoker<span style="color:#719e07">,</span> String key<span style="color:#719e07">,</span> String group<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//originalInvoker代表真正的服务调用器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> last <span style="color:#719e07">=</span> originalInvoker<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> URL url <span style="color:#719e07">=</span> originalInvoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ModuleModel<span style="color:#719e07">&gt;</span> moduleModels <span style="color:#719e07">=</span> getModuleModelsFromUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Filter<span style="color:#719e07">&gt;</span> filters<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModels <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> moduleModels<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//类型Filter key为service.filter 分组为provider 所有提供者过滤器拉取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> filters <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Filter<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> moduleModels<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">)).</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> group<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModels <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> moduleModels<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ExtensionDirector<span style="color:#719e07">&gt;</span> directors <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ModuleModel moduleModel <span style="color:#719e07">:</span> moduleModels<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Filter<span style="color:#719e07">&gt;</span> tempFilters <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Filter<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> moduleModel<span style="color:#719e07">).</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> group<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> filters<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>tempFilters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> directors<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> sortingAndDeduplication<span style="color:#719e07">(</span>filters<span style="color:#719e07">,</span> directors<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Filter<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">).</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> group<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//倒序拼接,将过滤器的调用对象添加到链表中 最后倒序遍历之后 last节点指向了调用链路链表头节点的对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>filters<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> filters<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">-</span> 1<span style="color:#719e07">;</span> i <span style="color:#719e07">&gt;=</span> 0<span style="color:#719e07">;</span> i<span style="color:#719e07">--)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> Filter filter <span style="color:#719e07">=</span> filters<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>i<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> next <span style="color:#719e07">=</span> last<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//每个invoker对象中都有originalInvoker对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> last <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CopyOfFilterChainNode<span style="color:#719e07">&lt;&gt;(</span>originalInvoker<span style="color:#719e07">,</span> next<span style="color:#719e07">,</span> filter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> CallbackRegistrationInvoker<span style="color:#719e07">&lt;&gt;(</span>last<span style="color:#719e07">,</span> filters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> last<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/16-deploy4.png" alt="在这里插入图片描述"></p> |
| <h3 id="16633-协议监听器wrapper-protocollistenerwrapper">16.6.3.3 协议监听器Wrapper ProtocolListenerWrapper</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心地址则直接导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 先导出对象 再创建过滤器包装对象 执行监听器逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ListenerExporterWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> Collections<span style="color:#719e07">.</span>unmodifiableList<span style="color:#719e07">(</span>ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExporterListener<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">().</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getActivateExtension<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">(),</span> EXPORTER_LISTENER_KEY<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="16634-qos的协议wrapper-qosprotocolwrapper">16.6.3.4 QOS的协议Wrapper QosProtocolWrapper</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心导出的时候开启QOS 默认端口22222 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startQosServer<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="16635-injvmprotocol-的导出方法">16.6.3.5 InjvmProtocol 的导出方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> InjvmExporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>invoker<span style="color:#719e07">,</span> invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">().</span>getServiceKey<span style="color:#719e07">(),</span> exporterMap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="167-导出服务到注册中心">16.7 导出服务到注册中心</h2> |
| <p>16.5.2 导出服务配置模板方法 中我们看到了服务导出会导出到本地和远程,接下来就看下导出到远程的方法exportRemote |
| 参数url:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>dubbo:<span style="color:#586e75">//192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=12865&amp;release=3.0.8&amp;side=provider&amp;timestamp=1653708351378 |
| </span></span></span></code></pre></div><p>参数registryURLs目前有两个 应用级注册地址和接口级注册地址:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry:<span style="color:#586e75">//127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=9008&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653703292768 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>service<span style="color:#719e07">-</span>discovery<span style="color:#719e07">-</span>registry<span style="color:#719e07">:</span><span style="color:#586e75">//8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=10275&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653704425920 |
| </span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> URL <span style="color:#268bd2">exportRemote</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>registryURLs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有注册地址与注册模式 逐个注册 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>URL registryURL <span style="color:#719e07">:</span> registryURLs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为协议URL 添加应用级注册service-discovery-registry参数service-name-mapping为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>SERVICE_REGISTRY_PROTOCOL<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>SERVICE_NAME_MAPPING_KEY<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//if protocol is only injvm ,not register |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOCAL_PROTOCOL<span style="color:#719e07">.</span>equalsIgnoreCase<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为协议url 添加动态配置dynamic |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>DYNAMIC_KEY<span style="color:#719e07">,</span> registryURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>DYNAMIC_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//监控配置暂时为null |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL monitorUrl <span style="color:#719e07">=</span> ConfigValidationUtils<span style="color:#719e07">.</span>loadMonitor<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> registryURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>monitorUrl <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>putAttribute<span style="color:#719e07">(</span>MONITOR_KEY<span style="color:#719e07">,</span> monitorUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// For providers, this is used to enable custom proxy to generate invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String proxy <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>PROXY_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>proxy<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registryURL <span style="color:#719e07">=</span> registryURL<span style="color:#719e07">.</span>addParameter<span style="color:#719e07">(</span>PROXY_KEY<span style="color:#719e07">,</span> proxy<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//开始注册服务了 打印个认知 提示下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Register dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; url &#34;</span> <span style="color:#719e07">+</span> url <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to registry &#34;</span> <span style="color:#719e07">+</span> registryURL<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Export dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to url &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> doExportUrl<span style="color:#719e07">(</span>registryURL<span style="color:#719e07">.</span>putAttribute<span style="color:#719e07">(</span>EXPORT_KEY<span style="color:#719e07">,</span> url<span style="color:#719e07">),</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Export dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to url &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> doExportUrl<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> url<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1671---doexporturl方法">16.7.1 doExportUrl方法</h3> |
| <p>与 16.6.1 doExportUrl方法 导出本地协议是一样的逻辑 ,我们来看看点不同地方</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrl</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> withMetaData<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Invoker<span style="color:#719e07">&lt;?&gt;</span> invoker <span style="color:#719e07">=</span> proxyFactory<span style="color:#719e07">.</span>getInvoker<span style="color:#719e07">(</span>ref<span style="color:#719e07">,</span> <span style="color:#719e07">(</span>Class<span style="color:#719e07">)</span> interfaceClass<span style="color:#719e07">,</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>withMetaData<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//远程服务导出逐个值为true 元数据invoker包装一下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invoker <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DelegateProviderMetaDataInvoker<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Exporter<span style="color:#719e07">&lt;?&gt;</span> exporter <span style="color:#719e07">=</span> protocolSPI<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exporters<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>exporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>与本地导出ProtocolFilterWrapper的不同之处 |
| 服务发现service-discovery-registry的导出UrlUtils.isRegistry(invoker.getUrl() 判断结果为true会走这个逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心的协议导出直接执行 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 服务发现service-discovery-registry的导出会走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//过滤器调用链FilterChainBuilder的扩展对象查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FilterChainBuilder builder <span style="color:#719e07">=</span> getFilterChainBuilder<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为2步 生成过滤器调用链 然后使用链表中的节点调用 这里值查询provider类型的过滤器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>builder<span style="color:#719e07">.</span>buildInvokerChain<span style="color:#719e07">(</span>invoker<span style="color:#719e07">,</span> SERVICE_FILTER_KEY<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>PROVIDER<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>与 协议监听器Wrapper ProtocolListenerWrapper 的不同之处</p> |
| <p>服务发现service-discovery-registry的导出UrlUtils.isRegistry(invoker.getUrl() 判断结果为true会走这个逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心地址则直接导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 服务发现service-discovery-registry的导出会走这个逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 先导出对象 再创建过滤器包装对象 执行监听器逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ListenerExporterWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> Collections<span style="color:#719e07">.</span>unmodifiableList<span style="color:#719e07">(</span>ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExporterListener<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">().</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getActivateExtension<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">(),</span> EXPORTER_LISTENER_KEY<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>与 16.6.3.4 QOS的协议Wrapper QosProtocolWrapper 不同之处</p> |
| <p>服务发现service-discovery-registry的导出UrlUtils.isRegistry(invoker.getUrl() 判断结果为true会走这个逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心导出的时候开启QOS 默认端口22222 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>UrlUtils<span style="color:#719e07">.</span>isRegistry<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startQosServer<span style="color:#719e07">(</span>invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>启动QOS服务startQosServer</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startQosServer</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>hasStarted<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> qosEnable <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>QOS_ENABLE<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>qosEnable<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos won&#39;t be started because it is disabled. &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;Please check dubbo.application.qos.enable is configured either in system property, &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.properties or XML/spring-boot configuration.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String host <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>QOS_HOST<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> port <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>QOS_PORT<span style="color:#719e07">,</span> QosConstants<span style="color:#719e07">.</span>DEFAULT_PORT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> acceptForeignIp <span style="color:#719e07">=</span> Boolean<span style="color:#719e07">.</span>parseBoolean<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>ACCEPT_FOREIGN_IP<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;false&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> Server server <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>Server<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>setHost<span style="color:#719e07">(</span>host<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>setPort<span style="color:#719e07">(</span>port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>setAcceptForeignIp<span style="color:#719e07">(</span>acceptForeignIp<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable throwable<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Fail to start qos server: &#34;</span><span style="color:#719e07">,</span> throwable<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>QOS的Server的启动方法start</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> Throwable <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>started<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//1个主线程 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> boss <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> NioEventLoopGroup<span style="color:#719e07">(</span>1<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> DefaultThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos-boss&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//0个从线程 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> worker <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> NioEventLoopGroup<span style="color:#719e07">(</span>0<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> DefaultThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos-worker&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务端启动器,和参数设置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServerBootstrap serverBootstrap <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServerBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>group<span style="color:#719e07">(</span>boss<span style="color:#719e07">,</span> worker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>channel<span style="color:#719e07">(</span>NioServerSocketChannel<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>option<span style="color:#719e07">(</span>ChannelOption<span style="color:#719e07">.</span>SO_REUSEADDR<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>childOption<span style="color:#719e07">(</span>ChannelOption<span style="color:#719e07">.</span>TCP_NODELAY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>childHandler<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ChannelInitializer<span style="color:#719e07">&lt;</span>Channel<span style="color:#719e07">&gt;()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initChannel</span><span style="color:#719e07">(</span>Channel ch<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ch<span style="color:#719e07">.</span>pipeline<span style="color:#719e07">().</span>addLast<span style="color:#719e07">(</span><span style="color:#719e07">new</span> QosProcessHandler<span style="color:#719e07">(</span>frameworkModel<span style="color:#719e07">,</span> welcome<span style="color:#719e07">,</span> acceptForeignIp<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isBlank<span style="color:#719e07">(</span>host<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>bind<span style="color:#719e07">(</span>port<span style="color:#719e07">).</span>sync<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serverBootstrap<span style="color:#719e07">.</span>bind<span style="color:#719e07">(</span>host<span style="color:#719e07">,</span> port<span style="color:#719e07">).</span>sync<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos-server bind localhost:&#34;</span> <span style="color:#719e07">+</span> port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable throwable<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;qos-server can not bind localhost:&#34;</span> <span style="color:#719e07">+</span> port<span style="color:#719e07">,</span> throwable<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> throwable<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>QOS处理器为QosProcessHandler关于QosProcessHandler的细节这里先不说</p> |
| <p>最后一个不同的地方调用链路走的这个 RegistryProtocol</p> |
| <h3 id="1672-通过注册协议导出服务与注册服务的流程">16.7.2 通过注册协议导出服务与注册服务的流程</h3> |
| <p>RegistryProtocol的导出方法: |
| 这个方法非常重要也是服务注册的核心代码,先概括下包含了哪些步骤</p> |
| <ul> |
| <li>覆盖配置</li> |
| <li>导出协议端口开启TCP服务</li> |
| <li>注册到注册中心</li> |
| <li>通知服务启动了</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> originInvoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//service-discovery-registry://8.131.79.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&amp;dubbo=2.0.2&amp;pid=14256&amp;registry=zookeeper&amp;release=3.0.8&amp;timestamp=1653710477057 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL registryUrl <span style="color:#719e07">=</span> getRegistryUrl<span style="color:#719e07">(</span>originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// url to export locally |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=14256&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1653710479073 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL providerUrl <span style="color:#719e07">=</span> getProviderUrl<span style="color:#719e07">(</span>originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Subscribe the override data |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// the same service. Because the subscribed is cached key with the name of the service, it causes the |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// subscription information to cover. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//provider://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;category=configurators&amp;check=false&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=14256&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1653710479073 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> URL overrideSubscribeUrl <span style="color:#719e07">=</span> getSubscribedOverrideUrl<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//override配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> OverrideListener overrideSubscribeListener <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> OverrideListener<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">,</span> originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">,</span> NotifyListener<span style="color:#719e07">&gt;</span> overrideListeners <span style="color:#719e07">=</span> getProviderConfigurationListener<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">).</span>getOverrideListeners<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> overrideListeners<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">,</span> overrideSubscribeListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> providerUrl <span style="color:#719e07">=</span> overrideUrlWithConfig<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">,</span> overrideSubscribeListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//export invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> ExporterChangeableWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> exporter <span style="color:#719e07">=</span> doLocalExport<span style="color:#719e07">(</span>originInvoker<span style="color:#719e07">,</span> providerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// url to registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//通过URL获取 注册中心Registry操作对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//需要向注册中心注册地址转换 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=14656&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1653711086189 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> URL registeredProviderUrl <span style="color:#719e07">=</span> getUrlToRegistry<span style="color:#719e07">(</span>providerUrl<span style="color:#719e07">,</span> registryUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// decide if we need to delay publish (provider itself and registry should both need to register) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> register <span style="color:#719e07">=</span> providerUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> registryUrl<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REGISTER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否向注册中心注册 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>register<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> register<span style="color:#719e07">(</span>registry<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register stated url on provider model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerStatedUrl<span style="color:#719e07">(</span>registryUrl<span style="color:#719e07">,</span> registeredProviderUrl<span style="color:#719e07">,</span> register<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> exporter<span style="color:#719e07">.</span>setRegisterUrl<span style="color:#719e07">(</span>registeredProviderUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exporter<span style="color:#719e07">.</span>setSubscribeUrl<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>registry<span style="color:#719e07">.</span>isServiceDiscovery<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Deprecated! Subscribe to override rules in 2.6.x or before. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registry<span style="color:#719e07">.</span>subscribe<span style="color:#719e07">(</span>overrideSubscribeUrl<span style="color:#719e07">,</span> overrideSubscribeListener<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内置监听器通知 这个不是通知消费者的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> notifyExport<span style="color:#719e07">(</span>exporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//Ensure that a new exporter instance is returned every time export |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> DestroyableExporter<span style="color:#719e07">&lt;&gt;(</span>exporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="168-dolocalexport本地导出协议开启端口">16.8 doLocalExport本地导出协议开启端口</h2> |
| <p>前面已经看过了本地协议JVM协议的服务导出和注册中心配置的导出,这里可以直接看一些关键代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExporterChangeableWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">doLocalExport</span><span style="color:#719e07">(</span><span style="color:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> originInvoker<span style="color:#719e07">,</span> URL providerUrl<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> getCacheKey<span style="color:#719e07">(</span>originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>ExporterChangeableWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> bounds<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> s <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Invoker<span style="color:#719e07">&lt;?&gt;</span> invokerDelegate <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InvokerDelegate<span style="color:#719e07">&lt;&gt;(</span>originInvoker<span style="color:#719e07">,</span> providerUrl<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//代码中用的这个protoco对象是dubbo自动生成的适配器对象protocol$Adaptive 适配器对象会根据当前协议的参数来查询具体的协议扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ExporterChangeableWrapper<span style="color:#719e07">&lt;&gt;((</span>Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> protocol<span style="color:#719e07">.</span>export<span style="color:#719e07">(</span>invokerDelegate<span style="color:#719e07">),</span> originInvoker<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面这个protocol$Adaptive 协议的export导出方法与之前的一样也会经历下面几个过程,具体细节可以参考JVM协议的导出:</p> |
| <ul> |
| <li>ProtocolSerializationWrapper</li> |
| <li>ProtocolFilterWrapper</li> |
| <li>ProtocolListenerWrapper</li> |
| <li>QosProtocolWrapper</li> |
| <li>唯一不同的是我们这里对应的协议扩展类型为DubboProtocol、 |
| 接下来来看下DubboProtocol的导出服务export方法实现:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> Exporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">export</span><span style="color:#719e07">(</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> RpcException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务提供者的url参考例子dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=6043&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1654224285437 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> invoker<span style="color:#719e07">.</span>getUrl<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export service. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//生成服务的key参考:link.elastic.dubbo.entity.DemoService:20880 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> serviceKey<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建导出服务用的导出器DubboExporter |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DubboExporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> exporter <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DubboExporter<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>invoker<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> exporterMap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//export a stub service for dispatching event |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//stub配置校验 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Boolean isStubSupportEvent <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>STUB_EVENT_KEY<span style="color:#719e07">,</span> DEFAULT_STUB_EVENT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Boolean isCallbackservice <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>IS_CALLBACK_SERVICE<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStubSupportEvent <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isCallbackservice<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String stubServiceMethods <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>STUB_EVENT_METHODS_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>stubServiceMethods <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> stubServiceMethods<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isWarnEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;consumer [&#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>INTERFACE_KEY<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;], has set stubproxy support event ,but no stub methods founded.&#34;</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务开启服务端口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> openServer<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> optimizeSerialization<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> exporter<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="开启服务端口">开启服务端口</h3> |
| <p>这里就到了RPC协议的TCP通信模块了,对应DubboProtocol 的 openServer(url);方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">openServer</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// find server. 地址作为key这里是192.168.1.9:20880 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// client can export a service which only for server to invoke |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//默认提供者开启服务,消费者是不能开启服务的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> isServer <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>IS_SERVER_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isServer<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//协议服务器 下面一个双重校验锁检查,如果为空则创建服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ProtocolServer server <span style="color:#719e07">=</span> serverMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>server <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> server <span style="color:#719e07">=</span> serverMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>server <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serverMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> createServer<span style="color:#719e07">(</span>url<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span><span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> server<span style="color:#719e07">.</span>reset<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// server supports reset, use together with override |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> server<span style="color:#719e07">.</span>reset<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>为当前地址创建协议服务对应方法如下: |
| DubboProtocol的createServer方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> ProtocolServer <span style="color:#268bd2">createServer</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//下面将url增加了心跳参数最终如下dubbo://192.168.1.9:20880/link.elastic.dubbo.entity.DemoService?anyhost=true&amp;application=dubbo-demo-api-provider&amp;background=false&amp;bind.ip=192.168.1.9&amp;bind.port=20880&amp;channel.readonly.sent=true&amp;codec=dubbo&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;heartbeat=60000&amp;interface=link.elastic.dubbo.entity.DemoService&amp;methods=sayHello,sayHelloAsync&amp;pid=6700&amp;release=3.0.8&amp;service-name-mapping=true&amp;side=provider&amp;timestamp=1654225251112 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// send readonly event when server closes, it&#39;s enabled by default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>CHANNEL_READONLYEVENT_SENT_KEY<span style="color:#719e07">,</span> Boolean<span style="color:#719e07">.</span>TRUE<span style="color:#719e07">.</span>toString<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// enable heartbeat by default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>HEARTBEAT_KEY<span style="color:#719e07">,</span> String<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>DEFAULT_HEARTBEAT<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>addParameter<span style="color:#719e07">(</span>CODEC_KEY<span style="color:#719e07">,</span> DubboCodec<span style="color:#719e07">.</span>NAME<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里服务端使用的网络库这里是默认值netty |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String str <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>SERVER_KEY<span style="color:#719e07">,</span> DEFAULT_REMOTING_SERVER<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>str<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>url<span style="color:#719e07">.</span>getOrDefaultFrameworkModel<span style="color:#719e07">().</span>getExtensionLoader<span style="color:#719e07">(</span>Transporter<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>hasExtension<span style="color:#719e07">(</span>str<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unsupported server type: &#34;</span> <span style="color:#719e07">+</span> str <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, url: &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//dubbo交换器层对象创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExchangeServer server<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个方法会绑定端口,关于交换器与传输网络层到后面统一说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里通过绑定url和请求处理器来创建交换器对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> server <span style="color:#719e07">=</span> Exchangers<span style="color:#719e07">.</span>bind<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> requestHandler<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>RemotingException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Fail to start server(url: &#34;</span> <span style="color:#719e07">+</span> url <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> str <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CLIENT_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>str<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> supportedTypes <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getOrDefaultFrameworkModel<span style="color:#719e07">().</span>getExtensionLoader<span style="color:#719e07">(</span>Transporter<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>supportedTypes<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>str<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unsupported client type: &#34;</span> <span style="color:#719e07">+</span> str<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DubboProtocolServer protocolServer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DubboProtocolServer<span style="color:#719e07">(</span>server<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//关闭等待时长默认为10秒 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadServerProperties<span style="color:#719e07">(</span>protocolServer<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocolServer<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="169-向注册中心注册服务register">16.9 向注册中心注册服务register</h2> |
| <p>这个细节在下个博客中说涉及到Dubbo3的双注册</p> |
| <p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/16-mo-kuai-fa-bu-qi-fa-bu-fu-wu-quan-guo-cheng/">16-模块发布器发布服务全过程</a></p></description></item><item><title>Blog: 15-Dubbo的三大中心之元数据中心源码解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/15/15-dubbo%E7%9A%84%E4%B8%89%E5%A4%A7%E4%B8%AD%E5%BF%83%E4%B9%8B%E5%85%83%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</link><pubDate>Mon, 15 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/15/15-dubbo%E7%9A%84%E4%B8%89%E5%A4%A7%E4%B8%AD%E5%BF%83%E4%B9%8B%E5%85%83%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</guid><description> |
| <h1 id="15-dubbo的三大中心之元数据中心源码解析">15-Dubbo的三大中心之元数据中心源码解析</h1> |
| <h2 id="151-简介">15.1 简介</h2> |
| <p>关于元数据中心的概念对于大部分用户来说是比较陌生的,配置中心的话我们还好理解,对于元数据中心是什么,我们来看下我从官网拷贝过来的一段文字:</p> |
| <p>元数据中心在2.7.x版本开始支持,随着应用级别的服务注册和服务发现在Dubbo中落地,<strong>元数据中心也变的越来越重要</strong>。在以下几种情况下会需要部署元数据中心:</p> |
| <ul> |
| <li>对于一个原先采用老版本Dubbo搭建的应用服务,在迁移到Dubbo 3时,Dubbo 3 会需要一个<strong>元数据中心来维护RPC服务与应用的映射关系(即接口与应用的映射关系)</strong>,因为如果采用了<strong>应用级别的服务发现和服务注册</strong>,在注册中心中将<strong>采用“应用 —— 实例列表”结构</strong>的数据组织形式,<strong>不再是以往的“接口 —— 实例列表”结构的数据组织形式</strong>,而以往用接口级别的服务注册和服务发现的应用服务在<strong>迁移到应用级别</strong>时,<strong>得不到接口与应用之间的对应关系</strong>,从而无法从注册中心得到实例列表信息,所以<strong>Dubbo为了兼容这种场景,在Provider端启动时,会往元数据中心存储接口与应用的映射关系</strong>。</li> |
| <li>为了让<strong>注册中心更加聚焦与地址的发现和推送能力</strong>,<strong>减轻注册中心的负担</strong>,元数据中心承载了所有的服务元数据、大量接口/方法级别配置信息等,无论是接口粒度还是应用粒度的服务发现和注册,元数据中心都起到了重要的作用。</li> |
| <li></li> |
| </ul> |
| <p>如果有以上两种需求,都可以选择部署元数据中心,并通过Dubbo的配置来集成该元数据中心。</p> |
| <p><strong>元数据中心并不依赖于注册中心和配置中心</strong>,用户可以自由选择是否集成和部署元数据中心,如下图所示:</p> |
| <p><img src="https://dubbo.apache.org/imgs/v3/concepts/centers-metadata.png" alt="/imgs/v3/concepts/centers-metadata.png"></p> |
| <p>该图中不配备配置中心,意味着可以不需要全局管理配置的能力。该图中不配备注册中心,意味着可能采用了Dubbo mesh的方案,也可能不需要进行服务注册,仅仅接收直连模式的服务调用。 |
| 官网参考文章地址:</p> |
| <ul> |
| <li><a href="https://dubbo.apache.org/zh-cn/docs/concepts/registry-configcenter-metadata/">部署架构(注册中心 配置中心 元数据中心</a></li> |
| <li><a href="https://dubbo.apache.org/zh-cn/docs/references/metadata/">元数据参考手册</a></li> |
| </ul> |
| <p>综上所述可以用几句话概括下:</p> |
| <ul> |
| <li>元数据中心来维护RPC服务与应用的映射关系(即接口与应用的映射关系)来兼容接口与应用之间的对应关系</li> |
| <li>让注册中心更加聚焦与地址的发现和推送能力</li> |
| </ul> |
| <p>注册中心的启动是在DefaultApplicationDeployer中的初始化方法 initialize() 中:如下所示</p> |
| <p>这里只看下 startMetadataCenter();方法即可</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>startLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register shutdown hook |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerShutdownHook<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> startConfigCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> loadApplicationConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initModuleDeployers<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// @since 2.7.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startMetadataCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has been initialized!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="152-深入探究元数据中心的启动过程">15.2 深入探究元数据中心的启动过程</h2> |
| <h3 id="1521-启动元数据中心的代码全貌">15.2.1 启动元数据中心的代码全貌</h3> |
| <p>关于元数据中心我们看下 startMetadataCenter()方法来大致了解下整个流程</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startMetadataCenter</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> useRegistryAsMetadataCenterIfNecessary<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用的配置信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> getApplication<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据配置类型 元数据类型, local 或 remote,,如果选择远程,则需要进一步指定元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String metadataType <span style="color:#719e07">=</span> applicationConfig<span style="color:#719e07">.</span>getMetadataType<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// FIXME, multiple metadata config support. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//查询元数据中心的地址等配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataReportConfigs <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getMetadataConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>metadataReportConfigs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个就是判断 如果选择远程,则需要进一步指定元数据中心 否则就抛出来异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>REMOTE_METADATA_STORAGE_TYPE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>metadataType<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No MetadataConfig found, Metadata Center address is required when &#39;metadata=remote&#39; is enabled.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//MetadataReport实例的存储库对象获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReportInstance metadataReportInstance <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>getBean<span style="color:#719e07">(</span>MetadataReportInstance<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> validMetadataReportConfigs <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>metadataReportConfigs<span style="color:#719e07">.</span>size<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetadataReportConfig metadataReportConfig <span style="color:#719e07">:</span> metadataReportConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ConfigValidationUtils<span style="color:#719e07">.</span>validateMetadataConfig<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> validMetadataReportConfigs<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReportInstance<span style="color:#719e07">.</span>init<span style="color:#719e07">(</span>validMetadataReportConfigs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//MetadataReport实例的存储库对象初始化失败则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>metadataReportInstance<span style="color:#719e07">.</span>inited<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;%s MetadataConfigs found, but none of them is valid.&#34;</span><span style="color:#719e07">,</span> metadataReportConfigs<span style="color:#719e07">.</span>size<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1522-元数据中心未配置则使用注册中心配置">15.2.2 元数据中心未配置则使用注册中心配置</h3> |
| <p>前面在说配置中心的时候有说过配置中心如果未配置会使用注册中心的地址等信息作为默认配置,这里元数据做了类似的操作:如代码: |
| DefaultApplicationDeployer类型的 useRegistryAsMetadataCenterIfNecessary()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">useRegistryAsMetadataCenterIfNecessary</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置缓存中查询元数据配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataConfigs <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getMetadataConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置存在则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>metadataConfigs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">////查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> defaultRegistries <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getDefaultRegistries<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultRegistries<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多注册中心遍历 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultRegistries |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>stream<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>filter<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>isUsedRegistryAsMetadataCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心配置映射为元数据中心 映射就是获取需要的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>registryAsMetadataCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将元数据中心配置存储在配置缓存中方便后续使用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>metadataReportConfig <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">.</span>getId<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataReportConfigs <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getMetadataConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>metadataReportConfigs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetadataReportConfig existedConfig <span style="color:#719e07">:</span> metadataReportConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>existedConfig<span style="color:#719e07">.</span>getId<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> existedConfig<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">().</span>equals<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> configManager<span style="color:#719e07">.</span>addMetadataReport<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Optional<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> configOptional <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getConfig<span style="color:#719e07">(</span>MetadataReportConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> metadataReportConfig<span style="color:#719e07">.</span>getId<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>configOptional<span style="color:#719e07">.</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> configManager<span style="color:#719e07">.</span>addMetadataReport<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;use registry as metadata-center: &#34;</span> <span style="color:#719e07">+</span> metadataReportConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个代码有些细节就不细说了 我们概括下顺序梳理下思路:</p> |
| <ul> |
| <li>配置缓存中查询元数据配置,配置存在则直接返回</li> |
| <li>查询所有可用的默认注册中心列表 |
| <ul> |
| <li>多注册中心遍历</li> |
| <li>选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)</li> |
| <li>注册中心配置RegistryConfig映射转换为元数据中心配置类型MetadataReportConfig 映射就是获取需要的配置</li> |
| <li>将元数据中心配置存储在配置缓存中方便后续使用</li> |
| </ul> |
| </li> |
| </ul> |
| <p>元数据的配置可以参考官网:<a href="https://dubbo.apache.org/zh-cn/docs/references/metadata/">元数据参考手册</a></p> |
| <p>这里主要看下可配置项有哪些 对应类型为MetadataReportConfig 在官网暂时未找到合适的文档,这里整理下属性列表方便后续配置说明查看:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>配置变量</th> |
| <th>类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>id</td> |
| <td>String</td> |
| <td>配置id</td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td>String</td> |
| <td>元数据协议</td> |
| </tr> |
| <tr> |
| <td>address</td> |
| <td>String</td> |
| <td>元数据中心地址</td> |
| </tr> |
| <tr> |
| <td>port</td> |
| <td>Integer</td> |
| <td>元数据中心端口</td> |
| </tr> |
| <tr> |
| <td>username</td> |
| <td>String</td> |
| <td>元数据中心认证用户名</td> |
| </tr> |
| <tr> |
| <td>password</td> |
| <td>String</td> |
| <td>元数据中心认证密码</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>Integer</td> |
| <td>元数据中心的请求超时(毫秒)</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>String</td> |
| <td>该组将元数据保存在中。它与注册表相同</td> |
| </tr> |
| <tr> |
| <td>parameters</td> |
| <td>Map&lt;String, String&gt;</td> |
| <td>自定义参数</td> |
| </tr> |
| <tr> |
| <td>retryTimes</td> |
| <td>Integer</td> |
| <td>重试次数</td> |
| </tr> |
| <tr> |
| <td>retryPeriod</td> |
| <td>Integer</td> |
| <td>重试间隔</td> |
| </tr> |
| <tr> |
| <td>cycleReport</td> |
| <td>Boolean</td> |
| <td>默认情况下, 是否每天重复存储完整的元数据</td> |
| </tr> |
| <tr> |
| <td>syncReport</td> |
| <td>Boolean</td> |
| <td>Sync or Async report.</td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>Boolean</td> |
| <td>需要群集支持,默认为false</td> |
| </tr> |
| <tr> |
| <td>registry</td> |
| <td>String</td> |
| <td>注册表配置id</td> |
| </tr> |
| <tr> |
| <td>file</td> |
| <td>String</td> |
| <td>元数据报告文件存储位置</td> |
| </tr> |
| <tr> |
| <td>check</td> |
| <td>Boolean</td> |
| <td>连接到元数据中心时要应用的失败策略</td> |
| </tr> |
| </tbody> |
| </table> |
| <h3 id="1523-元数据中心的初始化逻辑">15.2.3 元数据中心的初始化逻辑</h3> |
| <h4 id="15231-元数据中心的初始化调用逻辑">15.2.3.1 元数据中心的初始化调用逻辑</h4> |
| <p>主要看这一行比较重要的逻辑:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//初始化元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReportInstance<span style="color:#719e07">.</span>init<span style="color:#719e07">(</span>validMetadataReportConfigs<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>在了解这一行逻辑之前我们先来看下元数据相关联的类型:</p> |
| <p>MetadataReportInstance中的初始化方法init</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">(</span>List<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataReportConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//CAS判断是否有初始化过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>init<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据类型配置如果未配置则默认为local |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataType <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">().</span>getApplicationOrElseThrow<span style="color:#719e07">().</span>getMetadataType<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataType <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataType <span style="color:#719e07">=</span> DEFAULT_METADATA_STORAGE_TYPE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取MetadataReportFactory 工厂类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReportFactory metadataReportFactory <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>MetadataReportFactory<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多元数据中心初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>MetadataReportConfig metadataReportConfig <span style="color:#719e07">:</span> metadataReportConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> init<span style="color:#719e07">(</span>metadataReportConfig<span style="color:#719e07">,</span> metadataReportFactory<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span><span style="color:#719e07">(</span>MetadataReportConfig config<span style="color:#719e07">,</span> MetadataReportFactory metadataReportFactory<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置转url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>toUrl<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>METADATA_REPORT_KEY<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String protocol <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>METADATA_REPORT_KEY<span style="color:#719e07">,</span> DEFAULT_DIRECTORY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> URLBuilder<span style="color:#719e07">.</span>from<span style="color:#719e07">(</span>url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>protocol<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameter<span style="color:#719e07">(</span>METADATA_REPORT_KEY<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>build<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>addParameterIfAbsent<span style="color:#719e07">(</span>APPLICATION_KEY<span style="color:#719e07">,</span> applicationModel<span style="color:#719e07">.</span>getCurrentConfig<span style="color:#719e07">().</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> String relatedRegistryId <span style="color:#719e07">=</span> isEmpty<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">())</span> <span style="color:#719e07">?</span> <span style="color:#719e07">(</span>isEmpty<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getId<span style="color:#719e07">())</span> <span style="color:#719e07">?</span> DEFAULT_KEY <span style="color:#719e07">:</span> config<span style="color:#719e07">.</span>getId<span style="color:#719e07">())</span> <span style="color:#719e07">:</span> config<span style="color:#719e07">.</span>getRegistry<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从元数据工厂中获取元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReport metadataReport <span style="color:#719e07">=</span> metadataReportFactory<span style="color:#719e07">.</span>getMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存元数据到内存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> metadataReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>relatedRegistryId<span style="color:#719e07">,</span> metadataReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>关于元数据的初始化我们主要看两个位置:</p> |
| <ul> |
| <li>一个是元数据工厂对象的创建与初始化MetadataReportFactory</li> |
| <li>一个是元数据对象的创建与初始化MetadataReport</li> |
| </ul> |
| <h4 id="15232-元数据工厂对象metadatareportfactory">15.2.3.2 元数据工厂对象MetadataReportFactory</h4> |
| <p>关于元数据工厂类型MetadataReportFactory,元数据工厂 用于<strong>创建与管理元数据对象</strong>, 相关类型如下: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/15-config.png" alt="在这里插入图片描述"></p> |
| <p>我们这里主要以为Zookeeper扩展的元数据工厂ZookeeperMetadataReportFactory类型为例子: |
| 实现类型逻辑不复杂,这里就直接贴代码看看:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ZookeeperMetadataReportFactory</span> <span style="color:#268bd2">extends</span> AbstractMetadataReportFactory <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//与Zookeeper交互的传输器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> ZookeeperTransporter zookeeperTransporter<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用程序模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> ApplicationModel applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ZookeeperMetadataReportFactory</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>zookeeperTransporter <span style="color:#719e07">=</span> ZookeeperTransporter<span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@DisableInject</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setZookeeperTransporter</span><span style="color:#719e07">(</span>ZookeeperTransporter zookeeperTransporter<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>zookeeperTransporter <span style="color:#719e07">=</span> zookeeperTransporter<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetadataReport <span style="color:#268bd2">createMetadataReport</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ZookeeperMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> zookeeperTransporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>元数据工厂的实现比较简单</p> |
| <ul> |
| <li>继承抽象的元数据工厂AbstractMetadataReportFactory</li> |
| <li>实现工厂方法createMetadataReport来创建一个元数据操作类型</li> |
| </ul> |
| <p>如果我们想要实现一个元数据工厂扩展可以参考Zookeeper的这个方式</p> |
| <h4 id="15233-元数据操作对象metadatareport的创建与初始化">15.2.3.3 元数据操作对象MetadataReport的创建与初始化</h4> |
| <p>前面的从元数据工厂中获取元数据操作对象的逻辑处理代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//从元数据工厂中获取元数据 ,url对象可以理解为配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReport metadataReport <span style="color:#719e07">=</span> metadataReportFactory<span style="color:#719e07">.</span>getMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>关于元数据对象,用于元数据信息的增删改查等逻辑的操作与元数据信息的缓存</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/15-config2.png" alt="在这里插入图片描述"></p> |
| <p>我们这里还是以Zookeeper的实现ZookeeperMetadataReportFactory类型做为参考:</p> |
| <p>我们先来看这个逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//从元数据工厂中获取元数据 ,url对象可以理解为配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReport metadataReport <span style="color:#719e07">=</span> metadataReportFactory<span style="color:#719e07">.</span>getMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>ZookeeperMetadataReportFactory的父类型AbstractMetadataReportFactory中的getMetadataReport方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetadataReport <span style="color:#268bd2">getMetadataReport</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//url值参考例子zookeeper://127.0.0.1:2181?application=dubbo-demo-api-provider&amp;client=&amp;port=2181&amp;protocol=zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果存在export则移除 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> url <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>setPath<span style="color:#719e07">(</span>MetadataReport<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getName<span style="color:#719e07">())</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>removeParameters<span style="color:#719e07">(</span>EXPORT_KEY<span style="color:#719e07">,</span> REFER_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成元数据缓存key 元数据维度 地址+名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如: zookeeper://127.0.0.1:2181/org.apache.dubbo.metadata.report.MetadataReport |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>toServiceString<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中查询 查到则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> MetadataReport metadataReport <span style="color:#719e07">=</span> serviceStoreMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> metadataReport<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Lock the metadata access process to ensure a single instance of the metadata instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//存在写操作 加个锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> lock<span style="color:#719e07">.</span>lock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁在查一下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReport <span style="color:#719e07">=</span> serviceStoreMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> metadataReport<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check参数 查元数据报错是否抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> check <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CHECK_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> url<span style="color:#719e07">.</span>getPort<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> 0<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//关键模版方法 调用扩展实现的具体业务(创建元数据操作对象) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReport <span style="color:#719e07">=</span> createMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>check<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The metadata reporter failed to initialize&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check逻辑检查 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>check <span style="color:#719e07">&amp;&amp;</span> metadataReport <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can not create metadata Report &#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> serviceStoreMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> metadataReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> metadataReport<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Release the lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> lock<span style="color:#719e07">.</span>unlock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面这个抽象类AbstractMetadataReportFactory中的获取元数据操作对象的模版方法getMetadataReport(URL url), 用了双重校验锁的逻辑来创建对象缓存对象,又用了模版方法设计模式,来让抽象类做通用的逻辑,让实现类型去做扩展, 虽然代码写的太长了些整体还是用了不少的设计思想.</p> |
| <p>我们直接看这个代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>metadataReport <span style="color:#719e07">=</span> createMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这个创建元数据操作对象的代码实际上走的是实现类型的逻辑:</p> |
| <p>来自工厂Bean ZookeeperMetadataReportFactory的工厂方法如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetadataReport <span style="color:#268bd2">createMetadataReport</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ZookeeperMetadataReport<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> zookeeperTransporter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>创建了元数据操作对象,这里我们继续看下元数据操作对象ZookeeperMetadataReport创建做了哪些逻辑: |
| 来自ZookeeperMetadataReport的构造器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ZookeeperMetadataReport</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> ZookeeperTransporter zookeeperTransporter<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//url即配置 配置传递给抽象类 做一些公共的逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//url参考:zookeeper://127.0.0.1:2181/org.apache.dubbo.metadata.report.MetadataReport?application=dubbo-demo-api-provider&amp;client=&amp;port=2181&amp;protocol=zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>url<span style="color:#719e07">.</span>isAnyHost<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;registry address == null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> String group <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">(</span>DEFAULT_ROOT<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>group<span style="color:#719e07">.</span>startsWith<span style="color:#719e07">(</span>PATH_SEPARATOR<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> group <span style="color:#719e07">=</span> PATH_SEPARATOR <span style="color:#719e07">+</span> group<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>root <span style="color:#719e07">=</span> group<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//连接Zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> zkClient <span style="color:#719e07">=</span> zookeeperTransporter<span style="color:#719e07">.</span>connect<span style="color:#719e07">(</span>url<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>核心的公共的操作逻辑封装在父类AbstractMetadataReport里面 |
| 我们来看前面super调用的构造器逻辑: |
| 如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractMetadataReport</span><span style="color:#719e07">(</span>URL reportServerURL<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置url 如:zookeeper://127.0.0.1:2181/org.apache.dubbo.metadata.report.MetadataReport?application=dubbo-demo-api-provider&amp;client=&amp;port=2181&amp;protocol=zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setUrl<span style="color:#719e07">(</span>reportServerURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Start file save timer |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//缓存的文件名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//格式为: 用户目录+/.dubbo/dubbo-metadata- + 应用程序名字application + url地址(IP+端口) + 后缀.cache 如下所示 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">///Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String defaultFilename <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>USER_HOME<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> DUBBO_METADATA <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> reportServerURL<span style="color:#719e07">.</span>getApplication<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> replace<span style="color:#719e07">(</span>reportServerURL<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;:&#34;</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;-&#34;</span><span style="color:#719e07">)</span> <span style="color:#719e07">+</span> CACHE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果用户配置了缓存文件名字则以用户配置为准file |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String filename <span style="color:#719e07">=</span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>FILE_KEY<span style="color:#719e07">,</span> defaultFilename<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> File file <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件名字不为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ConfigUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>filename<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> file <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File<span style="color:#719e07">(</span>filename<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件和父目录不存在则创建文件目录 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>exists<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">().</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">().</span>mkdirs<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Invalid service store file &#34;</span> <span style="color:#719e07">+</span> file <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: Failed to create directory &#34;</span> <span style="color:#719e07">+</span> file<span style="color:#719e07">.</span>getParentFile<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if this file exists, firstly delete it. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//还未初始化则已存在的历史文件删除掉 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>initialized<span style="color:#719e07">.</span>getAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">true</span><span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> file<span style="color:#719e07">.</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> file<span style="color:#719e07">.</span>delete<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//赋值给成员变量后续继续可以用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>file <span style="color:#719e07">=</span> file<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件存在则直接加载文件中的内容 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadProperties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//sync-report配置的值为同步配置还异步配置,true是同步配置,默认为false为异步配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> syncReport <span style="color:#719e07">=</span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>SYNC_REPORT_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重试属性与逻辑也封装了一个类型 创建对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//retry-times重试次数配置 默认为100次 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//retry-period 重试间隔配置 默认为3000 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> metadataReportRetry <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetadataReportRetry<span style="color:#719e07">(</span>reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>RETRY_TIMES_KEY<span style="color:#719e07">,</span> DEFAULT_METADATA_REPORT_RETRY_TIMES<span style="color:#719e07">),</span> |
| </span></span><span style="display:flex;"><span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>RETRY_PERIOD_KEY<span style="color:#719e07">,</span> DEFAULT_METADATA_REPORT_RETRY_PERIOD<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// cycle report the data switch |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//是否定期从元数据中心同步配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//cycle-report配置默认为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>CYCLE_REPORT_KEY<span style="color:#719e07">,</span> DEFAULT_METADATA_REPORT_CYCLE_REPORT<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//开启重试定时器 24个小时间隔从元数据中心同步一次 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> reportTimerScheduler <span style="color:#719e07">=</span> Executors<span style="color:#719e07">.</span>newSingleThreadScheduledExecutor<span style="color:#719e07">(</span><span style="color:#719e07">new</span> NamedThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;DubboMetadataReportTimer&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> reportTimerScheduler<span style="color:#719e07">.</span>scheduleAtFixedRate<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>publishAll<span style="color:#719e07">,</span> calculateStartTime<span style="color:#719e07">(),</span> ONE_DAY_IN_MILLISECONDS<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>reportMetadata <span style="color:#719e07">=</span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REPORT_METADATA_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>reportDefinition <span style="color:#719e07">=</span> reportServerURL<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>REPORT_DEFINITION_KEY<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="15234-内存中元数据自动同步到zookeeper和本地文件">15.2.3.4 内存中元数据自动同步到Zookeeper和本地文件</h4> |
| <p>这里来总结下元数据操作的初始化逻辑:</p> |
| <ul> |
| <li>首次初始化清理历史元数据文件如: |
| Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache</li> |
| <li>如果非首次进来则直接加载缓存在本地的缓存文件,赋值给properties成员变量</li> |
| <li>初始化同步配置是否异步(默认为false), sync-report配置的值为同步配置还异步配置,true是同步配置,默认为false为异步配置</li> |
| <li>初始化重试属性</li> |
| <li>是否定期从元数据中心同步配置初始化 默认为true 24小时自动同步一次</li> |
| </ul> |
| <p>关于元数据同步可以看AbstractMetadataReport类型的publishAll方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> reportTimerScheduler <span style="color:#719e07">=</span> Executors<span style="color:#719e07">.</span>newSingleThreadScheduledExecutor<span style="color:#719e07">(</span><span style="color:#719e07">new</span> NamedThreadFactory<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;DubboMetadataReportTimer&#34;</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> reportTimerScheduler<span style="color:#719e07">.</span>scheduleAtFixedRate<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>publishAll<span style="color:#719e07">,</span> calculateStartTime<span style="color:#719e07">(),</span> ONE_DAY_IN_MILLISECONDS<span style="color:#719e07">,</span> TimeUnit<span style="color:#719e07">.</span>MILLISECONDS<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这里有个方法叫做calculateStartTime 这个代码是随机时间的between 2:00 am to 6:00 am, the time is random. 2点到6点之间启动, 低峰期启动自动同步 |
| 返回值:</p> |
| <p>AbstractMetadataReport类型的</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishAll</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;start to publish all metadata.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>doHandleMetadataCollection<span style="color:#719e07">(</span>allMetadataReports<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractMetadataReport类型的doHandleMetadataCollection</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">doHandleMetadataCollection</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>MetadataIdentifier<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> metadataMap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>metadataMap<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Iterator<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>MetadataIdentifier<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;&gt;</span> iterable <span style="color:#719e07">=</span> metadataMap<span style="color:#719e07">.</span>entrySet<span style="color:#719e07">().</span>iterator<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>iterable<span style="color:#719e07">.</span>hasNext<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>MetadataIdentifier<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> item <span style="color:#719e07">=</span> iterable<span style="color:#719e07">.</span>next<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>PROVIDER_SIDE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>item<span style="color:#719e07">.</span>getKey<span style="color:#719e07">().</span>getSide<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供端的元数据则存储提供端元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>storeProviderMetadata<span style="color:#719e07">(</span>item<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> <span style="color:#719e07">(</span>FullServiceDefinition<span style="color:#719e07">)</span> item<span style="color:#719e07">.</span>getValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CONSUMER_SIDE<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>item<span style="color:#719e07">.</span>getKey<span style="color:#719e07">().</span>getSide<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//消费端的元数据则存储提供端元数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>storeConsumerMetadata<span style="color:#719e07">(</span>item<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">)</span> item<span style="color:#719e07">.</span>getValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>提供端元数据的存储: |
| AbstractMetadataReport类型的storeProviderMetadata</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>syncReport<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> storeProviderMetadataTask<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportCacheExecutor<span style="color:#719e07">.</span>execute<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> storeProviderMetadataTask<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>AbstractMetadataReport类型的storeProviderMetadataTask |
| 具体同步代码:storeProviderMetadataTask</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadataTask</span><span style="color:#719e07">(</span>MetadataIdentifier providerMetadataIdentifier<span style="color:#719e07">,</span> ServiceDefinition serviceDefinition<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;store provider metadata. Identifier : &#34;</span> <span style="color:#719e07">+</span> providerMetadataIdentifier <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;; definition: &#34;</span> <span style="color:#719e07">+</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> allMetadataReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> failedReports<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Gson gson <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Gson<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> String data <span style="color:#719e07">=</span> gson<span style="color:#719e07">.</span>toJson<span style="color:#719e07">(</span>serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内存中的元数据同步到元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doStoreProviderMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内存中的元数据同步到本地文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> saveProperties<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> <span style="color:#719e07">!</span>syncReport<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// retry again. If failed again, throw exception. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> failedReports<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> serviceDefinition<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> metadataReportRetry<span style="color:#719e07">.</span>startRetryTask<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to put provider metadata &#34;</span> <span style="color:#719e07">+</span> providerMetadataIdentifier <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in &#34;</span> <span style="color:#719e07">+</span> serviceDefinition <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面代码我们主要看本地内存中的元数据同步到元数据中心和存本地的两个点:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//内存中的元数据同步到元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>doStoreProviderMetadata<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//内存中的元数据同步到本地文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>saveProperties<span style="color:#719e07">(</span>providerMetadataIdentifier<span style="color:#719e07">,</span> data<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> |
| </span></span></code></pre></div><p>//内存中的元数据同步到元数据中心</p> |
| <p>这个方法会调用当前子类重写的具体存储逻辑:这里我们以 |
| ZookeeperMetadataReport的doStoreProviderMetadata举例:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeMetadata</span><span style="color:#719e07">(</span>MetadataIdentifier metadataIdentifier<span style="color:#719e07">,</span> String v<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用zkClient创建一个节点数据为参数V v是前面说的服务定义数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> zkClient<span style="color:#719e07">.</span>create<span style="color:#719e07">(</span>getNodePath<span style="color:#719e07">(</span>metadataIdentifier<span style="color:#719e07">),</span> v<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里参数我们举个例子: 提供者的元数据内容如下: |
| 节点路径为:</p> |
| <ul> |
| <li>/dubbo/metadata/link.elastic.dubbo.entity.DemoService/provider/dubbo-demo-api-provider</li> |
| </ul> |
| <p>格式:</p> |
| <ul> |
| <li>/dubbo/metadata前缀</li> |
| <li>服务提供者接口</li> |
| <li>提供者类型provider</li> |
| <li>应用名</li> |
| </ul> |
| <p>具体的元数据内容如下: |
| 比较详细的记录了应用信息,服务接口信息和服务接口对应的方法信息</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameters&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;side&#34;</span>: <span style="color:#2aa198">&#34;provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;interface&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;pid&#34;</span>: <span style="color:#2aa198">&#34;38680&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;application&#34;</span>: <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo&#34;</span>: <span style="color:#2aa198">&#34;2.0.2&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;release&#34;</span>: <span style="color:#2aa198">&#34;3.0.8&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;anyhost&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.ip&#34;</span>: <span style="color:#2aa198">&#34;192.168.1.9&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: <span style="color:#2aa198">&#34;sayHello,sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;background&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;deprecated&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dynamic&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;service-name-mapping&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;generic&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.port&#34;</span>: <span style="color:#2aa198">&#34;20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;timestamp&#34;</span>: <span style="color:#2aa198">&#34;1653097653865&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;canonicalName&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;codeSource&#34;</span>: <span style="color:#2aa198">&#34;file:/Users/song/Desktop/Computer/A/code/gitee/weaving-a-net/weaving-test/dubbo-test/target/classes/&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHello&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;types&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;result&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;stack&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;next&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;status&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>本地缓存文件的写入 可以看下如下代码 |
| AbstractMetadataReport类型的saveProperties方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">saveProperties</span><span style="color:#719e07">(</span>MetadataIdentifier metadataIdentifier<span style="color:#719e07">,</span> String value<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> add<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> sync<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>file <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>add<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>setProperty<span style="color:#719e07">(</span>metadataIdentifier<span style="color:#719e07">.</span>getUniqueKey<span style="color:#719e07">(</span>KeyTypeEnum<span style="color:#719e07">.</span>UNIQUE_KEY<span style="color:#719e07">),</span> value<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>metadataIdentifier<span style="color:#719e07">.</span>getUniqueKey<span style="color:#719e07">(</span>KeyTypeEnum<span style="color:#719e07">.</span>UNIQUE_KEY<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">long</span> version <span style="color:#719e07">=</span> lastCacheChanged<span style="color:#719e07">.</span>incrementAndGet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>sync<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取最新修改版本持久化到磁盘 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">new</span> SaveProperties<span style="color:#719e07">(</span>version<span style="color:#719e07">).</span>run<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportCacheExecutor<span style="color:#719e07">.</span>execute<span style="color:#719e07">(</span><span style="color:#719e07">new</span> SaveProperties<span style="color:#719e07">(</span>version<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>主要看如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#719e07">new</span> SaveProperties<span style="color:#719e07">(</span>version<span style="color:#719e07">).</span>run<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>SaveProperties类型代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">SaveProperties</span> <span style="color:#268bd2">implements</span> Runnable <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">long</span> version<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">SaveProperties</span><span style="color:#719e07">(</span><span style="color:#dc322f">long</span> version<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>version <span style="color:#719e07">=</span> version<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">run</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> doSaveProperties<span style="color:#719e07">(</span>version<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看doSaveProperties方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doSaveProperties</span><span style="color:#719e07">(</span><span style="color:#dc322f">long</span> version<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不是最新的就不要持久化了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>version <span style="color:#719e07">&lt;</span> lastCacheChanged<span style="color:#719e07">.</span>get<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>file <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Save |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建本地文件锁: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//路径为: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">///Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache.lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> File lockfile <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File<span style="color:#719e07">(</span>file<span style="color:#719e07">.</span>getAbsolutePath<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.lock&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//锁文件不存在则创建锁文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>lockfile<span style="color:#719e07">.</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> lockfile<span style="color:#719e07">.</span>createNewFile<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//随机访问文件工具类对象创建 读写权限 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>RandomAccessFile raf <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> RandomAccessFile<span style="color:#719e07">(</span>lockfile<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;rw&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件文件Channel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//返回与此文件关联的唯一FileChannel对象。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FileChannel channel <span style="color:#719e07">=</span> raf<span style="color:#719e07">.</span>getChannel<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//FileChannel中的lock()与tryLock()方法都是尝试去获取在某一文件上的独有锁(以下简称独有锁),可以实现进程间操作的互斥。区别在于lock()会阻塞(blocking)方法的执行,tryLock()则不会。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FileLock lock <span style="color:#719e07">=</span> channel<span style="color:#719e07">.</span>tryLock<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果多个线程同时进来未获取锁的则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>lock <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IOException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can not lock the metadataReport cache file &#34;</span> <span style="color:#719e07">+</span> file<span style="color:#719e07">.</span>getAbsolutePath<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, ignore and retry later, maybe multi java process use the file, please config: dubbo.metadata.file=xxx.properties&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Save |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件不存在则创建本地元数据缓存文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">///Users/song/.dubbo/dubbo-metadata-dubbo-demo-api-provider-127.0.0.1-2181.cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>file<span style="color:#719e07">.</span>exists<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> file<span style="color:#719e07">.</span>createNewFile<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Properties tmpProperties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>syncReport<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// When syncReport = false, properties.setProperty and properties.store are called from the same |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// thread(reportCacheExecutor), so deep copy is not required |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> tmpProperties <span style="color:#719e07">=</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Using store method and setProperty method of the this.properties will cause lock contention |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// under multi-threading, so deep copy a new container |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//异步存储会导致锁争用 使用此的store方法和setProperty方法。属性将导致多线程下的锁争用,因此深度复制新容器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> tmpProperties <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>Object<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;&gt;</span> entries <span style="color:#719e07">=</span> properties<span style="color:#719e07">.</span>entrySet<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>Object<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> entries<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> tmpProperties<span style="color:#719e07">.</span>setProperty<span style="color:#719e07">((</span>String<span style="color:#719e07">)</span> entry<span style="color:#719e07">.</span>getKey<span style="color:#719e07">(),</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>FileOutputStream outputFile <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FileOutputStream<span style="color:#719e07">(</span>file<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//Properties类型自带的方法: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将此属性表中的属性列表(键和元素对)以适合使用load(Reader)方法的格式写入输出字符流。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> tmpProperties<span style="color:#719e07">.</span>store<span style="color:#719e07">(</span>outputFile<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;Dubbo metadataReport Cache&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> lock<span style="color:#719e07">.</span>release<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>version <span style="color:#719e07">&lt;</span> lastCacheChanged<span style="color:#719e07">.</span>get<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> reportCacheExecutor<span style="color:#719e07">.</span>execute<span style="color:#719e07">(</span><span style="color:#719e07">new</span> SaveProperties<span style="color:#719e07">(</span>lastCacheChanged<span style="color:#719e07">.</span>incrementAndGet<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个代码太诡异了如果是lock失败也会打印异常给人非常疑惑的感觉 后续会修复 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to save service store file, cause: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>写入文件的内容大致如下</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>link.elastic.dubbo.entity.DemoService:::provider:dubbo-demo-api-provider -&gt; { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameters&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;side&#34;</span>: <span style="color:#2aa198">&#34;provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;interface&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;pid&#34;</span>: <span style="color:#2aa198">&#34;41457&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;application&#34;</span>: <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo&#34;</span>: <span style="color:#2aa198">&#34;2.0.2&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;release&#34;</span>: <span style="color:#2aa198">&#34;3.0.8&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;anyhost&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.ip&#34;</span>: <span style="color:#2aa198">&#34;192.168.1.9&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: <span style="color:#2aa198">&#34;sayHello,sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;background&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;deprecated&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dynamic&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;service-name-mapping&#34;</span>: <span style="color:#2aa198">&#34;true&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;generic&#34;</span>: <span style="color:#2aa198">&#34;false&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;bind.port&#34;</span>: <span style="color:#2aa198">&#34;20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;timestamp&#34;</span>: <span style="color:#2aa198">&#34;1653100253548&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;canonicalName&#34;</span>: <span style="color:#2aa198">&#34;link.elastic.dubbo.entity.DemoService&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;codeSource&#34;</span>: <span style="color:#2aa198">&#34;file:/Users/song/Desktop/Computer/A/code/gitee/weaving-a-net/weaving-test/dubbo-test/target/classes/&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;methods&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHelloAsync&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;sayHello&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;parameterTypes&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;returnType&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;types&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;result&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;stack&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.Object&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.lang.String&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;properties&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;next&#34;</span>: <span style="color:#2aa198">&#34;java.util.concurrent.CompletableFuture.Completion&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;status&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;type&#34;</span>: <span style="color:#2aa198">&#34;int&#34;</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ], |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;annotations&#34;</span>: [ |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> ] |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/15-dubbo-de-san-da-zhong-xin-zhi-yuan-shu-ju-zhong-xin-yuan-ma-jie-xi/">15-Dubbo的三大中心之元数据中心源码解析</a></p></description></item><item><title>Blog: 14-Dubbo配置加载全解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/14/14-dubbo%E9%85%8D%E7%BD%AE%E5%8A%A0%E8%BD%BD%E5%85%A8%E8%A7%A3%E6%9E%90/</link><pubDate>Sun, 14 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/14/14-dubbo%E9%85%8D%E7%BD%AE%E5%8A%A0%E8%BD%BD%E5%85%A8%E8%A7%A3%E6%9E%90/</guid><description> |
| <h1 id="14-dubbo配置加载全解析">14-Dubbo配置加载全解析</h1> |
| <h2 id="141-回到启动器的初始化过程">14.1 回到启动器的初始化过程</h2> |
| <p>在应用程序启动的时候会调用发布器的启动方法 ,然后调用初始化方法,在发布器DefaultApplicationDeployer中的初始化方法initialize() 如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>startLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register shutdown hook |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerShutdownHook<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> startConfigCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> loadApplicationConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initModuleDeployers<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// @since 2.7.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startMetadataCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has been initialized!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>初始化过程中会先启动配置中心配置信息处理,然后 调用加载初始化应用程序配置方法loadApplicationConfigs();进行配置加载 |
| 关于配置的官方文档链接为 <a href="https://dubbo.apache.org/zh-cn/docs/references/configuration/overview/">配置概述</a></p> |
| <p>Dubbo框架的配置项比较繁多,为了更好地管理各种配置,将其按照用途划分为不同的组件,最终所有配置项都会汇聚到URL中,传递给后续处理模块。</p> |
| <p><strong>常用配置组件如下</strong>:</p> |
| <ul> |
| <li>application: Dubbo应用配置</li> |
| <li>registry: 注册中心</li> |
| <li>protocol: 服务提供者RPC协议</li> |
| <li>config-center: 配置中心</li> |
| <li>metadata-report: 元数据中心</li> |
| <li>service: 服务提供者配置</li> |
| <li>reference: 远程服务引用配置</li> |
| <li>provider: service的默认配置或分组配置</li> |
| <li>consumer: reference的默认配置或分组配置</li> |
| <li>module: 模块配置</li> |
| <li>monitor: 监控配置</li> |
| <li>metrics: 指标配置</li> |
| <li>ssl: SSL/TLS配置</li> |
| </ul> |
| <p>配置还有几个比较重要的点:</p> |
| <p><strong>配置来源</strong> |
| 从Dubbo支持的配置来源说起,默认有6种配置来源:</p> |
| <ul> |
| <li>JVM System Properties,JVM -D 参数</li> |
| <li>System environment,JVM进程的环境变量</li> |
| <li>Externalized Configuration,外部化配置,从配置中心读取</li> |
| <li>Application Configuration,应用的属性配置,从Spring应用的Environment中提取&quot;dubbo&quot;打头的属性集</li> |
| <li>API / XML /注解等编程接口采集的配置可以被理解成配置来源的一种,是直接面向用户编程的配置采集方式</li> |
| <li>从classpath读取配置文件 dubbo.properties</li> |
| </ul> |
| <p><strong>覆盖关系</strong> |
| 下图展示了配置覆盖关系的优先级,从上到下优先级依次降低: <img src="https://dubbo.apache.org/imgs/blog/configuration.jpg" alt="在这里插入图片描述"></p> |
| <p><strong>配置方式</strong></p> |
| <ul> |
| <li>Java API配置</li> |
| <li>XML配置</li> |
| <li>Annotation配置</li> |
| <li>属性配置</li> |
| </ul> |
| <p>配置虽然非常多,但是我们掌握一下配置加载的原理,再了解下官网的文档说明路径应该基础的配置搞定是没问题的,更深入的配置很多参数还是需要了解下源码的.</p> |
| <h2 id="142-配置信息的初始化回顾">14.2 配置信息的初始化回顾</h2> |
| <p>前面我们在讲ModuleModel对象的创建的时候ModuleModel模型中包含了一个成员变量为ModuleEnvironment 代表当前的模块环境和ModuleConfigManager配置管理器 |
| 而ModuleModel模型对象的父模型对象ApplicationModel中包含了一个成员变量Environment环境和ConfigManager配置管理器.</p> |
| <p>在回顾调用过程之前我们先看下模型,配置管理器和环境与配置之间的关系如下图: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/14-config.png" alt="在这里插入图片描述"></p> |
| <p>在ModuleModel对象初始化方法initialize()中创建了模块配置管理器:ModuleConfigManager |
| 如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleConfigManager<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>ModuleEnvironment环境信息对象也会在配置管理器创建的时候被调用到: |
| 如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> ModuleEnvironment <span style="color:#268bd2">getModelEnvironment</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleEnvironment <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> moduleEnvironment <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ModuleEnvironment<span style="color:#719e07">)</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ModuleExt<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>ModuleEnvironment<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> moduleEnvironment<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在扩展对象ExtensionLoader进行对象ModuleEnvironment创建之后会对对象进行初始化调用 initExtension(instance)方法 初始化的时候调用如下代码: |
| ExtensionLoader中的初始化方法如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initExtension</span><span style="color:#719e07">(</span>T instance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">instanceof</span> Lifecycle<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Lifecycle lifecycle <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>Lifecycle<span style="color:#719e07">)</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> lifecycle<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="143-属性加载">14.3 属性加载</h2> |
| <h3 id="1431-environment中属性的初始化方法">14.3.1 Environment中属性的初始化方法</h3> |
| <p>这个初始化方法对应ModuleEnvironment的父类型Environment中的初始化方法如下:initialize()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载在JVM或者环境变量指定的dubbo.properties配置文件 配置的key为dubbo.properties.file ,如果未指定则查找类路径下的dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>propertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> PropertiesConfiguration<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//系统JVM参数的配置无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getProperty(key)来调用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>systemConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SystemConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//系统环境变量的配置,无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getenv(key)来获取就可以 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>environmentConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> EnvironmentConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从远程配置中心的全局配置获取对应配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>externalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;ExternalConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从远程配置中心的应用配置获取对应配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>appExternalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;AppExternalConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用内的配置比如: Spring Environment/PropertySources/application.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>appConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;AppConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载迁移配置,用户在JVM参数或者环境变量中指定的dubbo.migration.file,如果用户未指定测尝试加载类路径下的dubbo-migration.yaml |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadMigrationRule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1442--属性变量说明">14.4.2 属性变量说明</h3> |
| <p>前面我们已经基本上介绍了各个属性的含义下面用一个表格列举一下方便查看:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性变量名</th> |
| <th>属性类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>propertiesConfiguration</td> |
| <td>PropertiesConfiguration</td> |
| <td>dubbo.properties文件中的属性</td> |
| </tr> |
| <tr> |
| <td>systemConfiguration</td> |
| <td>SystemConfiguration</td> |
| <td>JVM参数 启动进程时指定的 (-D)配置</td> |
| </tr> |
| <tr> |
| <td>environmentConfiguration</td> |
| <td>EnvironmentConfiguration</td> |
| <td>环境变量中的配置</td> |
| </tr> |
| <tr> |
| <td>externalConfiguration</td> |
| <td>InmemoryConfiguration</td> |
| <td>外部配置全局配置 例如配置中心中 config-center global/default config</td> |
| </tr> |
| <tr> |
| <td>appExternalConfiguration</td> |
| <td>InmemoryConfiguration</td> |
| <td>外部的应用配置 例如配置中心中执行的当前应用的配置 config-center app config</td> |
| </tr> |
| <tr> |
| <td>appConfiguration</td> |
| <td>InmemoryConfiguration</td> |
| <td>来自应用中的配置例如:Spring Environment/PropertySources/application.properties</td> |
| </tr> |
| <tr> |
| <td>globalConfiguration</td> |
| <td>CompositeConfiguration</td> |
| <td>前面6个配置属性放到一起就是这个</td> |
| </tr> |
| <tr> |
| <td>globalConfigurationMaps</td> |
| <td>List&lt;Map&lt;String, String&raquo;</td> |
| <td>最前面的6个属性转换为map放到一起就是这个可以理解为将全局配置globalConfiguration转换成了列表 这个列表顺序在这里是:SystemConfiguration -&gt; EnvironmentConfiguration -&gt; AppExternalConfiguration -&gt; ExternalConfiguration -&gt; AppConfiguration -&gt; AbstractConfig -&gt; PropertiesConfiguration</td> |
| </tr> |
| <tr> |
| <td>defaultDynamicGlobalConfiguration</td> |
| <td>CompositeConfiguration</td> |
| <td>这个也是一个组合配置将defaultDynamicConfiguration动态配置(来自配置中心的配置)和全局配置添加到了自己的配置列表中 列表顺序为defaultDynamicConfiguration -&gt; globalConfiguration</td> |
| </tr> |
| <tr> |
| <td>localMigrationRule</td> |
| <td>String</td> |
| <td>,用户在JVM参数或者环境变量中指定的dubbo.migration.file,如果用户未指定测尝试加载类路径下的dubbo-migration.yaml</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>关于每个配置信息这里还是来了解下细节,方便大家了解原理.</p> |
| <h3 id="1433-dubboproperties配置文件加载解析原理">14.3.3 dubbo.properties配置文件加载解析原理</h3> |
| <p>如前面所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//加载在JVM或者环境变量指定的dubbo.properties配置文件 配置的key为dubbo.properties.file ,如果未指定则查找类路径下的dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>propertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> PropertiesConfiguration<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>下面就直接提构造器的PropertiesConfiguration代码了:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">PropertiesConfiguration</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refresh</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置获取的过程是借助工具类ConfigUtils来获取的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> properties <span style="color:#719e07">=</span> ConfigUtils<span style="color:#719e07">.</span>getProperties<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">.</span>getClassLoaders<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看ConfigUtils的getProperties方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> Properties <span style="color:#268bd2">getProperties</span><span style="color:#719e07">(</span>Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个配置的KEY是dubbo.properties.file System.getProperty是从JVM参数中获取配置的 一般情况下我们在启动Java进程的时候会指定Dubbo配置文件 如配置: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//-Ddubbo.properties.file=/dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_PROPERTIES_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//优先级最高的JVM参数拿不到数据则从 环境变量中获取,这个配置key也是dubbo.properties.file System.getenv是从环境变量中获取数据 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//例如我们在环境变量中配置 dubbo.properties.file=/dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_PROPERTIES_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果在JVM参数和环境变量都拿不到这个配置文件的路径我们就用默认的吧 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//默认的路径是类路径下的资源文件 这个路径是: dubbo.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> CommonConstants<span style="color:#719e07">.</span>DEFAULT_DUBBO_PROPERTIES<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> ConfigUtils<span style="color:#719e07">.</span>loadProperties<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">,</span> path<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>路径获取之后加载详细的配置内容:</p> |
| <p>ConfigUtils的loadProperties代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ConfigUtils<span style="color:#719e07">.</span>loadProperties<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">,</span> path<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> Properties <span style="color:#268bd2">loadProperties</span><span style="color:#719e07">(</span>Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders<span style="color:#719e07">,</span> String fileName<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> allowMultiFile<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> optional<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Properties properties <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// add scene judgement in windows environment Fix 2557 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//检查文件是否存在 直接加载配置文件如果加载到了配置文件则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>checkFileNameExist<span style="color:#719e07">(</span>fileName<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> FileInputStream input <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FileInputStream<span style="color:#719e07">(</span>fileName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>load<span style="color:#719e07">(</span>input<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> input<span style="color:#719e07">.</span>close<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为什么会有下面的逻辑呢,如果仅仅使用上面的加载方式只能加载到本系统下的配置文件,无法加载封装在jar中的根路径的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL<span style="color:#719e07">&gt;</span> set <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoadersToLoad <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>ClassUtils<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个方法loadResources在扩展加载的时候说过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> set <span style="color:#719e07">=</span> ClassLoaderResourceLoader<span style="color:#719e07">.</span>loadResources<span style="color:#719e07">(</span>fileName<span style="color:#719e07">,</span> classLoadersToLoad<span style="color:#719e07">).</span>values<span style="color:#719e07">().</span>stream<span style="color:#719e07">().</span>reduce<span style="color:#719e07">(</span><span style="color:#719e07">new</span> LinkedHashSet<span style="color:#719e07">&lt;&gt;(),</span> <span style="color:#719e07">(</span>a<span style="color:#719e07">,</span> i<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> a<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>i<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> a<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Fail to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file: &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>set<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>optional<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; found on the class path.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>allowMultiFile<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>set<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String errMsg <span style="color:#719e07">=</span> String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;only 1 %s file is expected, but %d dubbo.properties files found on class path: %s&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> fileName<span style="color:#719e07">,</span> set<span style="color:#719e07">.</span>size<span style="color:#719e07">(),</span> set<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>errMsg<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fall back to use method getResourceAsStream |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>load<span style="color:#719e07">(</span>ClassUtils<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">().</span>getResourceAsStream<span style="color:#719e07">(</span>fileName<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; properties file from &#34;</span> <span style="color:#719e07">+</span> set<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL url <span style="color:#719e07">:</span> set<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Properties p <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> InputStream input <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>openStream<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>input <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> p<span style="color:#719e07">.</span>load<span style="color:#719e07">(</span>input<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>putAll<span style="color:#719e07">(</span>p<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> input<span style="color:#719e07">.</span>close<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Fail to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> url <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>完整的配置加载流程这里用简单的话描述下:</p> |
| <ul> |
| <li>项目内配置查询 |
| <ul> |
| <li>路径查询 |
| <ul> |
| <li>从JVM参数中获取配置的 dubbo.properties.file配置文件路径</li> |
| <li>如果前面未获取到路径则从环境变量参数中获取配置的dubbo.properties.file配置文件路径</li> |
| <li>如果前面未获取到路径则使用默认路径dubbo.propertie</li> |
| </ul> |
| </li> |
| <li>配置加载 |
| <ul> |
| <li>将路径转为FileInputStream 然后使用Properties加载</li> |
| </ul> |
| </li> |
| </ul> |
| </li> |
| <li>依赖中的配置扫描查询 |
| <ul> |
| <li>使用类加载器扫描所有资源URL</li> |
| <li>url转InputStream 如 url.openStream() 然后使用Properties加载</li> |
| </ul> |
| </li> |
| </ul> |
| <h3 id="1434-加载jvm参数的配置">14.3.4 加载JVM参数的配置</h3> |
| <p>这里我们继续看SystemConfiguration配置的加载 |
| 这个直接看下代码就可以了:</p> |
| <p>这个类型仅仅是使用System.getProperty来获取JVM配置即可</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">SystemConfiguration</span> <span style="color:#268bd2">implements</span> Configuration <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Object <span style="color:#268bd2">getInternalProperty</span><span style="color:#719e07">(</span>String key<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getProperties</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">)</span> System<span style="color:#719e07">.</span>getProperties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1435-加载环境变量参数的配置">14.3.5 加载环境变量参数的配置</h3> |
| <p>这里我们来看EnvironmentConfiguration,这里我们直接来看代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">EnvironmentConfiguration</span> <span style="color:#268bd2">implements</span> Configuration <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Object <span style="color:#268bd2">getInternalProperty</span><span style="color:#719e07">(</span>String key<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String value <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>value<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> value <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>toOSStyleKey<span style="color:#719e07">(</span>key<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> value<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getProperties</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1436-内存配置的封装inmemoryconfiguration">14.3.6 内存配置的封装:InmemoryConfiguration</h3> |
| <p>这里我们看下InmemoryConfiguration的设计,这个直接看代码吧内部使用了一个LinkedHashMap来存储配置</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">InmemoryConfiguration</span> <span style="color:#268bd2">implements</span> Configuration <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String name<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// stores the configuration key-value pairs |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> store <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">InmemoryConfiguration</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">InmemoryConfiguration</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>name <span style="color:#719e07">=</span> name<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">InmemoryConfiguration</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> properties<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>setProperties<span style="color:#719e07">(</span>properties<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Object <span style="color:#268bd2">getInternalProperty</span><span style="color:#719e07">(</span>String key<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> store<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Add one property into the store, the previous value will be replaced if the key exists |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addProperty</span><span style="color:#719e07">(</span>String key<span style="color:#719e07">,</span> String value<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> store<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> value<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Add a set of properties into the store |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addProperties</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> properties<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>properties <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>store<span style="color:#719e07">.</span>putAll<span style="color:#719e07">(</span>properties<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * set store |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setProperties</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> properties<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>properties <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>store <span style="color:#719e07">=</span> properties<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getProperties</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> store<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1437-dubbo迁移新版本的配置文件加载dubbo-migrationyaml">14.3.7 Dubbo迁移新版本的配置文件加载dubbo-migration.yaml</h3> |
| <p>关于配置迁移文件的用法可以看下这个Dubbo官方的<a href="https://dubbo.apache.org/zh-cn/docs/advanced/migration-invoker/">地址迁移规则说明</a></p> |
| <p>这个配置文件的文件名字为:dubbo-migration.yaml</p> |
| <p>这个和14.3.4加载JVM参数配置的过程是相似的细节可以看14.3.4节</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadMigrationRule</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//JVM参数的dubbo.migration.file配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_MIGRATION_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//环境变量的dubbo.migration.file配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_MIGRATION_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认的迁移配置文件 dubbo-migration.yaml |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> CommonConstants<span style="color:#719e07">.</span>DEFAULT_DUBBO_MIGRATION_FILE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>localMigrationRule <span style="color:#719e07">=</span> ConfigUtils<span style="color:#719e07">.</span>loadMigrationRule<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">.</span>getClassLoaders<span style="color:#719e07">(),</span> path<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="144-初始化加载应用配置">14.4 初始化加载应用配置</h2> |
| <p>加载配置涉及到了配置优先级的处理,</p> |
| <p>下面来看加载配置代码 loadApplicationConfigs()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadApplicationConfigs</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//发布器还是不处理配置加载的逻辑还是交给配置管理器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>loadConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>配置管理器加载配置:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadConfigs</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// application config has load before starting config center |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// load dubbo.applications.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载应用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ApplicationConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load dubbo.monitors.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载监控配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>MonitorConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load dubbo.metrics.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载指标监控配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>MetricsConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load multiple config types: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// load dubbo.protocols.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载协议配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ProtocolConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load dubbo.registries.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>RegistryConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load dubbo.metadata-report.xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载元数据配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>MetadataReportConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// config centers has bean loaded before starting config center |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//loadConfigsOfTypeFromProps(ConfigCenterConfig.class); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> refreshAll<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// set model name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isBlank<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getModelName<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getApplicationName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文地址:<a href="https://blog.elastic.link/2022/07/10/dubbo/14-dubbo-pei-zhi-jia-zai-quan-jie-xi/">Dubbo配置加载全解析</a></p></description></item><item><title>Blog: 13-Dubbo的三大中心之配置中心</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/13/13-dubbo%E7%9A%84%E4%B8%89%E5%A4%A7%E4%B8%AD%E5%BF%83%E4%B9%8B%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83/</link><pubDate>Sat, 13 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/13/13-dubbo%E7%9A%84%E4%B8%89%E5%A4%A7%E4%B8%AD%E5%BF%83%E4%B9%8B%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83/</guid><description> |
| <h1 id="13-dubbo的三大中心之配置中心">13-Dubbo的三大中心之配置中心</h1> |
| <h2 id="131-配置中心简介">13.1 配置中心简介</h2> |
| <p>百度了一段不错的文字来介绍配置中心,我看了下肯定比我写的好多了,那我就直接拷贝过来一起看:</p> |
| <p><em>对于传统的单体应用而言,常使用配置文件来管理所有配置,比如SpringBoot的application.yml文件,但是在微服务架构中全部手动修改的话很麻烦而且不易维护。微服务的配置管理一般有以下需求:</em></p> |
| <ul> |
| <li><em><strong>集中配置管理</strong>,一个微服务架构中可能有成百上千个微服务,所以集中配置管理是很重要的。</em></li> |
| <li><em><strong>不同环境不同配置</strong>,比如数据源配置在不同环境(开发,生产,测试)中是不同的。</em></li> |
| <li><em><strong>运行期间可动态调整</strong>。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小等。</em></li> |
| <li><em><strong>配置修改后可自动更新</strong>。如配置内容发生变化,微服务可以自动更新配置。</em></li> |
| </ul> |
| <p>综上所述对于微服务架构而言,一套统一的,通用的管理配置机制是不可缺少的主要组成部分。常见的做法就是通过配置服务器进行管理。</p> |
| <p>不过对于来看这个文章的小伙伴应该大部分对配置中心都会比较了解,分布式配置中心实现简单一点就是借助Zookeeper来协助存储,变更推送,不过为了实现各种不同的业务需求,市面上已经有很多很可靠的配置中心可用了,比如我从其他地方拷贝过来的图(虽然不是最新的但是可以供大家参考下):</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/register.png" alt="在这里插入图片描述"></p> |
| <p>每个配置中心都有自己的实现,如果对配置中心感兴趣的小伙伴可以自行去对应开源项目官网查看,我们这里来看Dubbo对配置中心的支持</p> |
| <p><em><strong>多配置中心:</strong> Dubbo支持多配置中心,来 <strong>保证其中一个配置中心集群出现不可用时能够切换到另一个配置中心集群</strong> ,保证能够正常从配置中心获取全局的配置、路由规则等信息。这也能够满足配置中心在部署上适应各类高可用的部署架构模式。-来自官网</em></p> |
| <p>做中间件可能考虑更多的的不仅仅是性能,还要过多的考虑高可用,高可用怎么做呢,其实就是失效转移,主备切换,降级,降级再降级这些理论的运用,多多考虑某一个服务挂了怎么办,Dubbo的多配置中心支持增加了复杂性,不过降低了服务不可用的风险,有一定的人手的公司还是值得做的。</p> |
| <p>关于Dubbo的配置中心这里我来贴个官网的图: |
| <img src="https://dubbo.apache.org/imgs/v3/concepts/centers-config.png" alt="在这里插入图片描述"> |
| 关于官网的介绍可以自行去官网看详细内容: <a href="https://dubbo.apache.org/zh-cn/docs/concepts/registry-configcenter-metadata/">部署架构(注册中心、配置中心、元数据中心</a></p> |
| <h2 id="132-启动配置中心">13.2 启动配置中心</h2> |
| <p>在上一个博客中说到了<a href="https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/">《12-全局视野来看Dubbo3.0.8的服务启动生命周期》</a>Dubbo应用的启动过程DefaultApplicationDeployer的initialize()方法的全生命周期,在初始化方法中通过调用startConfigCenter();方法来启动配置中心的加载。后面就来详细看下:</p> |
| <p>DefaultApplicationDeployer类型的startConfigCenter()代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startConfigCenter</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load application config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载应用程序配置 (配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置,也可能是多应用,多注册中心这样的配置) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ApplicationConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// try set model name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isBlank<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>getModelName<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置一下模块名字和模块描述(我们再Debug里面经常会看到这个描述信息 toString直接返回了Dubbo为我们改造的对象信息) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">.</span>tryGetApplicationName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load config centers |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载配置中心配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置,也可能是多应用,多注册中心这样的配置) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ConfigCenterConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//出于兼容性目的,如果没有明确指定配置中心,并且registryConfig的UseAConfigCenter为null或true,请使用registry作为默认配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> useRegistryAsConfigCenterIfNecessary<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// check Config Center |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//配置管理器中获取配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>ConfigCenterConfig<span style="color:#719e07">&gt;</span> configCenters <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getConfigCenters<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心配置不为空则刷新配置中心配置将其放入配置管理器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//下面开始刷新配置中心配置,如果配置中心配置为空则执行空刷新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>configCenters<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心不存在的配置刷新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ConfigCenterConfig configCenterConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConfigCenterConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> configCenterConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> configCenterConfig<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//验证配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ConfigValidationUtils<span style="color:#719e07">.</span>validateConfigCenterConfig<span style="color:#719e07">(</span>configCenterConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>configCenterConfig<span style="color:#719e07">.</span>isValid<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置合法则将配置放入配置管理器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>addConfigCenter<span style="color:#719e07">(</span>configCenterConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> configCenters <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getConfigCenters<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一个或者多个配置中心配置存在的情况下的配置刷新 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ConfigCenterConfig configCenterConfig <span style="color:#719e07">:</span> configCenters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> configCenterConfig<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//验证配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ConfigValidationUtils<span style="color:#719e07">.</span>validateConfigCenterConfig<span style="color:#719e07">(</span>configCenterConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心配置不为空则将配置中心配置添加到environment中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>configCenters<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多配置中心本地动态配置对象创建CompositeDynamicConfiguration |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> CompositeDynamicConfiguration compositeDynamicConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CompositeDynamicConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置中心的相关配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ConfigCenterConfig configCenter <span style="color:#719e07">:</span> configCenters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Pass config from ConfigCenterBean to environment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将配置中心的外部化配置,更新到环境里面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> environment<span style="color:#719e07">.</span>updateExternalConfigMap<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">.</span>getExternalConfiguration<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置中心的应用配置,添加到环境里面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> environment<span style="color:#719e07">.</span>updateAppExternalConfigMap<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">.</span>getAppExternalConfiguration<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Fetch config from remote config center |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//从配置中心拉取配置添加到组合配置中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> compositeDynamicConfiguration<span style="color:#719e07">.</span>addConfiguration<span style="color:#719e07">(</span>prepareEnvironment<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置中心中的动态配置信息 设置到environment的动态配置属性中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> environment<span style="color:#719e07">.</span>setDynamicConfiguration<span style="color:#719e07">(</span>compositeDynamicConfiguration<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="1321-配置管理器加载配置">13.2.1 配置管理器加载配置</h3> |
| <p>前面我们看到了配置管理器会从系统属性中加载配置这里我们来详细看下,配置往往是我们使用者比较关注的内容,</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>configManager<span style="color:#719e07">.</span>loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>ApplicationConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>配置管理器加载配置代码: |
| 来自ConfigManager的父类型AbstractConfigManager中</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">loadConfigsOfTypeFromProps</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> cls<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> tmpConfigs <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取属性配置 dubbo properties in classpath |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这个配置信息回头说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> PropertiesConfiguration properties <span style="color:#719e07">=</span> environment<span style="color:#719e07">.</span>getPropertiesConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load multiple configs with id |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//多注册中心配置id查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/* |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> 搜索属性并提取指定类型的配置ID。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> 例如如下配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> # 配置信息 properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> dubbo.registries.registry1.address=xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> dubbo.registries.registry2.port=xxx |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> # 提取配置的id extract |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> Set configIds = getConfigIds(RegistryConfig.class) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> # 提取的配置id结果 result |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> configIds: [&#34;registry1&#34;, &#34;registry2&#34;] |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> configIds <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getConfigIdsFromProps<span style="color:#719e07">(</span>cls<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> configIds<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>id <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历这些配置id 判断配置缓存(configsCache成员变量)中是否已经存在当前配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>getConfig<span style="color:#719e07">(</span>cls<span style="color:#719e07">,</span> id<span style="color:#719e07">).</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> T config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建配置对象 为配置对象初始化配置id |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> config <span style="color:#719e07">=</span> createConfig<span style="color:#719e07">(</span>cls<span style="color:#719e07">,</span> scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> config<span style="color:#719e07">.</span>setId<span style="color:#719e07">(</span>id<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;create config instance failed, id: &#34;</span> <span style="color:#719e07">+</span> id <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, type:&#34;</span> <span style="color:#719e07">+</span> cls<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> addDefaultNameConfig <span style="color:#719e07">=</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// add default name config (same as id), e.g. dubbo.protocols.rest.port=1234 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> key <span style="color:#719e07">=</span> DUBBO <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.&#34;</span> <span style="color:#719e07">+</span> AbstractConfig<span style="color:#719e07">.</span>getPluralTagName<span style="color:#719e07">(</span>cls<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.&#34;</span> <span style="color:#719e07">+</span> id <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.name&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>properties<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>key<span style="color:#719e07">)</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>setProperty<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> id<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> addDefaultNameConfig <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新配置信息 好理解点就是Dubbo配置属性重写 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> config<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将当前配置信息添加到配置缓存中configsCache成员变量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>addConfig<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> tmpConfigs<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;load config failed, id: &#34;</span> <span style="color:#719e07">+</span> id <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, type:&#34;</span> <span style="color:#719e07">+</span> cls<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;load config failed, id: &#34;</span> <span style="color:#719e07">+</span> id <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, type:&#34;</span> <span style="color:#719e07">+</span> cls<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>addDefaultNameConfig <span style="color:#719e07">&amp;&amp;</span> key <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// If none config of the type, try load single config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果没有该类型的配置,请尝试加载单个配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>getConfigs<span style="color:#719e07">(</span>cls<span style="color:#719e07">).</span>isEmpty<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load single config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;&gt;</span> configurationMaps <span style="color:#719e07">=</span> environment<span style="color:#719e07">.</span>getConfigurationMaps<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ConfigurationUtils<span style="color:#719e07">.</span>hasSubProperties<span style="color:#719e07">(</span>configurationMaps<span style="color:#719e07">,</span> AbstractConfig<span style="color:#719e07">.</span>getTypePrefix<span style="color:#719e07">(</span>cls<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> T config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> config <span style="color:#719e07">=</span> createConfig<span style="color:#719e07">(</span>cls<span style="color:#719e07">,</span> scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> config<span style="color:#719e07">.</span>refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;create default config instance failed, type:&#34;</span> <span style="color:#719e07">+</span> cls<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>addConfig<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> tmpConfigs<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> tmpConfigs<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="1322--默认使用注册中心地址为配置中心">13.2.2 默认使用注册中心地址为配置中心</h2> |
| <p>出于兼容性目的,如果没有明确指定配置中心,并且registryConfig的UseAConfigCenter为null或true,请使用registry作为默认配置中心 |
| 调用方法useRegistryAsConfigCenterIfNecessary()来处理逻辑 |
| 我们来看下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">useRegistryAsConfigCenterIfNecessary</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//我们使用DynamicConfiguration的加载状态来决定是否已启动ConfigCenter。配置中心配置加载完成之后会初始化动态配置defaultDynamicConfiguration |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>environment<span style="color:#719e07">.</span>getDynamicConfiguration<span style="color:#719e07">().</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从配置缓存中查询是否存在config-center相关配置 ,如果已经存在配置了就无需使用注册中心的配置地址直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>configManager<span style="color:#719e07">.</span>getConfigCenters<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//加载注册中心相关配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>loadConfigsOfTypeFromProps<span style="color:#719e07">(</span>RegistryConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> defaultRegistries <span style="color:#719e07">=</span> configManager<span style="color:#719e07">.</span>getDefaultRegistries<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在注册中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultRegistries<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> defaultRegistries |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>stream<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//判断当前注册中心是否可以作为配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>filter<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>isUsedRegistryAsConfigCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将注册中心配置映射转换为配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>map<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>registryAsConfigCenter<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历配置中心流 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>configCenter <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>configManager<span style="color:#719e07">.</span>getConfigCenter<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">.</span>getId<span style="color:#719e07">()).</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置管理器中添加配置中心,方便后去读取配置中心的配置信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>addConfigCenter<span style="color:#719e07">(</span>configCenter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;use registry as config-center: &#34;</span> <span style="color:#719e07">+</span> configCenter<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="13221-如何判断当前注册中心是否可以为配置中心">13.2.2.1 如何判断当前注册中心是否可以为配置中心</h4> |
| <p>isUsedRegistryAsConfigCenter</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isUsedRegistryAsCenter</span><span style="color:#719e07">(</span>RegistryConfig registryConfig<span style="color:#719e07">,</span> Supplier<span style="color:#719e07">&lt;</span>Boolean<span style="color:#719e07">&gt;</span> usedRegistryAsCenter<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> String centerType<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;</span> extensionClass<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">boolean</span> supported<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个useAsConfigCenter参数是来自注册中心的配置 如果配置了这个值则以这个值为准,如果配置了false则这个注册中心不能做为配置中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Boolean configuredValue <span style="color:#719e07">=</span> usedRegistryAsCenter<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>configuredValue <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> <span style="color:#586e75">// If configured, take its value. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> supported <span style="color:#719e07">=</span> configuredValue<span style="color:#719e07">.</span>booleanValue<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> <span style="color:#586e75">// Or check the extension existence |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这个逻辑的话是判断下注册中心的协议是否满足要求,我们例子代码中使用的是zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String protocol <span style="color:#719e07">=</span> registryConfig<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个扩展是否支持的逻辑判断是这样的扫描扩展类 看一下当前扩展类型是否有对应协议的扩展 比如在扩展文件里面这样配置过后是支持的 protocol=xxxImpl |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//动态配置的扩展类型为:interface org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//zookeeper协议肯定是支持的因为zookeeper协议实现了这个动态配置工厂 ,这个扩展类型为ZookeeperDynamicConfigurationFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//代码位置在dubbo-configcenter-zookeeper包中的org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory扩展配置中内容为zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> supported <span style="color:#719e07">=</span> supportsExtension<span style="color:#719e07">(</span>extensionClass<span style="color:#719e07">,</span> protocol<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心走注册中心会打印一条日志 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No value is configured in the registry, the %s extension[name : %s] %s as the %s center&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">,</span> extensionClass<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">(),</span> protocol<span style="color:#719e07">,</span> supported <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;supports&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;does not support&#34;</span><span style="color:#719e07">,</span> centerType<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心走注册中心会打印一条日志 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;The registry[%s] will be %s as the %s center&#34;</span><span style="color:#719e07">,</span> registryConfig<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> supported <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;used&#34;</span> <span style="color:#719e07">:</span> <span style="color:#2aa198">&#34;not used&#34;</span><span style="color:#719e07">,</span> centerType<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> supported<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个扩展是否支持的逻辑判断是这样的扫描扩展类 看一下当前扩展类型是否有对应协议的扩展 比如在扩展文件里面这样配置过后是支持的 protocol=xxxImpl |
| 配置中心的动态配置的扩展类型为 org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory</p> |
| <p>zookeeper协议肯定是支持的因为zookeeper协议实现了这个动态配置工厂 ,这个扩展类型为ZookeeperDynamicConfigurationFactory代码位置在dubbo-configcenter-zookeeper包中的org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory扩展配置中内容为</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory |
| </span></span></code></pre></div><h4 id="13222-注册中心配置转配置中心配置">13.2.2.2 注册中心配置转配置中心配置</h4> |
| <p>这个逻辑是registryAsConfigCenter方法,我来贴一下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> ConfigCenterConfig <span style="color:#268bd2">registryAsConfigCenter</span><span style="color:#719e07">(</span>RegistryConfig registryConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心协议获取这里例子中的是zookeeper协议 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String protocol <span style="color:#719e07">=</span> registryConfig<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心端口 2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Integer port <span style="color:#719e07">=</span> registryConfig<span style="color:#719e07">.</span>getPort<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//在Dubbo中配置信息 很多情况下都以URL形式表示,这里转换后的地址为zookeeper://127.0.0.1:2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> URL<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getAddress<span style="color:#719e07">(),</span> registryConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成当前配置中心的id 封装之后的内容为: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//config-center-zookeeper-127.0.0.1-2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String id <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;config-center-&#34;</span> <span style="color:#719e07">+</span> protocol <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> url<span style="color:#719e07">.</span>getHost<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> port<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心配置对象创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ConfigCenterConfig cc <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConfigCenterConfig<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//config-center-zookeeper-127.0.0.1-2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setId<span style="color:#719e07">(</span>id<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cc<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>setParameters<span style="color:#719e07">(</span><span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmptyMap<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">().</span>putAll<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">());</span> <span style="color:#586e75">// copy the parameters |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">().</span>put<span style="color:#719e07">(</span>CLIENT_KEY<span style="color:#719e07">,</span> registryConfig<span style="color:#719e07">.</span>getClient<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zookeeper |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setProtocol<span style="color:#719e07">(</span>protocol<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//2181 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setPort<span style="color:#719e07">(</span>port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>setGroup<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个方法转换地址是修复bug用的可以看bug https://github.com/apache/dubbo/issues/6476 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setAddress<span style="color:#719e07">(</span>getRegistryCompatibleAddress<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心分组做为配置中心命名空间 这里为null |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setNamespace<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getGroup<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zk认证信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setUsername<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getUsername<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zk认证信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setPassword<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getPassword<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getTimeout<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cc<span style="color:#719e07">.</span>setTimeout<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getTimeout<span style="color:#719e07">().</span>longValue<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个属性注释中已经建议了已经弃用了默认就是false了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果配置中心被赋予最高优先级,它将覆盖所有其他配置, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cc<span style="color:#719e07">.</span>setHighestPriority<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cc<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="133-配置刷新逻辑">13.3 配置刷新逻辑</h2> |
| <p>来自AbstractConfig类型的refresh()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refresh</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> refreshed<span style="color:#719e07">.</span>set<span style="color:#719e07">(</span><span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// check and init before do refresh |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//刷新之前执行的逻辑 这里并做什么逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> preProcessRefresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取当前域模型的环境信息对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Environment environment <span style="color:#719e07">=</span> getScopeModel<span style="color:#719e07">().</span>getModelEnvironment<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;&gt;</span> configurationMaps <span style="color:#719e07">=</span> environment<span style="color:#719e07">.</span>getConfigurationMaps<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Search props starts with PREFIX in order |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String preferredPrefix <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String prefix <span style="color:#719e07">:</span> getPrefixes<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ConfigurationUtils<span style="color:#719e07">.</span>hasSubProperties<span style="color:#719e07">(</span>configurationMaps<span style="color:#719e07">,</span> prefix<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> preferredPrefix <span style="color:#719e07">=</span> prefix<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>preferredPrefix <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> preferredPrefix <span style="color:#719e07">=</span> getPrefixes<span style="color:#719e07">().</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Extract sub props (which key was starts with preferredPrefix) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collection<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;&gt;</span> instanceConfigMaps <span style="color:#719e07">=</span> environment<span style="color:#719e07">.</span>getConfigurationMaps<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> preferredPrefix<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> subProperties <span style="color:#719e07">=</span> ConfigurationUtils<span style="color:#719e07">.</span>getSubProperties<span style="color:#719e07">(</span>instanceConfigMaps<span style="color:#719e07">,</span> preferredPrefix<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> InmemoryConfiguration subPropsConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span>subProperties<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isDebugEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String idOrName <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>getId<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> idOrName <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;[id=&#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getId<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;]&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> ReflectUtils<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;getName&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> idOrName <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;[name=&#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;]&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>debug<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Refreshing &#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getClass<span style="color:#719e07">().</span>getSimpleName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> idOrName <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34; with prefix [&#34;</span> <span style="color:#719e07">+</span> preferredPrefix <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;], extracted props: &#34;</span> <span style="color:#719e07">+</span> subProperties<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> assignProperties<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> environment<span style="color:#719e07">,</span> subProperties<span style="color:#719e07">,</span> subPropsConfiguration<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// process extra refresh of subclass, e.g. refresh method configs |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> processExtraRefresh<span style="color:#719e07">(</span>preferredPrefix<span style="color:#719e07">,</span> subPropsConfiguration<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to override field value of config bean: &#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to override field value of config bean: &#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> postProcessRefresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/13-config-1.png" alt="在这里插入图片描述"></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/13-config2.png" alt="在这里插入图片描述"></p> |
| <h2 id="134-配置中心配置大全">13.4 配置中心配置大全</h2> |
| <p>ConfigCenterConfig类型 |
| 下面配置信息来自官网 |
| dubbo:config-center 配置</p> |
| <p>配置中心。对应的配置类:<code>org.apache.dubbo.config.ConfigCenterConfig</code></p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>protocol</td> |
| <td>config.protocol</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>zookeeper</td> |
| <td>使用哪个配置中心:apollo、zookeeper、nacos等。 以zookeeper为例 1. 指定protocol,则address可以简化为<code>127.0.0.1:2181</code>; 2. 不指定protocol,则address取值为<code>zookeeper://127.0.0.1:2181</code></td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>address</td> |
| <td>config.address</td> |
| <td>string</td> |
| <td>必填</td> |
| <td></td> |
| <td>配置中心地址。 取值参见protocol说明</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>highest-priority</td> |
| <td>config.highestPriority</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>来自配置中心的配置项具有最高优先级,即会覆盖本地配置项。</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>namespace</td> |
| <td>config.namespace</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>通常用于多租户隔离,实际含义视具体配置中心而不同。 如: zookeeper - 环境隔离,默认值<code>dubbo</code>; apollo - 区分不同领域的配置集合,默认使用<code>dubbo</code>和<code>application</code></td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>config.cluster</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>含义视所选定的配置中心而不同。 如Apollo中用来区分不同的配置集群</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>config.group</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>含义视所选定的配置中心而不同。 nacos - 隔离不同配置集 zookeeper - 隔离不同配置集</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>check</td> |
| <td>config.check</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>当配置中心连接失败时,是否终止应用启动。</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>config-file</td> |
| <td>config.configFile</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo.properties</td> |
| <td>全局级配置文件所映射到的key zookeeper - 默认路径/dubbo/config/dubbo/dubbo.properties apollo - dubbo namespace中的dubbo.properties键</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>config.timeout</td> |
| <td>integer</td> |
| <td></td> |
| <td>3000ms</td> |
| <td>获取配置的超时时间</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>username</td> |
| <td></td> |
| <td>string</td> |
| <td></td> |
| <td></td> |
| <td>如果配置中心需要做校验,用户名 Apollo暂未启用</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>password</td> |
| <td></td> |
| <td>string</td> |
| <td></td> |
| <td></td> |
| <td>如果配置中心需要做校验,密码 Apollo暂未启用</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>parameters</td> |
| <td></td> |
| <td>Map&lt;string, string&gt;</td> |
| <td></td> |
| <td></td> |
| <td>扩展参数,用来支持不同配置中心的定制化配置参数</td> |
| <td>2.7.0+</td> |
| </tr> |
| <tr> |
| <td>include-spring-env</td> |
| <td></td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>使用Spring框架时支持,为true时,会自动从Spring Environment中读取配置。 默认依次读取 key为dubbo.properties的配置 key为dubbo.properties的PropertySource</td> |
| <td>2.7.0+</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>原文:<a href="https://blog.elastic.link/2022/07/10/dubbo/13-dubbo-de-san-da-zhong-xin-zhi-pei-zhi-zhong-xin-yuan-ma-jie-xi/">Dubbo的三大中心之配置中心</a></p></description></item><item><title>Blog: 12 全局视野来看Dubbo3的服务启动生命周期</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/12/12-%E5%85%A8%E5%B1%80%E8%A7%86%E9%87%8E%E6%9D%A5%E7%9C%8Bdubbo3%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%90%AF%E5%8A%A8%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/</link><pubDate>Fri, 12 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/12/12-%E5%85%A8%E5%B1%80%E8%A7%86%E9%87%8E%E6%9D%A5%E7%9C%8Bdubbo3%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%90%AF%E5%8A%A8%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/</guid><description> |
| <h1 id="12-全局视野来看dubbo3的服务启动生命周期">12 全局视野来看Dubbo3的服务启动生命周期</h1> |
| <h2 id="121-启动方法简介">12.1 启动方法简介</h2> |
| <p>在说启动方法之前先把视野拉回第一章<a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">《1-从一个服务提供者的Demo说起》</a>我们的Demo代码,下面只贴一下核心代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">Application</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> service<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DemoServiceImpl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一个篇章主要说这里: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化应用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化注册中心配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化协议配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化服务配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面我们介绍了Dubbo启动器DubboBootstrap类型对象的创建,又介绍了为DubboBootstrap启动器初始化各种配置信息,这一个博客就开始到了分析启动方法的位置了,Dubbo启动器借助Deployer发布器来启动和发布服务,发布器的启动过程包含了启动配置中心,加载配置,启动元数据中心,启动服务等操作都是比较重要又比较复杂的过程,这里我们先来看下启动过程的生命周期来为后面的内容做好铺垫。</p> |
| <h2 id="122-启动器启动方法的调用逻辑start">12.2 启动器启动方法的调用逻辑start()</h2> |
| <p>这里我们就直接来看DubboBootstrap的start()方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用重载的方法进行启动参数代表是否等待启动结束 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>start<span style="color:#719e07">(</span><span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>我们再来看重载的start方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">start</span><span style="color:#719e07">(</span><span style="color:#dc322f">boolean</span> wait<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个发布器是在ApplicationModel对象创建之后初始化的时候进行初始化的具体类型为DefaultApplicationDeployer |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Future future <span style="color:#719e07">=</span> applicationDeployer<span style="color:#719e07">.</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>wait<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//等待异步启动的结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> future<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动失败则抛出一个异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;await dubbo application start finish failure&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="123-应用程序发布器defaultapplicationdeployer的启动方法">12.3 应用程序发布器DefaultApplicationDeployer的启动方法</h2> |
| <p>发布器是帮助我们发布服务和引用服务的,在Dubbo3中不论是服务提供者还是服务消费者如果想要启动服务都需要走这个启动方法的逻辑,所以务必重视</p> |
| <p>我们直接来看DefaultApplicationDeployer的start()代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Future <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动锁,防止重复启动 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>startLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//发布器,状态已经设置为停止或者失败了就直接抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStopping<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isStopped<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isFailed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is stopping or stopped, can not start again&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// maybe call start again after add new module, check if any new module |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//可能在添加新模块后再次调用start,检查是否有任何新模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里遍历当前应用程序下的所有模块如果某个模块是PENDING状态则这里hasPendingModule的值为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">boolean</span> hasPendingModule <span style="color:#719e07">=</span> hasPendingModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//发布器状态正在启动中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStarting<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// currently, is starting, maybe both start by module and application |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// if it has new modules, start them |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//存在挂器的模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>hasPendingModule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startModules<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if it is starting, reuse previous startFuture |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//模块异步启动中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if is started and no new module, just return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果已启动且没有新模块,直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStarted<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>hasPendingModule<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture<span style="color:#719e07">.</span>completedFuture<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// pending -&gt; starting : first start app |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// started -&gt; starting : re-start app |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//启动状态切换,将启动状态切换到STARTING(pending和started状态无需切换) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//核心初始化逻辑,这里主要做一些应用级别启动比如配置中心,元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动模块(我们的服务提供和服务引用是在这个模块级别的) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> doStart<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> onFailed<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; start failure&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个启动方法逻辑不多 主要三个方法我们重点来看:</p> |
| <ul> |
| <li>onStarting() 这个是启动之前的状态切换</li> |
| <li>initialize() 应用的初始化逻辑 比如配置中心,元数据中心的初始化</li> |
| <li>doStart() 启动模块比如启动我们的服务提供和服务引用的)</li> |
| </ul> |
| <p>继续看后面的细节吧,代码胜千言。</p> |
| <h2 id="124-应用程序发布器对应用级别的初始化逻辑">12.4 应用程序发布器对应用级别的初始化逻辑</h2> |
| <p>这个我们先来看DefaultApplicationDeployer的初始化方法initialize():</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//状态判断 如果已经初始化过了就直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//启动锁,确保在并发调用时完成初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>startLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁 如果已经初始化过了就直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register shutdown hook |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//注册关闭钩子,这个逻辑基本每个中间件应用都必须要要做的事情了,正常关闭应用回收资源,一般没这个逻辑情况下容易出现一些异常,让我们开发人员很疑惑,而这个逻辑往往并不好处理的干净。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registerShutdownHook<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动配置中心,感觉Dubbo3耦合了这个玩意 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startConfigCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载配置,一般配置信息当前机器的来源:环境变量,JVM启动参数,配置文字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadApplicationConfigs<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块发布器 (发布服务提供和服务引用使用) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initModuleDeployers<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// @since 2.7.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//启动元数据中心 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startMetadataCenter<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化完成 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>logger<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has been initialized!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个是个生命周期整体概览的方法,将具体逻辑拆分到各个子方法中,是代码重构的一种策略,上面注释也很清楚了就不细说了,上面每个方法在后面会有单独的博客来分析。</p> |
| <h2 id="125-应用下模块的启动服务的发布与引用">12.5 应用下模块的启动(服务的发布与引用)</h2> |
| <p>我们回过头来详细看DefaultApplicationDeployer的doStart()代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doStart</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 启动模块 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> startModules<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>DefaultApplicationDeployer的 startModules()方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startModules</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ensure init and start internal module first |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//确保初始化并首先启动内部模块,Dubbo3中将模块分为内部和外部,内部是核心代码已经提供的一些服务比如元数据服务,外部是我们自己写的服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> prepareInternalModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// filter and start pending modules, ignore new module during starting, throw exception of module start |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//启动所有的模块 (启动所有的服务) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ModuleModel moduleModel <span style="color:#719e07">:</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>applicationModel<span style="color:#719e07">.</span>getModuleModels<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个状态默认就是PENDING的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">().</span>isPending<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块启动器,发布服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> moduleModel<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">().</span>start<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这个模块的启动其实就是用来启动服务的 先启动内部服务,再启动外部服务 |
| 内部服务有个元数据服务Dubbo3中每个服务都可以对外提供服务的元数据信息,来简化服务配置,不论是内部服务还是外部服务调用的代码逻辑都是模块发布器ModuleDeployer的start()方法,接下来我们详细看下模块发布器的生命周期函数。</p> |
| <h2 id="126-模块发布器发布服务的过程">12.6 模块发布器发布服务的过程</h2> |
| <p>前面我们说到了所有的服务都是经过模块发布器,ModuleDeployer的start()方法来启动的,那我们接下来就来看看这个模块发布器的启动方法。</p> |
| <p>ModuleDeployer的start()方法代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">synchronized</span> Future <span style="color:#268bd2">start</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块发布器已经停止或者启动失败则直接抛出异常返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStopping<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isStopped<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isFailed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is stopping or stopped, can not start again&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动中或者已经启动了则直接返回一个Future对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isStarting<span style="color:#719e07">()</span> <span style="color:#719e07">||</span> isStarted<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//切换模块启动状态为STARTING |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onModuleStarting<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// initialize |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果应用未初始化则初始化(非正常逻辑) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块发布器进行初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// export services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//暴露服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> exportServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// prepare application instance |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// exclude internal module to avoid wait itself |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>moduleModel <span style="color:#719e07">!=</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getInternalModule<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer<span style="color:#719e07">.</span>prepareInternalModule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// refer services |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//引用服务 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> referServices<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if no async export/refer services, just set started |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//非异步启动则直接切换状态为STARTED |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>asyncExportingFutures<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">()</span> <span style="color:#719e07">&amp;&amp;</span> asyncReferringFutures<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> onModuleStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果是异步的则等待服务发布和服务引用异步回调 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> frameworkExecutorRepository<span style="color:#719e07">.</span>getSharedExecutor<span style="color:#719e07">().</span>submit<span style="color:#719e07">(()</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// wait for export finish |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> waitExportFinish<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// wait for refer finish |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> waitReferFinish<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;wait for export/refer services occurred an exception&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">finally</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//异步回调完成 所有服务都启动了,再切换状态 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> onModuleStarted<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> onModuleFailed<span style="color:#719e07">(</span>getIdentifier<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; start failed: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>好了整体的服务启动生命周期就如上代码,后续我们再详细来看每个细节。</p> |
| <h2 id="127-发布器简介">12.7 发布器简介</h2> |
| <p>前面主要说了应用和模块的发布器的启动和初始化,下面简单了解下它们的关系,如下所示 |
| <img src="https://img-blog.csdnimg.cn/37e7c05796ab4b38aa7658377e16c0aa.png" alt="在这里插入图片描述"> |
| 可以发布器主要包含</p> |
| <ul> |
| <li>应用的发布器ApplicationDeployer用于初始化并启动应用程序实例</li> |
| <li>模块发布器ModuleDeployer 模块(服务)的导出/引用服务</li> |
| </ul> |
| <p>两种发布器有各自的接口,他们都继承了抽象的发布器AbstractDeployer 封装了一些公共的操作比如状态切换,状态查询的逻辑。</p> |
| <p>另外我们再来看下发布过程的状态枚举DeployState如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">enum</span> DeployState <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Unknown state |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> UNKNOWN<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Pending, wait for start |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> PENDING<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Starting |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> STARTING<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Started |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> STARTED<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Stopping |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> STOPPING<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Stopped |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> STOPPED<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Failed |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> FAILED |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>Dubbo这一块后续可以优化以下,这里的状态切换全部耦合在一起了,可以考虑使用状态机将状态与行为解耦。</p> |
| <p>原文:<a href="https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/">Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</a></p></description></item><item><title>Blog: 11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/11/11-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E5%8D%8F%E8%AE%AE%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFprotocolconfig/</link><pubDate>Thu, 11 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/11/11-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E5%8D%8F%E8%AE%AE%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFprotocolconfig/</guid><description> |
| <h1 id="11-dubbo启动器dubbobootstrap添加协议配置信息protocolconfig">11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</h1> |
| <h2 id="111-简介">11.1 简介</h2> |
| <p>先贴个代码用来参考:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>上个博客我们说了 RegistryConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码协议配置信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span></code></pre></div><h2 id="112--协议的配置相关">11.2 协议的配置相关</h2> |
| <p>下面的配置来源于官网</p> |
| <p>服务提供者协议配置。对应的配置类: org.apache.dubbo.config.ProtocolConfig。同时,如果需要支持多协议,可以声明多个 <a href="dubbo:protocol">dubbo:protocol</a> 标签,并在 <a href="dubbo:service">dubbo:service</a> 中通过 protocol 属性指定使用的协议。</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>id</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>配置关联</td> |
| <td>协议BeanId,可以在&lt;dubbo:service protocol=&quot;&quot;&gt;中引用此ID,如果ID不填,缺省和name属性值一样,重复则在name后加序号。</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>name</td> |
| <td><protocol></td> |
| <td>string</td> |
| <td><strong>必填</strong></td> |
| <td>dubbo</td> |
| <td>性能调优</td> |
| <td>协议名称</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>port</td> |
| <td><port></td> |
| <td>int</td> |
| <td>可选</td> |
| <td>dubbo协议缺省端口为20880,rmi协议缺省端口为1099,http和hessian协议缺省端口为80;如果<strong>没有</strong>配置port,则自动采用默认端口,如果配置为**-1**,则会分配一个没有被占用的端口。Dubbo 2.4.0+,分配的端口在协议缺省端口的基础上增长,确保端口段可控。</td> |
| <td>服务发现</td> |
| <td>服务端口</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>host</td> |
| <td><host></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>自动查找本机IP</td> |
| <td>服务发现</td> |
| <td>-服务主机名,多网卡选择或指定VIP及域名时使用,为空则自动查找本机IP,-建议不要配置,让Dubbo自动获取本机IP</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>threadpool</td> |
| <td>threadpool</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>fixed</td> |
| <td>性能调优</td> |
| <td>线程池类型,可选:fixed/cached</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>threads</td> |
| <td>threads</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>200</td> |
| <td>性能调优</td> |
| <td>服务线程池大小(固定大小)</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>iothreads</td> |
| <td>threads</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>cpu个数+1</td> |
| <td>性能调优</td> |
| <td>io线程池大小(固定大小)</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>accepts</td> |
| <td>accepts</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>服务提供方最大可接受连接数</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>payload</td> |
| <td>payload</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>8388608(=8M)</td> |
| <td>性能调优</td> |
| <td>请求及响应数据包大小限制,单位:字节</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>codec</td> |
| <td>codec</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>性能调优</td> |
| <td>协议编码方式</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>serialization</td> |
| <td>serialization</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为hessian2,rmi协议缺省为java,http协议缺省为json</td> |
| <td>性能调优</td> |
| <td>协议序列化方式,当协议支持多种序列化方式时使用,比如:dubbo协议的dubbo,hessian2,java,compactedjava,以及http协议的json等</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>accesslog</td> |
| <td>accesslog</td> |
| <td>string/boolean</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>path</td> |
| <td><path></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>提供者上下文路径,为服务path的前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>transporter</td> |
| <td>transporter</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为netty</td> |
| <td>性能调优</td> |
| <td>协议的服务端和客户端实现类型,比如:dubbo协议的mina,netty等,可以分拆为server和client配置</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>server</td> |
| <td>server</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为netty,http协议缺省为servlet</td> |
| <td>性能调优</td> |
| <td>协议的服务器端实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>client</td> |
| <td>client</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为netty</td> |
| <td>性能调优</td> |
| <td>协议的客户端实现类型,比如:dubbo协议的mina,netty等</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>dispatcher</td> |
| <td>dispatcher</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo协议缺省为all</td> |
| <td>性能调优</td> |
| <td>协议的消息派发方式,用于指定线程模型,比如:dubbo协议的all, direct, message, execution, connection等</td> |
| <td>2.1.0以上版本</td> |
| </tr> |
| <tr> |
| <td>queues</td> |
| <td>queues</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>线程池队列大小,当线程池满时,排队等待执行的队列大小,建议不要设置,当线程池满时应立即失败,重试其它服务提供机器,而不是排队,除非有特殊需求。</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>charset</td> |
| <td>charset</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>UTF-8</td> |
| <td>性能调优</td> |
| <td>序列化编码</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>buffer</td> |
| <td>buffer</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>8192</td> |
| <td>性能调优</td> |
| <td>网络读写缓冲区大小</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>heartbeat</td> |
| <td>heartbeat</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>心跳间隔,对于长连接,当物理层断开时,比如拔网线,TCP的FIN消息来不及发送,对方收不到断开事件,此时需要心跳来帮助检查连接是否已断开</td> |
| <td>2.0.10以上版本</td> |
| </tr> |
| <tr> |
| <td>telnet</td> |
| <td>telnet</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>所支持的telnet命令,多个命令用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>register</td> |
| <td>register</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>该协议的服务是否注册到注册中心</td> |
| <td>2.0.8以上版本</td> |
| </tr> |
| <tr> |
| <td>contextpath</td> |
| <td>contextpath</td> |
| <td>String</td> |
| <td>可选</td> |
| <td>缺省为空串</td> |
| <td>服务治理</td> |
| <td></td> |
| <td>2.0.6以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>变量</th> |
| <th>类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>threadname</td> |
| <td>String</td> |
| <td>线程池名称</td> |
| </tr> |
| <tr> |
| <td>corethreads</td> |
| <td>Integer</td> |
| <td>线程池核心线程大小</td> |
| </tr> |
| <tr> |
| <td>alive</td> |
| <td>Integer</td> |
| <td>线程池keepAliveTime,默认单位时间单位。毫秒</td> |
| </tr> |
| <tr> |
| <td>exchanger</td> |
| <td>String</td> |
| <td>交换器配置信息如何交换</td> |
| </tr> |
| <tr> |
| <td>prompt</td> |
| <td>String</td> |
| <td>命令行提示符</td> |
| </tr> |
| <tr> |
| <td>status</td> |
| <td>String</td> |
| <td>状态检查</td> |
| </tr> |
| <tr> |
| <td>sslEnabled</td> |
| <td>Boolean</td> |
| <td>ssl是否启用</td> |
| </tr> |
| </tbody> |
| </table> |
| <h2 id="113-协议配置对象创建与添加">11.3 协议配置对象创建与添加</h2> |
| <p>前面例子中调用的代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span></code></pre></div><p>这里我们配置了协议类型为dubbo 端口为-1则会分配一个没有被占用的端口</p> |
| <p>继续看下DubboBootstrap的protocol方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">protocol</span><span style="color:#719e07">(</span>ProtocolConfig protocolConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置信息转List |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> protocols<span style="color:#719e07">(</span>singletonList<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看protocols方法 ,这个代码与前面两个博客中看到的向配置管理器添加配置对象的逻辑是一样的 |
| 这里就不说了可以看前面的博客<a href="https://blog.elastic.link/2022/07/10/dubbo/9-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-ying-yong-cheng-xu-de-pei-zhi-xin-xi-applicationconfig/">《9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig》</a></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">protocols</span><span style="color:#719e07">(</span>List<span style="color:#719e07">&lt;</span>ProtocolConfig<span style="color:#719e07">&gt;</span> protocolConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>protocolConfigs<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ProtocolConfig protocolConfig <span style="color:#719e07">:</span> protocolConfigs<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> protocolConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> configManager<span style="color:#719e07">.</span>addProtocol<span style="color:#719e07">(</span>protocolConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文:<a href="https://blog.elastic.link/2022/07/10/dubbo/11-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-xie-yi-pei-zhi-xin-xi-protocolconfig/">Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</a></p></description></item><item><title>Blog: 10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/10/10-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFregistryconfig/</link><pubDate>Wed, 10 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/10/10-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFregistryconfig/</guid><description> |
| <h1 id="10-dubbo启动器dubbobootstrap添加注册中心配置信息registryconfig">10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig</h1> |
| <h2 id="101-简介">10.1 简介</h2> |
| <p>先贴个代码用来参考:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>上个博客我们说了启动器ApplicationConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码注册中心配置信息:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span></code></pre></div><h2 id="102--注册中心的配置相关">10.2 注册中心的配置相关</h2> |
| <p>下面的配置来源于官网</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>id</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>配置关联</td> |
| <td>注册中心引用BeanId,可以在&lt;dubbo:service registry=&quot;&quot;&gt;或&lt;dubbo:reference registry=&quot;&quot;&gt;中引用此ID</td> |
| <td>1.0.16以上版本</td> |
| </tr> |
| <tr> |
| <td>address</td> |
| <td><a href="host:port">host:port</a></td> |
| <td>string</td> |
| <td><strong>必填</strong></td> |
| <td></td> |
| <td>服务发现</td> |
| <td>注册中心服务器地址,如果地址没有端口缺省为9090,同一集群内的多个地址用逗号分隔,如:ip:port,ip:port,不同集群的注册中心,请配置多个<a href="dubbo:registry">dubbo:registry</a>标签</td> |
| <td>1.0.16以上版本</td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td><protocol></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>服务发现</td> |
| <td>注册中心地址协议,支持<code>dubbo</code>, <code>multicast</code>, <code>zookeeper</code>, <code>redis</code>, <code>consul(2.7.1)</code>, <code>sofa(2.7.2)</code>, <code>etcd(2.7.2)</code>, <code>nacos(2.7.2)</code>等协议</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>port</td> |
| <td><port></td> |
| <td>int</td> |
| <td>可选</td> |
| <td>9090</td> |
| <td>服务发现</td> |
| <td>注册中心缺省端口,当address没有带端口时使用此端口做为缺省值</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>username</td> |
| <td><username></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>登录注册中心用户名,如果注册中心不需要验证可不填</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>password</td> |
| <td><password></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>登录注册中心密码,如果注册中心不需要验证可不填</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>transport</td> |
| <td>registry.transporter</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>netty</td> |
| <td>性能调优</td> |
| <td>网络传输方式,可选mina,netty</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>registry.timeout</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>5000</td> |
| <td>性能调优</td> |
| <td>注册中心请求超时时间(毫秒)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>session</td> |
| <td>registry.session</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>60000</td> |
| <td>性能调优</td> |
| <td>注册中心会话超时时间(毫秒),用于检测提供者非正常断线后的脏数据,比如用心跳检测的实现,此时间就是心跳间隔,不同注册中心实现不一样。</td> |
| <td>2.1.0以上版本</td> |
| </tr> |
| <tr> |
| <td>file</td> |
| <td>registry.file</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>使用文件缓存注册中心地址列表及服务提供者列表,应用重启时将基于此文件恢复,注意:两个注册中心不能使用同一文件存储</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>wait</td> |
| <td>registry.wait</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>停止时等待通知完成时间(毫秒)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>check</td> |
| <td>check</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>注册中心不存在时,是否报错</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>register</td> |
| <td>register</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>是否向此注册中心注册服务,如果设为false,将只订阅,不注册</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>subscribe</td> |
| <td>subscribe</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>是否向此注册中心订阅服务,如果设为false,将只注册,不订阅</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>dynamic</td> |
| <td>dynamic</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>服务是否动态注册,如果设为false,注册后将显示为disable状态,需人工启用,并且服务提供者停止时,也不会自动取消注册,需人工禁用。</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>group</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>服务治理</td> |
| <td>服务注册分组,跨组的服务不会相互影响,也无法相互调用,适用于环境隔离。</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>simplified</td> |
| <td>simplified</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>注册到注册中心的URL是否采用精简模式的(与低版本兼容)</td> |
| <td>2.7.0以上版本</td> |
| </tr> |
| <tr> |
| <td>extra-keys</td> |
| <td>extraKeys</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>在simplified=true时,extraKeys允许你在默认参数外将额外的key放到URL中,格式:“interface,key1,key2”。</td> |
| <td>2.7.0以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>变量</th> |
| <th>类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>server</td> |
| <td>String</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>client</td> |
| <td>String</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>String</td> |
| <td>影响流量在注册中心之间的分布,在订阅多个注册中心时很有用,可用选项:1。区域感知,特定类型的流量总是根据流量的来源进入一个注册表。</td> |
| </tr> |
| <tr> |
| <td>zone</td> |
| <td>String</td> |
| <td>注册表所属的区域,通常用于隔离流量</td> |
| </tr> |
| <tr> |
| <td>parameters</td> |
| <td>Map&lt;String, String&gt;</td> |
| <td>自定义参数</td> |
| </tr> |
| <tr> |
| <td>useAsConfigCenter</td> |
| <td>Boolean</td> |
| <td>该地址是否用作配置中心</td> |
| </tr> |
| <tr> |
| <td>useAsMetadataCenter</td> |
| <td>Boolean</td> |
| <td>该地址是否用作远程元数据中心</td> |
| </tr> |
| <tr> |
| <td>accepts</td> |
| <td>String</td> |
| <td>此注册表接受的rpc协议列表,例如“dubbo,rest”</td> |
| </tr> |
| <tr> |
| <td>preferred</td> |
| <td>Boolean</td> |
| <td>如果设置为true,则始终首先使用此注册表,这在订阅多个注册表时非常有用</td> |
| </tr> |
| <tr> |
| <td>weight</td> |
| <td>Integer</td> |
| <td>影响注册中心之间的流量分布,当订阅多个注册中心仅在未指定首选注册中心时才生效时,此功能非常有用。</td> |
| </tr> |
| <tr> |
| <td>registerMode</td> |
| <td>String</td> |
| <td>注册模式:实例级,接口级,所有</td> |
| </tr> |
| <tr> |
| <td>enableEmptyProtection</td> |
| <td>Boolean</td> |
| <td>收到的空url地址列表和空保护被禁用,将清除当前可用地址</td> |
| </tr> |
| </tbody> |
| </table> |
| <h2 id="103-注册中心配置对象创建与添加">10.3 注册中心配置对象创建与添加</h2> |
| <p>前面例子中调用的代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span></code></pre></div><p>首先我们要来看的是RegistryConfig类型的构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">RegistryConfig</span><span style="color:#719e07">(</span>String address<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> setAddress<span style="color:#719e07">(</span>address<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看setAddress方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setAddress</span><span style="color:#719e07">(</span>String address<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//保存地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>address <span style="color:#719e07">=</span> address<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//下面是支持将参数在url地址后面 比如用户名,密码,协议,端口,这几个参数提前做解析放入成员变量中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>address <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//地址转Dubbo的URL对象 这个URL是Dubbo自行实现的URL封装信息的类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> URL url <span style="color:#719e07">=</span> URL<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>address<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Refactor since 2.7.8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//值不存在时候更新属性,非常巧妙的代码 重构了多个if判断 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//第一个参数值不存在则调用第二个方法,第二个方法的参数为第三方方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> updatePropertyIfAbsent<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>getUsername<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">::</span>setUsername<span style="color:#719e07">,</span> url<span style="color:#719e07">.</span>getUsername<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>getPassword<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">::</span>setPassword<span style="color:#719e07">,</span> url<span style="color:#719e07">.</span>getPassword<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>getProtocol<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">::</span>setProtocol<span style="color:#719e07">,</span> url<span style="color:#719e07">.</span>getProtocol<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">::</span>getPort<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">::</span>setPort<span style="color:#719e07">,</span> url<span style="color:#719e07">.</span>getPort<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//移除掉url中的backup自定义参数 (备份的注册中心地址) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> params <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameters<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmptyMap<span style="color:#719e07">(</span>params<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> params<span style="color:#719e07">.</span>remove<span style="color:#719e07">(</span>BACKUP_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将自定义参数存储到成员变量中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> updateParameters<span style="color:#719e07">(</span>params<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception ignored<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>然后再回过头来看DubboBootstrap的registry方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">registry</span><span style="color:#719e07">(</span>RegistryConfig registryConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将applicationModel对象设置给注册中心配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> registryConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将注册中心配置对象添加到配置管理器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>addRegistry<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>直接来看配置管理器configManager的添加注册中心配置addRegistry方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addRegistry</span><span style="color:#719e07">(</span>RegistryConfig registryConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> addConfig<span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>configManager 的addConfig方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">final</span> <span style="color:#719e07">&lt;</span>T <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> T <span style="color:#268bd2">addConfig</span><span style="color:#719e07">(</span>AbstractConfig config<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore MethodConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//检查当前配置管理器支持管理的配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isSupportConfigType<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unsupported config type: &#34;</span> <span style="color:#719e07">+</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> config<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中是否存在 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> AbstractConfig<span style="color:#719e07">&gt;</span> configsMap <span style="color:#719e07">=</span> configsCache<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>getTagName<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()),</span> type <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不是服务级接口配置则直接从缓存中读取到配置之后直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// fast check duplicated equivalent config before write lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!(</span>config <span style="color:#719e07">instanceof</span> ReferenceConfigBase <span style="color:#719e07">||</span> config <span style="color:#719e07">instanceof</span> ServiceConfigBase<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>AbstractConfig value <span style="color:#719e07">:</span> configsMap<span style="color:#719e07">.</span>values<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>value<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>config<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> value<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// lock by config type |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//添加配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>configsMap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> addIfAbsent<span style="color:#719e07">(</span>config<span style="color:#719e07">,</span> configsMap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigManager配置管理器的addIfAbsent方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>C <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> C <span style="color:#268bd2">addIfAbsent</span><span style="color:#719e07">(</span>C config<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> C<span style="color:#719e07">&gt;</span> configsMap<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置信息为空直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> configsMap <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// find by value |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//根据配置规则判断,配置存在则返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Optional<span style="color:#719e07">&lt;</span>C<span style="color:#719e07">&gt;</span> prevConfig <span style="color:#719e07">=</span> findDuplicatedConfig<span style="color:#719e07">(</span>configsMap<span style="color:#719e07">,</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>prevConfig<span style="color:#719e07">.</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> prevConfig<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成配置key |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>getId<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>key <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">do</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generate key if id is not set |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> key <span style="color:#719e07">=</span> generateConfigId<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>configsMap<span style="color:#719e07">.</span>containsKey<span style="color:#719e07">(</span>key<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不相同的配置key重复则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> C existedConfig <span style="color:#719e07">=</span> configsMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>existedConfig <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isEquals<span style="color:#719e07">(</span>existedConfig<span style="color:#719e07">,</span> config<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String type <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">().</span>getSimpleName<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Duplicate %s found, there already has one default %s or more than two %ss have the same id, &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;you can try to give each %s a different id, override previous config with later config. id: %s, prev: %s, later: %s&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> existedConfig<span style="color:#719e07">,</span> config<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// override existed config if any |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将配置对象存入configsMap对象中,configsMap来源于configsCache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configsMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/10-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-zhu-ce-zhong-xin-pei-zhi-xin-xi-registryconfig//">&laquo;Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig&raquo;</a></p></description></item><item><title>Blog: 9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/09/9-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%9A%84%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFapplicationconfig/</link><pubDate>Tue, 09 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/09/9-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E6%B7%BB%E5%8A%A0%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%9A%84%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AFapplicationconfig/</guid><description> |
| <h1 id="9-dubbo启动器dubbobootstrap添加应用程序的配置信息applicationconfig">9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig</h1> |
| <h2 id="91-简介">9.1 简介</h2> |
| <p>先贴个代码用来参考:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>上个博客我们说了启动器对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span></code></pre></div><h2 id="92-应用程序applicationconfig的配置信息">9.2 应用程序ApplicationConfig的配置信息</h2> |
| <p>ApplicationConfig的构造器比较简单就是为他的成员变量name赋值来标识这个应用程序的名字 |
| 下面我们直接参考下官网的配置表格:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>name</td> |
| <td>application</td> |
| <td>string</td> |
| <td><strong>必填</strong></td> |
| <td></td> |
| <td>服务治理</td> |
| <td>当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样,此参数不是匹配条件,你当前项目叫什么名字就填什么,和提供者消费者角色无关,比如:kylin应用调用了morgan应用的服务,则kylin项目配成kylin,morgan项目配成morgan,可能kylin也提供其它服务给别人使用,但kylin项目永远配成kylin,这样注册中心将显示kylin依赖于morgan</td> |
| <td>1.0.16以上版本</td> |
| </tr> |
| <tr> |
| <td>version</td> |
| <td>application.version</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>当前应用的版本</td> |
| <td>2.2.0以上版本</td> |
| </tr> |
| <tr> |
| <td>owner</td> |
| <td>owner</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>应用负责人,用于服务治理,请填写负责人公司邮箱前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>organization</td> |
| <td>organization</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>组织名称(BU或部门),用于注册中心区分服务来源,此配置项建议不要使用autoconfig,直接写死在配置中,比如china,intl,itu,crm,asc,dw,aliexpress等</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>architecture</td> |
| <td>architecture</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>用于服务分层对应的架构。如,intl、china。不同的架构使用不同的分层。</td> |
| <td>2.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>environment</td> |
| <td>environment</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>应用环境,如:develop/test/product,不同环境使用不同的缺省值,以及作为只用于开发测试功能的限制条件</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>compiler</td> |
| <td>compiler</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>javassist</td> |
| <td>性能优化</td> |
| <td>Java字节码编译器,用于动态类的生成,可选:jdk或javassist</td> |
| <td>2.1.0以上版本</td> |
| </tr> |
| <tr> |
| <td>logger</td> |
| <td>logger</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>slf4j</td> |
| <td>性能优化</td> |
| <td>日志输出方式,可选:slf4j,jcl,log4j,log4j2,jdk</td> |
| <td>2.2.0以上版本</td> |
| </tr> |
| <tr> |
| <td>metadata-type</td> |
| <td>metadata-type</td> |
| <td>String</td> |
| <td>可选</td> |
| <td>local</td> |
| <td>服务治理</td> |
| <td>metadata 传递方式,是以 Provider 视角而言的,Consumer 侧配置无效,可选值有: remote - Provider 把 metadata 放到远端注册中心,Consumer 从注册中心获取 local - Provider 把 metadata 放在本地,Consumer 从 Provider 处直接获取</td> |
| <td>2.7.6以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>官网的配置很详细了上面有一些属性是值得注意的比如这个name,compiler,logger,metadata-type 我们可能要多看下默认值是什么,方便我们在使用过程中遇到问题的排查</p> |
| <p>常用的属性参考官网的表格已经足够了,不过上面的属性不是列举了所有的属性,后续应该官方文档回更新: |
| 我这里把缺失的一些属性列举出来:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>变量</th> |
| <th>类型</th> |
| <th>说明</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>registries</td> |
| <td>List<RegistryConfig></td> |
| <td>应用级注册中心列表</td> |
| </tr> |
| <tr> |
| <td>registryIds</td> |
| <td>String</td> |
| <td>注册中心id列表</td> |
| </tr> |
| <tr> |
| <td>monitor</td> |
| <td>MonitorConfig</td> |
| <td>应用级监控配置</td> |
| </tr> |
| <tr> |
| <td>dumpDirectory</td> |
| <td>String</td> |
| <td>保存线程转储的目录</td> |
| </tr> |
| <tr> |
| <td>qosEnable</td> |
| <td>Boolean</td> |
| <td>是否启用qos</td> |
| </tr> |
| <tr> |
| <td>qosHost</td> |
| <td>String</td> |
| <td>要侦听的qos主机地址</td> |
| </tr> |
| <tr> |
| <td>qosPort</td> |
| <td>Integer</td> |
| <td>要侦听的qos端口</td> |
| </tr> |
| <tr> |
| <td>qosAcceptForeignIp</td> |
| <td>Boolean</td> |
| <td>qos是否接收外部IP</td> |
| </tr> |
| <tr> |
| <td>parameters</td> |
| <td>Map&lt;String, String&gt;</td> |
| <td>自定义参数</td> |
| </tr> |
| <tr> |
| <td>shutwait</td> |
| <td>String</td> |
| <td>应用程序关闭时间</td> |
| </tr> |
| <tr> |
| <td>hostname</td> |
| <td>String</td> |
| <td>主机名</td> |
| </tr> |
| <tr> |
| <td>registerConsumer</td> |
| <td>Boolean</td> |
| <td>用于控制是否将实例注册到注册表。仅当实例是纯消费者时才设置为“false”。</td> |
| </tr> |
| <tr> |
| <td>repository</td> |
| <td>String</td> |
| <td>没找到哪里用了</td> |
| </tr> |
| <tr> |
| <td>enableFileCache</td> |
| <td>Boolean</td> |
| <td>是否开启本地文件缓存</td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td>String</td> |
| <td>此应用程序的首选协议(名称)适用于难以确定哪个是首选协议的地方</td> |
| </tr> |
| <tr> |
| <td>metadataServiceProtocol</td> |
| <td>String</td> |
| <td>用于点对点的元数据传输的协议</td> |
| </tr> |
| <tr> |
| <td>metadataServicePort</td> |
| <td>Integer</td> |
| <td>元数据服务端口号,用于服务发现</td> |
| </tr> |
| <tr> |
| <td>livenessProbe</td> |
| <td>String</td> |
| <td>Liveness 存活探针 用于设置qos中探测器的扩展</td> |
| </tr> |
| <tr> |
| <td>readinessProbe</td> |
| <td>String</td> |
| <td>Readiness 就绪探针</td> |
| </tr> |
| <tr> |
| <td>startupProbe</td> |
| <td>String</td> |
| <td>Startup 启动探针</td> |
| </tr> |
| <tr> |
| <td>registerMode</td> |
| <td>String</td> |
| <td>注册模式,实例级,接口集,所有</td> |
| </tr> |
| <tr> |
| <td>enableEmptyProtection</td> |
| <td>Boolean</td> |
| <td>接收到的空url地址列表和空保护被禁用,将清除当前可用地址</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>这里我们先来简单了解下这个实体类型的基本配置,直接看配置可能不太好理解,后面我们讲到每个配置的时候可以回来参考一下</p> |
| <h2 id="应用程序配置对象添加到启动器中的配置管理器中">应用程序配置对象添加到启动器中的配置管理器中</h2> |
| <p>了解了配置信息再回过头来看下这个配置信息如何存放到启动器里面的:</p> |
| <p>我们的Demo调用代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span></code></pre></div><p>DubboBootstrap的application方法设置一个应用程序配置ApplicationConfig对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> DubboBootstrap <span style="color:#268bd2">application</span><span style="color:#719e07">(</span>ApplicationConfig applicationConfig<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将启动器构造器中初始化的默认应用程序模型对象传递给配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置信息添加到配置管理器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager<span style="color:#719e07">.</span>setApplication<span style="color:#719e07">(</span>applicationConfig<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigManager配置管理器的setApplication方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@DisableInject</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setApplication</span><span style="color:#719e07">(</span>ApplicationConfig application<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> addConfig<span style="color:#719e07">(</span>application<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigManager配置管理器的addConfig方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">final</span> <span style="color:#719e07">&lt;</span>T <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> T <span style="color:#268bd2">addConfig</span><span style="color:#719e07">(</span>AbstractConfig config<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore MethodConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//检查当前配置管理器支持管理的配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isSupportConfigType<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unsupported config type: &#34;</span> <span style="color:#719e07">+</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> config<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中是否存在 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> AbstractConfig<span style="color:#719e07">&gt;</span> configsMap <span style="color:#719e07">=</span> configsCache<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>getTagName<span style="color:#719e07">(</span>config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">()),</span> type <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fast check duplicated equivalent config before write lock |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//不是服务级配置则直接从缓存中读取到配置之后直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!(</span>config <span style="color:#719e07">instanceof</span> ReferenceConfigBase <span style="color:#719e07">||</span> config <span style="color:#719e07">instanceof</span> ServiceConfigBase<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>AbstractConfig value <span style="color:#719e07">:</span> configsMap<span style="color:#719e07">.</span>values<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>value<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>config<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> value<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// lock by config type |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//添加配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>configsMap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> addIfAbsent<span style="color:#719e07">(</span>config<span style="color:#719e07">,</span> configsMap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ConfigManager配置管理器的addIfAbsent方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>C <span style="color:#268bd2">extends</span> AbstractConfig<span style="color:#719e07">&gt;</span> C <span style="color:#268bd2">addIfAbsent</span><span style="color:#719e07">(</span>C config<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> C<span style="color:#719e07">&gt;</span> configsMap<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置信息为空直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> configsMap <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// find by value |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//根据配置规则判断,配置存在则返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Optional<span style="color:#719e07">&lt;</span>C<span style="color:#719e07">&gt;</span> prevConfig <span style="color:#719e07">=</span> findDuplicatedConfig<span style="color:#719e07">(</span>configsMap<span style="color:#719e07">,</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>prevConfig<span style="color:#719e07">.</span>isPresent<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> prevConfig<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成配置key |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String key <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>getId<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>key <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">do</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// generate key if id is not set |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> key <span style="color:#719e07">=</span> generateConfigId<span style="color:#719e07">(</span>config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>configsMap<span style="color:#719e07">.</span>containsKey<span style="color:#719e07">(</span>key<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不相同的配置key重复则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> C existedConfig <span style="color:#719e07">=</span> configsMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>existedConfig <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isEquals<span style="color:#719e07">(</span>existedConfig<span style="color:#719e07">,</span> config<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String type <span style="color:#719e07">=</span> config<span style="color:#719e07">.</span>getClass<span style="color:#719e07">().</span>getSimpleName<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>format<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Duplicate %s found, there already has one default %s or more than two %ss have the same id, &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;you can try to give each %s a different id, override previous config with later config. id: %s, prev: %s, later: %s&#34;</span><span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> type<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> existedConfig<span style="color:#719e07">,</span> config<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// override existed config if any |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将配置对象存入configsMap对象中,configsMap来源于configsCache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configsMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>key<span style="color:#719e07">,</span> config<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/9-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-ying-yong-cheng-xu-de-pei-zhi-xin-xi-applicationconfig/">&laquo;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&raquo;</a></p></description></item><item><title>Blog: 8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/08/8-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E5%80%9F%E5%8A%A9%E5%8F%8C%E9%87%8D%E6%A0%A1%E9%AA%8C%E9%94%81%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E8%BF%9B%E8%A1%8C%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96/</link><pubDate>Mon, 08 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/08/8-dubbo%E5%90%AF%E5%8A%A8%E5%99%A8dubbobootstrap%E5%80%9F%E5%8A%A9%E5%8F%8C%E9%87%8D%E6%A0%A1%E9%AA%8C%E9%94%81%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E8%BF%9B%E8%A1%8C%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96/</guid><description> |
| <h1 id="8-dubbo启动器dubbobootstrap借助双重校验锁的单例模式进行对象的初始化">8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化</h1> |
| <h2 id="81-启动器简介">8.1 启动器简介</h2> |
| <p>在说启动器之前先把视野拉回第一章<a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">《1-从一个服务提供者的Demo说起》</a>我们的Demo代码,下面只贴一下核心代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">Application</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> service<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DemoServiceImpl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一个篇章主要说这里: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>Dubbo3 往云原生的方向走自然要针对云原生应用的应用启动,应用运行,应用发布等信息做一些建模,这个DubboBootstrap就是用来启动Dubbo服务的.类似于Netty的Bootstrap类型和ServerBootstrap启动器</p> |
| <h2 id="82-双重校验锁的单例模式创建启动器对象的">8.2 双重校验锁的单例模式创建启动器对象的</h2> |
| <p>Dubbo的bootstrap类为啥要用单例模式:</p> |
| <p>通过调用静态方法getInstance()获取单例实例。之所以设计为单例,是因为Dubbo中的一些类(如ExtensionLoader)只为每个进程设计一个实例。</p> |
| <p>下面就来直接看代码吧,代码胜千言: |
| 对象的调用代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>DubboBootstrap获取对象的getInstance()方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> DubboBootstrap <span style="color:#268bd2">getInstance</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁第一次判断空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为空都进行排队 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>DubboBootstrap<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁第二次判断空 上面为空的都排队了这里得判断下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用重载方法获取对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">(</span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>DubboBootstrap获取对象重载的getInstance(ApplicationModel applicationModel)方法:</p> |
| <p><em>computeIfAbsent() 方法对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hashMap 中。</em></p> |
| <p>instanceMap设计为Map&lt;ApplicationModel, DubboBootstrap&gt;类型 Key,意味着可以为多个应用程序模型创建不同的启动器,启动多个服务</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> DubboBootstrap <span style="color:#268bd2">getInstance</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instanceMap<span style="color:#719e07">.</span>computeIfAbsent<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> _k <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> DubboBootstrap<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="83-dubbobootstrap的构造器代码">8.3 DubboBootstrap的构造器代码</h2> |
| <p>构造器代码是逻辑比较复杂的地方,我们先来看下代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">DubboBootstrap</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存储应用程序启动模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置管理器ConfigManager: 配置管理器的扩展类型ApplicationExt ,扩展名字config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取环境信息Environment: 环境信息的扩展类型为ApplicationExt,扩展名字为environment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> environment <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getModelEnvironment<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//执行器存储仓库(线程池)ExecutorRepository: 扩展类型为ExecutorRepository,默认扩展扩展名字为default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> executorRepository <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getDefaultExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化并启动应用程序实例ApplicationDeployer,DefaultApplicationDeployer类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// listen deploy events |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//为发布器 设置生命周期回调 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationDeployer<span style="color:#719e07">.</span>addDeployListener<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DeployListenerAdapter<span style="color:#719e07">&lt;</span>ApplicationModel<span style="color:#719e07">&gt;()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onStarted</span><span style="color:#719e07">(</span>ApplicationModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> notifyStarted<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onStopped</span><span style="color:#719e07">(</span>ApplicationModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> notifyStopped<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onFailure</span><span style="color:#719e07">(</span>ApplicationModel scopeModel<span style="color:#719e07">,</span> Throwable cause<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> notifyStopped<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将启动器对象注册到应用程序模型applicationModel的Bean工厂中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// register DubboBootstrap bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">().</span>registerBean<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/8-dubbo-qi-dong-qi-dubbobootstrap-jie-zhu-shuang-chong-xiao-yan-suo-de-dan-li-mo-shi-jin-xing-dui-xiang-de-chu-shi-hua/">&laquo;Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化&raquo;</a></p></description></item><item><title>Blog: 7-Dubbo的SPI扩展机制之自动激活扩展Activate源码解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/07/7-dubbo%E7%9A%84spi%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E4%B9%8B%E8%87%AA%E5%8A%A8%E6%BF%80%E6%B4%BB%E6%89%A9%E5%B1%95activate%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</link><pubDate>Sun, 07 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/07/7-dubbo%E7%9A%84spi%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E4%B9%8B%E8%87%AA%E5%8A%A8%E6%BF%80%E6%B4%BB%E6%89%A9%E5%B1%95activate%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</guid><description> |
| <h1 id="7-dubbo的spi扩展机制之自动激活扩展activate源码解析">7-Dubbo的SPI扩展机制之自动激活扩展Activate源码解析</h1> |
| <h2 id="71-activate扩展的说明">7.1 Activate扩展的说明</h2> |
| <p>此注解对于使用给定条件自动激活某些扩展非常有用,例如:@Activate可用于在有多个实现时加载某些筛选器扩展。</p> |
| <ul> |
| <li><strong>group()</strong> 指定组条件。框架SPI定义了有效的组值。</li> |
| <li><strong>value()</strong> 指定URL条件中的参数键。</li> |
| </ul> |
| <p>SPI提供程序可以调用ExtensionLoader。getActivateExtension(URL、String、String)方法以查找具有给定条件的所有已激活扩展。</p> |
| <p>比如后面我们会说到的<strong>过滤器扩展对象</strong>的获取,如下通过调用<strong>getActivateExtension方法的</strong>代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Filter<span style="color:#719e07">&gt;</span> filters<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Filter<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> moduleModels<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">)).</span>getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> group<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><h2 id="72-获取自动激活扩展的源码">7.2 获取自动激活扩展的源码</h2> |
| <p>前面我们看了激活扩展是通过调用getActivateExtension方法来获取对象的,那接下来就来看下这个方法做了什么操作:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">* @param url 服务的url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">* @param key 用于获取扩展点名称的url参数键 比如监听器:exporter.listener,过滤器:params-filter,telnet处理器:telnet |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">*/</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getActivateExtension</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> String key<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> key<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续调用重载的方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param url 服务的url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param key 用于获取扩展点名称的url参数键 比如监听器:exporter.listener,过滤器:params-filter,telnet处理器:telnet |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param group group 用于筛选的分组,比如过滤器中使用此参数来区分消费者使用这个过滤器还是提供者使用这个过滤器他们的group参数分表为consumer,provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return 已激活的扩展列表。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getActivateExtension</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> String key<span style="color:#719e07">,</span> String group<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从参数中获取url指定的值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String value <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>getParameter<span style="color:#719e07">(</span>key<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用下个重载的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> getActivateExtension<span style="color:#719e07">(</span>url<span style="color:#719e07">,</span> StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>value<span style="color:#719e07">)</span> <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">:</span> COMMA_SPLIT_PATTERN<span style="color:#719e07">.</span>split<span style="color:#719e07">(</span>value<span style="color:#719e07">),</span> group<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面的重载方法都是用来转换参数的,下面这个方法才是真正的逻辑</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 获取激活扩展. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param url 服务的url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param values 这个value是扩展点的名字 当指定了时候会使用指定的名字的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param group group 用于筛选的分组,比如过滤器中使用此参数来区分消费者使用这个过滤器还是提供者使用这个过滤器他们的group参数分表为consumer,provider |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return 获取激活扩展. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> List<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getActivateExtension</span><span style="color:#719e07">(</span>URL url<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> values<span style="color:#719e07">,</span> String group<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查扩展加载器是否被销毁了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// solve the bug of using @SPI&#39;s wrapper method to report a null pointer exception. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//创建个有序的Map集合,用来对扩展进行排序 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>Class<span style="color:#719e07">&lt;?&gt;,</span> T<span style="color:#719e07">&gt;</span> activateExtensionsMap <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> TreeMap<span style="color:#719e07">&lt;&gt;(</span>activateComparator<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化扩展名字,指定了扩展名字values不为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> names <span style="color:#719e07">=</span> values <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>0<span style="color:#719e07">)</span> <span style="color:#719e07">:</span> asList<span style="color:#719e07">(</span>values<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展名字使用Set集合进行去重 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> namesSet <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashSet<span style="color:#719e07">&lt;&gt;(</span>names<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//参数常量是 -default 扩展名字是否不包含默认的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> DEFAULT_KEY<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次进来肯定是没有缓存对象双重校验锁检查下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedActivateGroups<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>cachedActivateGroups<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// cache all extensions |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedActivateGroups<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载扩展类型对应的扩展类,这个具体细节参考源码或者《[Dubbo3.0.7源码解析系列]-5-Dubbo的SPI扩展机制与自适应扩展对象的创建与扩展文件的扫描源码解析》章节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getExtensionClasses<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Map<span style="color:#719e07">.</span>Entry<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> entry <span style="color:#719e07">:</span> cachedActivates<span style="color:#719e07">.</span>entrySet<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> entry<span style="color:#719e07">.</span>getKey<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Object activate <span style="color:#719e07">=</span> entry<span style="color:#719e07">.</span>getValue<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> activateGroup<span style="color:#719e07">,</span> activateValue<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的activates列表获取group()和value()值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>activate <span style="color:#719e07">instanceof</span> Activate<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> activateGroup <span style="color:#719e07">=</span> <span style="color:#719e07">((</span>Activate<span style="color:#719e07">)</span> activate<span style="color:#719e07">).</span>group<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> activateValue <span style="color:#719e07">=</span> <span style="color:#719e07">((</span>Activate<span style="color:#719e07">)</span> activate<span style="color:#719e07">).</span>value<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>activate <span style="color:#719e07">instanceof</span> com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> activateGroup <span style="color:#719e07">=</span> <span style="color:#719e07">((</span>com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate<span style="color:#719e07">)</span> activate<span style="color:#719e07">).</span>group<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> activateValue <span style="color:#719e07">=</span> <span style="color:#719e07">((</span>com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate<span style="color:#719e07">)</span> activate<span style="color:#719e07">).</span>value<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存分组值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivateGroups<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> HashSet<span style="color:#719e07">&lt;&gt;(</span>Arrays<span style="color:#719e07">.</span>asList<span style="color:#719e07">(</span>activateGroup<span style="color:#719e07">)));</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[][]</span> keyPairs <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[</span>activateValue<span style="color:#719e07">.</span>length<span style="color:#719e07">][];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历指定的激活扩展的扩展名字列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0<span style="color:#719e07">;</span> i <span style="color:#719e07">&lt;</span> activateValue<span style="color:#719e07">.</span>length<span style="color:#719e07">;</span> i<span style="color:#719e07">++)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>activateValue<span style="color:#719e07">[</span>i<span style="color:#719e07">].</span>contains<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;:&#34;</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[</span>2<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> arr <span style="color:#719e07">=</span> activateValue<span style="color:#719e07">[</span>i<span style="color:#719e07">].</span>split<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;:&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">][</span>0<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> arr<span style="color:#719e07">[</span>0<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">][</span>1<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> arr<span style="color:#719e07">[</span>1<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> String<span style="color:#719e07">[</span>1<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> keyPairs<span style="color:#719e07">[</span>i<span style="color:#719e07">][</span>0<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> activateValue<span style="color:#719e07">[</span>i<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存指定扩展信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivateValues<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> keyPairs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// traverse all cached extensions |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//遍历所有激活的扩展名字和扩展分组集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivateGroups<span style="color:#719e07">.</span>forEach<span style="color:#719e07">((</span>name<span style="color:#719e07">,</span> activateGroup<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//筛选当前扩展的扩展分组与激活扩展的扩展分组是否可以匹配 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isMatchGroup<span style="color:#719e07">(</span>group<span style="color:#719e07">,</span> activateGroup<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不能是指定的扩展名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>name<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//也不能是带有 -指定扩展名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> name<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果在Active注解中配置了value则当指定的键出现在URL的参数中时,激活当前扩展名。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果未配置value属性则默认都是匹配的(cachedActivateValues中不存在对应扩展名字的缓存的时候默认为true) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">&amp;&amp;</span> isActive<span style="color:#719e07">(</span>cachedActivateValues<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>name<span style="color:#719e07">),</span> url<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存激活的扩展类型映射的扩展名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> activateExtensionsMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>getExtensionClass<span style="color:#719e07">(</span>name<span style="color:#719e07">),</span> getExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>DEFAULT_KEY<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// will affect order |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// `ext1,default,ext2` means ext1 will happens before all of the default extensions while ext2 will after them |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ArrayList<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> extensionsResult <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>activateExtensionsMap<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> names<span style="color:#719e07">.</span>size<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0<span style="color:#719e07">;</span> i <span style="color:#719e07">&lt;</span> names<span style="color:#719e07">.</span>size<span style="color:#719e07">();</span> i<span style="color:#719e07">++)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> names<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>i<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>name<span style="color:#719e07">.</span>startsWith<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>DEFAULT_KEY<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>containsExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> extensionsResult<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>getExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> extensionsResult<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>activateExtensionsMap<span style="color:#719e07">.</span>values<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extensionsResult<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// add extensions, will be sorted by its order |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span><span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0<span style="color:#719e07">;</span> i <span style="color:#719e07">&lt;</span> names<span style="color:#719e07">.</span>size<span style="color:#719e07">();</span> i<span style="color:#719e07">++)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> names<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>i<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>name<span style="color:#719e07">.</span>startsWith<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>DEFAULT_KEY<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>containsExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> activateExtensionsMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>getExtensionClass<span style="color:#719e07">(</span>name<span style="color:#719e07">),</span> getExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>activateExtensionsMap<span style="color:#719e07">.</span>values<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>再来回顾下扫描扩展类型的时候,与激活扩展的相关扫描代码: |
| 与激活注解关键的代码位置在这里ExtensionLoader的loadClass方法中 |
| 我来贴下loadClass方法核心的代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">if</span> <span style="color:#719e07">(</span>clazz<span style="color:#719e07">.</span>isAnnotationPresent<span style="color:#719e07">(</span>Adaptive<span style="color:#719e07">.</span>class<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheAdaptiveClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isWrapperClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheWrapperClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> name <span style="color:#719e07">=</span> findAnnotationName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>name<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No such extension name for the class &#34;</span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in the config &#34;</span> <span style="color:#719e07">+</span> resourceURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> names <span style="color:#719e07">=</span> NAME_SEPARATOR<span style="color:#719e07">.</span>split<span style="color:#719e07">(</span>name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ArrayUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>names<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//位置在这里其他地方就不标记注释了,前面判断了如果不是Adaptive也不是Wrapper类型则我们可以来判断是否为Activate 类型如果是的话调用cacheActivateClass方法将扩展缓存进cachedActivates缓存中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheActivateClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> names<span style="color:#719e07">[</span>0<span style="color:#719e07">]);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String n <span style="color:#719e07">:</span> names<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> n<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> saveInExtensionClass<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> clazz<span style="color:#719e07">,</span> n<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">cacheActivateClass</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">,</span> String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Activate activate <span style="color:#719e07">=</span> clazz<span style="color:#719e07">.</span>getAnnotation<span style="color:#719e07">(</span>Activate<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>activate <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注解存在则加入激活注解缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivates<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> activate<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// support com.alibaba.dubbo.common.extension.Activate |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate oldActivate <span style="color:#719e07">=</span> clazz<span style="color:#719e07">.</span>getAnnotation<span style="color:#719e07">(</span>com<span style="color:#719e07">.</span>alibaba<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>extension<span style="color:#719e07">.</span>Activate<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>oldActivate <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cachedActivates<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> oldActivate<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/7-dubbo-de-spi-kuo-zhan-ji-zhi-zhi-zi-dong-ji-huo-kuo-zhan-activate-yuan-ma-jie-xi/">《Dubbo的SPI扩展机制之自动激活扩展Activate源码解析》</a></p></description></item><item><title>Blog: 06-Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/06/06-dubbo%E7%9A%84spi%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E4%B9%8B%E6%99%AE%E9%80%9A%E6%89%A9%E5%B1%95%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BA%E4%B8%8Ewrapper%E6%9C%BA%E5%88%B6%E7%9A%84%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</link><pubDate>Sat, 06 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/06/06-dubbo%E7%9A%84spi%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E4%B9%8B%E6%99%AE%E9%80%9A%E6%89%A9%E5%B1%95%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BA%E4%B8%8Ewrapper%E6%9C%BA%E5%88%B6%E7%9A%84%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</guid><description> |
| <h1 id="6-dubbo的spi扩展机制之普通扩展对象的创建与wrapper机制的源码解析">6 Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析</h1> |
| <h2 id="61-普通扩展对象的加载与创建">6.1 普通扩展对象的加载与创建</h2> |
| <p>这里我们要分析的是ExtensionLoader类型的getExtension(String name)方法, 有了前面自适应扩展的铺垫,这里就更容易来看了getExtension是根据扩展名字获取具体扩展的通用方法,我们来根据某个类型来获取扩展的时候就是走的这里,比如在这个博客开头的介绍:</p> |
| <ul> |
| <li>ApplicationModel中获取配置管理器对象</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> configManager <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ConfigManager<span style="color:#719e07">)</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationExt<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>ConfigManager<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><h3 id="611-getextension方法源码">6.1.1 getExtension方法源码</h3> |
| <p>先来看下getExtension方法的源码,根据扩展名字查询扩展对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">public</span> T <span style="color:#268bd2">getExtension</span>(String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里并不能看到什么,只多传了个参数wrap为true调用另外一个重载的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T extension <span style="color:#719e07">=</span> getExtension(name, <span style="color:#b58900">true</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (extension <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException(<span style="color:#2aa198">&#34;Not find extension: &#34;</span> <span style="color:#719e07">+</span> name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extension; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> T <span style="color:#268bd2">getExtension</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> wrap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查扩展加载器是否已被销毁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Extension name == null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展名字为true则加载默认扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getDefaultExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//非wrap类型则将缓存的扩展名字key加上_origin后缀 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//wrap是aop机制 俗称切面,这个origin在aop里面可以称为切点,下面的wrap扩展可以称为增强通知的类型,普通扩展和wrap扩展的扩展名字是一样的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String cacheKey <span style="color:#719e07">=</span> name<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>wrap<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheKey <span style="color:#719e07">+=</span> <span style="color:#2aa198">&#34;_origin&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从cachedInstances缓存中查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> Holder<span style="color:#719e07">&lt;</span>Object<span style="color:#719e07">&gt;</span> holder <span style="color:#719e07">=</span> getOrCreateHolder<span style="color:#719e07">(</span>cacheKey<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Object instance <span style="color:#719e07">=</span> holder<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中不存在则创建扩展对象 双重校验锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>holder<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁的方式 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> holder<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> createExtension<span style="color:#719e07">(</span>name<span style="color:#719e07">,</span> wrap<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> holder<span style="color:#719e07">.</span>set<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>我们先来看一下默认扩展的加载代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> T <span style="color:#268bd2">getDefaultExtension</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载扩展类型对应的所有扩展SPI实现类型,在加载所有扩展实现类型的时候会缓存这个扩展的默认实现类型,将对象缓存在cachedDefaultName中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getExtensionClasses<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isBlank<span style="color:#719e07">(</span>cachedDefaultName<span style="color:#719e07">)</span> <span style="color:#719e07">||</span> <span style="color:#2aa198">&#34;true&#34;</span><span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>cachedDefaultName<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//再回到加载扩展的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> getExtension<span style="color:#719e07">(</span>cachedDefaultName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>创建扩展对象方法这个和自适应扩展的创建扩展类似 |
| createExtension: |
| 具体过程如下:</p> |
| <ul> |
| <li>加载扩展类型:getExtensionClasses()</li> |
| <li>创建扩展对象:createExtensionInstance(clazz)</li> |
| <li>注入自适应扩展: injectExtension(instance);</li> |
| <li>wrap处理</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> T <span style="color:#268bd2">createExtension</span>(String name, boolean wrap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展的创建的第一步扫描所有jar中的扩展实现,这里扫描完之后获取对应扩展名字的扩展实现类型的Class对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Class<span style="color:#719e07">&lt;?&gt;</span> clazz <span style="color:#719e07">=</span> getExtensionClasses().get(name); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//出现异常了 转换下异常信息 再抛出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (clazz <span style="color:#719e07">==</span> null <span style="color:#719e07">||</span> unacceptableExceptions.contains(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> findException(name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前扩展对象是否已经创建过了则直接从缓存中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T instance <span style="color:#719e07">=</span> (T) extensionInstances.get(clazz); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次获取缓存中肯定没有则创建扩展对象然后缓存起来 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//createExtensionInstance 这个是与自适应扩展对象创建对象的不同之处 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz)); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> (T) extensionInstances.get(clazz); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessBeforeInitialization(instance, name); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展自适应方法,这个方法前面讲自适应扩展时候说了,注入自适应扩展方法的自适应扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> injectExtension(instance); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessAfterInitialization(instance, name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否开启了wrap |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//Dubbo通过Wrapper实现AOP的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (wrap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个可以参考下Dubbo扩展的加载 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>Class<span style="color:#719e07">&lt;?&gt;&gt;</span> wrapperClassesList <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//wrap类型排序 这个wrap类型是如何来的呢,在前面扫描扩展类型的时候如果当前扩展类型不是Adaptive注解修饰的,并且当前类型type有个构造器参数是type自身的也是前面加载扩展类型时候说的装饰器模式 可以参考DubboProtocol的构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (cachedWrapperClasses <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> wrapperClassesList.addAll(cachedWrapperClasses); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据Wrapper注解的order值来进行排序值越小越在列表的前面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> wrapperClassesList.sort(WrapperComparator.COMPARATOR); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//反转之后值越大就会在列表的前面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collections.reverse(wrapperClassesList); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从缓存中查到了wrapper扩展则遍历这些wrapp扩展进行筛选 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(wrapperClassesList)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Class<span style="color:#719e07">&lt;?&gt;</span> wrapperClass : wrapperClassesList) { |
| </span></span><span style="display:flex;"><span> Wrapper wrapper <span style="color:#719e07">=</span> wrapperClass.getAnnotation(Wrapper.<span style="color:#719e07">class</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//需要包装的扩展名。当此数组为空时,默认值为匹配 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//看下当前扩展是否匹配这个wrap,如何判断呢? |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//wrapper注解不存在或者matches匹配,或者mismatches不包含当前扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> boolean match <span style="color:#719e07">=</span> (wrapper <span style="color:#719e07">==</span> null) <span style="color:#719e07">||</span> |
| </span></span><span style="display:flex;"><span> ((ArrayUtils.isEmpty(wrapper.matches()) <span style="color:#719e07">||</span> ArrayUtils.contains(wrapper.matches(), name)) <span style="color:#719e07">&amp;&amp;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">!</span>ArrayUtils.contains(wrapper.mismatches(), name)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这是扩展类型是匹配wrapp的则开始注入 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (match) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//匹配到了就创建所有的wrapper类型的对象同时构造器参数设置为当前类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessAfterInitialization(instance, name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//初始化扩展,如果当前扩展是Lifecycle类型则调用初始化方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initExtension(instance); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> <span style="color:#268bd2">IllegalStateException</span>(<span style="color:#2aa198">&#34;Extension instance (name: &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, class: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;)</span> couldn&#39;t be instantiated: <span style="color:#2aa198">&#34; + t.getMessage(), t)</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="612-创建扩展对象">6.1.2 创建扩展对象</h3> |
| <p>前面加载扩展类型在自适应扩展的时候已经说过了这里就不重复了,这里我们来看下 |
| 扩展对象的创建过程:createExtensionInstance(clazz)</p> |
| <p>前面看自适应扩展对象创建的时候自适应扩展对象仅仅是使用反射newInstance了一个扩展对象,而普通的扩展类型创建对象的过程就相对复杂一点,接下来我们来看下:</p> |
| <p>ExtensionLoader的createExtensionInstance方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> Object <span style="color:#268bd2">createExtensionInstance</span>(Class<span style="color:#719e07">&lt;?&gt;</span> type) <span style="color:#719e07">throws</span> ReflectiveOperationException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//在ExtensionLoader构造器中,有个initInstantiationStrategy()方法中new了一个初始化策略InstantiationStrategy类型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> instantiationStrategy.instantiate(type); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>InstantiationStrategy的实例化对象方法instantiate</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T instantiate(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type) <span style="color:#719e07">throws</span> ReflectiveOperationException { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// should not use default constructor directly, maybe also has another constructor matched scope model arguments |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 1. try to get default constructor |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Constructor<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> defaultConstructor <span style="color:#719e07">=</span> null; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//反射获取对应类型的无参构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultConstructor <span style="color:#719e07">=</span> type.getConstructor(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (NoSuchMethodException e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore no default constructor |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 2. use matched constructor if found |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>Constructor<span style="color:#719e07">&gt;</span> matchedConstructors <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取所有构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Constructor<span style="color:#719e07">&lt;?&gt;</span>[] declaredConstructors <span style="color:#719e07">=</span> type.getConstructors(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历构造器列表, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> (Constructor<span style="color:#719e07">&lt;?&gt;</span> constructor : declaredConstructors) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果存在构造器则构造器参数类型是否为ScopeModel类型,如果为ScopeModel则为匹配的构造器 说明我们扩展类型在这个版本如果想要让这个构造器生效必须参数类型为ScopeModel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (isMatched(constructor)) { |
| </span></span><span style="display:flex;"><span> matchedConstructors.add(constructor); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// remove default constructor from matchedConstructors |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (defaultConstructor <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> matchedConstructors.remove(defaultConstructor); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// match order: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 1. the only matched constructor with parameters |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 2. default constructor if absent |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> Constructor targetConstructor; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//匹配的参数ScopeModel的构造器太多了就抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (matchedConstructors.size() <span style="color:#719e07">&gt;</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> <span style="color:#268bd2">IllegalArgumentException</span>(<span style="color:#2aa198">&#34;Expect only one but found &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> matchedConstructors.size() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; matched constructors for type: &#34;</span> <span style="color:#719e07">+</span> type.getName() <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;, matched constructors: &#34;</span> <span style="color:#719e07">+</span> matchedConstructors); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (matchedConstructors.size() <span style="color:#719e07">==</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一个参数一般为一个参数类型ScopeModel的构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> targetConstructor <span style="color:#719e07">=</span> matchedConstructors.get(<span style="color:#2aa198">0</span>); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (defaultConstructor <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果没有自定义构造器则使用空参数构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> targetConstructor <span style="color:#719e07">=</span> defaultConstructor; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一个构造器也没匹配上也要报错 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> <span style="color:#268bd2">IllegalArgumentException</span>(<span style="color:#2aa198">&#34;None matched constructor was found for type: &#34;</span> <span style="color:#719e07">+</span> type.getName()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// create instance with arguments |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//反射获取构造器参数的参数类型列表 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Class[] parameterTypes <span style="color:#719e07">=</span> targetConstructor.getParameterTypes(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果存在参数则为参数设置值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Object[] args <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Object[parameterTypes.length]; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (<span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> <span style="color:#2aa198">0</span>; i <span style="color:#719e07">&lt;</span> parameterTypes.length; i<span style="color:#719e07">++</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//借助scopeModelAccessor工具获取参数类型,这个参数类型为当前的域模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> args[i] <span style="color:#719e07">=</span> getArgumentValueForType(parameterTypes[i]); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> (T) targetConstructor.newInstance(args); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h2 id="62-wrap机制">6.2 wrap机制</h2> |
| <h3 id="621-wrapper机制说明">6.2.1 Wrapper机制说明</h3> |
| <p>Dubbo通过Wrapper实现AOP的方法</p> |
| <p>Wrapper机制,即扩展点自动包装。Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。 |
| 扩展点的 Wrapper 类可以有多个,也可以根据需要新增。 |
| 通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。</p> |
| <p>Wrapper的规范 |
| Wrapper 机制不是通过注解实现的,而是通过一套 Wrapper 规范实现的。 |
| Wrapper 类在定义时需要遵循如下规范。</p> |
| <ul> |
| <li>该类要实现 SPI 接口</li> |
| <li>该类中要有 SPI 接口的引用</li> |
| <li>该类中必须含有一个含参的构造方法且参数只能有一个类型为SPI接口</li> |
| <li>在接口实现方法中要调用 SPI 接口引用对象的相应方法</li> |
| <li>该类名称以 Wrapper 结尾</li> |
| </ul> |
| <p>比如如下几个扩展类型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#719e07">class</span> <span style="color:#268bd2">org</span>.apache.dubbo.rpc.protocol.ProtocolListenerWrapper |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">class</span> <span style="color:#268bd2">org</span>.apache.dubbo.qos.protocol.QosProtocolWrapper |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">class</span> <span style="color:#268bd2">org</span>.apache.dubbo.rpc.protocol.ProtocolListenerWrapper |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">class</span> <span style="color:#268bd2">org</span>.apache.dubbo.qos.protocol.QosProtocolWrapper |
| </span></span></code></pre></div><p>回顾下Wrapper扩展类型的扫描于对象的创建</p> |
| <h3 id="622-wrapper类型的扫描">6.2.2 Wrapper类型的扫描</h3> |
| <p><strong>Wrapper类型的扫描代码如下:</strong></p> |
| <p>来自4.5.2.3小节ExtensionLoader类型中的loadClass方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#586e75">//扩展子类型是否存在这个注解@Adaptive |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (clazz.isAnnotationPresent(Adaptive.<span style="color:#719e07">class</span>)) { |
| </span></span><span style="display:flex;"><span> cacheAdaptiveClass(clazz, overridden); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (isWrapperClass(clazz)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展子类型构造器中是否有这个类型的接口 (这个可以想象下我们了解的Java IO流中的类型使用到的装饰器模式 构造器传个类型) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheWrapperClass(clazz); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span></code></pre></div><p>isWrapperClass方法通过判断构造器类型是否为当前类型来判断是否为Wrapper类型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#719e07">private</span> boolean <span style="color:#268bd2">isWrapperClass</span>(Class<span style="color:#719e07">&lt;?&gt;</span> clazz) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> clazz.getConstructor(type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#b58900">true</span>; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (NoSuchMethodException e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#b58900">false</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="623-wrapper类型的创建">6.2.3 Wrapper类型的创建</h3> |
| <p>这个可以看下4.6.1 getExtension方法源码的获取扩展对象时候查询扩展对象是否有对应的Wrapper类型的扩展为其创建Wrapper扩展对象,如下代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#586e75">//Dubbo通过Wrapper实现AOP的方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (wrap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个可以参考下Dubbo扩展的加载 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> List<span style="color:#719e07">&lt;</span>Class<span style="color:#719e07">&lt;?&gt;&gt;</span> wrapperClassesList <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//wrap类型排序 这个wrap类型是如何来的呢,在前面扫描扩展类型的时候如果当前扩展类型不是Adaptive注解修饰的,并且当前类型type有个构造器参数是type自身的也是前面加载扩展类型时候说的装饰器模式 可以参考DubboProtocol的构造器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (cachedWrapperClasses <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> wrapperClassesList.addAll(cachedWrapperClasses); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据Wrapper注解的order值来进行排序值越小越在列表的前面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> wrapperClassesList.sort(WrapperComparator.COMPARATOR); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//反转之后值越大就会在列表的前面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Collections.reverse(wrapperClassesList); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从缓存中查到了wrapper扩展则遍历这些wrapp扩展进行筛选 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(wrapperClassesList)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Class<span style="color:#719e07">&lt;?&gt;</span> wrapperClass : wrapperClassesList) { |
| </span></span><span style="display:flex;"><span> Wrapper wrapper <span style="color:#719e07">=</span> wrapperClass.getAnnotation(Wrapper.<span style="color:#719e07">class</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//需要包装的扩展名。当此数组为空时,默认值为匹配 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//看下当前扩展是否匹配这个wrap,如何判断呢? |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//wrapper注解不存在或者matches匹配,或者mismatches不包含当前扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> boolean match <span style="color:#719e07">=</span> (wrapper <span style="color:#719e07">==</span> null) <span style="color:#719e07">||</span> |
| </span></span><span style="display:flex;"><span> ((ArrayUtils.isEmpty(wrapper.matches()) <span style="color:#719e07">||</span> ArrayUtils.contains(wrapper.matches(), name)) <span style="color:#719e07">&amp;&amp;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">!</span>ArrayUtils.contains(wrapper.mismatches(), name)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这是扩展类型是匹配wrapp的则开始注入 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (match) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//匹配到了就创建所有的wrapper类型的对象同时构造器参数设置为当前类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessAfterInitialization(instance, name); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>主要来看下什么情况下才为当前扩展类型创建Wrapper包装类型:</p> |
| <ul> |
| <li>wrapper注解不存在(前面判断过Wrapper类型是构造器满足条件的)</li> |
| <li>存在Wrapper注解: |
| <ul> |
| <li>matches匹配,</li> |
| <li>或者mismatches不包含当前扩展</li> |
| </ul> |
| </li> |
| </ul> |
| <p>如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装</p> |
| <p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/6-dubbo-de-spi-kuo-zhan-ji-zhi-zhi-pu-tong-kuo-zhan-dui-xiang-de-chuang-jian-yu-wrapper-ji-zhi-de-yuan-ma-jie-xi/">《Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析》</a></p></description></item><item><title>Blog: 05-自适应扩展对象的创建getAdaptiveExtension方法</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/05/05-%E8%87%AA%E9%80%82%E5%BA%94%E6%89%A9%E5%B1%95%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BAgetadaptiveextension%E6%96%B9%E6%B3%95/</link><pubDate>Fri, 05 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/05/05-%E8%87%AA%E9%80%82%E5%BA%94%E6%89%A9%E5%B1%95%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BAgetadaptiveextension%E6%96%B9%E6%B3%95/</guid><description> |
| <h2 id="5-自适应扩展对象的创建getadaptiveextension方法">5 自适应扩展对象的创建getAdaptiveExtension方法</h2> |
| <p>自适应扩展又称为动态扩展,可以在运行时生成扩展对象</p> |
| <p>ExtensionLoader中的getAdaptiveExtension()方法,这个方法也是我们看到的第一个获取扩展对象的方法. ,这个方法可以帮助我们通过SPI机制从扩展文件中找到需要的扩展类型并创建它的对象, |
| <strong>自适应扩展:<strong>如果对设计模式比较了解的可能会联想到</strong>适配器模式</strong>,自适应扩展其实就是适配器模式的思路,自适应扩展有两种策略:</p> |
| <ul> |
| <li> |
| <p>一种是我们自己实现自适应扩展:然后使用@Adaptive修饰这个时候适配器的逻辑由我们自己实现,当扩展加载器去查找具体的扩展的时候可以通过找到我们这个对应的适配器扩展,然后适配器扩展帮忙去查询真正的扩展,这个比如我们下面要举的扩展注入器的例子,具体扩展通过扩展注入器适配器,注入器适配器来查询具体的注入器扩展实现来帮忙查找扩展。</p> |
| </li> |
| <li> |
| <p>还有一种方式是我们未实现这个自适应扩展,Dubbo在运行时通过字节码动态代理的方式在运行时生成一个适配器,使用这个适配器映射到具体的扩展. 第二种情况往往用在比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。(如果还不了解可以考虑看下@Adaptive注解加载方法上面的时候扩展是如何加载的)</p> |
| </li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> T <span style="color:#268bd2">getAdaptiveExtension</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查当前扩展加载器是否已经被销毁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从自适应扩展缓存中查询扩展对象如果存在就直接返回,这个自适应扩展类型只会有一个扩展实现类型如果是多个的话根据是否可以覆盖参数决定扩展实现类是否可以相互覆盖 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Object instance <span style="color:#719e07">=</span> cachedAdaptiveInstance<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个if判断不太优雅 容易多层嵌套,上面instance不为空就可以直接返回了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建异常则抛出异常直接返回(多线程场景下可能第一个线程异常了第二个线程进来之后走到这里) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>createAdaptiveInstanceError <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to create adaptive instance: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> createAdaptiveInstanceError<span style="color:#719e07">.</span>toString<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> createAdaptiveInstanceError<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加锁排队 (单例模式创建对象的思想 双重校验锁) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>cachedAdaptiveInstance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加锁的时候对象都是空的,进来之后先判断下防止重复创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> cachedAdaptiveInstance<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//只有第一个进来锁的对象为空开始创建扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据SPI机制获取类型,创建对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> createAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存入缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedAdaptiveInstance<span style="color:#719e07">.</span>set<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> createAdaptiveInstanceError <span style="color:#719e07">=</span> t<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to create adaptive instance: &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>toString<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面使用单例思想来调用创建自适应扩展对象的方法,下面就让我们深入探究下创建自适应扩展对象的整个过程createAdaptiveExtension();方法:</p> |
| <h2 id="51-创建扩展对象的生命周期方法-注意这个后续会详细解析这个声明周期方法的细节">5.1 创建扩展对象的生命周期方法-注意这个后续会详细解析这个声明周期方法的细节</h2> |
| <p>createAdaptiveExtension() |
| 我们先来看ExtensionLoader类型中的createAdaptiveExtension();方法,这个方法包含了扩展对象创建初始化的整个生命周期,如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> T <span style="color:#268bd2">createAdaptiveExtension</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取扩展类型实现类, 创建扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T instance <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> getAdaptiveExtensionClass<span style="color:#719e07">().</span>newInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象之前的回调方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> postProcessBeforeInitialization<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> injectExtension<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象之后的回调方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> instance <span style="color:#719e07">=</span> postProcessAfterInitialization<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化扩展对象的属性,如果当前扩展实例的类型实现了Lifecycle则调用当前扩展对象的生命周期回调方法initialize()(来自Lifecycle接口) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//参考样例第一个instance为ExtensionInjector的自适应扩展对象类型为AdaptiveExtensionInjector,自适应扩展注入器(适配器)用来查询具体支持的扩展注入器比如scope,spi,spring注入器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initExtension<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Can&#39;t create adaptive extension &#34;</span> <span style="color:#719e07">+</span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="52-spi机制获取扩展对象实现类型getadaptiveextensionclass">5.2 SPI机制获取扩展对象实现类型getAdaptiveExtensionClass()</h2> |
| <p>这个方法可以帮助我们了解具体的Dubbo SPI机制 如果找到扩展类型的实现类,会寻找哪些文件,扩展文件的优先级又是什么,对我们自己写扩展方法很有帮助,接下来我们就来看下它的源码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> Class<span style="color:#719e07">&lt;?&gt;</span> getAdaptiveExtensionClass<span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取扩展类型,将扩展类型存入成员变量cachedClasses中进行缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> getExtensionClasses<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//在上个方法的详细解析中的最后一步loadClass方法中如果扩展类型存在Adaptive注解将会将扩展类型赋值给cachedAdaptiveClass,否则的话会把扩展类型都缓存起来存储在扩展集合extensionClasses中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedAdaptiveClass <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cachedAdaptiveClass<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展实现类型没有一个这个自适应注解Adaptive时候会走到这里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//刚刚我们扫描到了扩展类型然后将其存入cachedClasses集合中了 接下来我们看下如何创建扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> cachedAdaptiveClass <span style="color:#719e07">=</span> createAdaptiveExtensionClass<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续看获取扩展类型的方法<strong>getExtensionClasses()</strong>:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> getExtensionClasses<span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中查询扩展类型是否存在 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> classes <span style="color:#719e07">=</span> cachedClasses<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>classes <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//单例模式双重校验锁判断 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>cachedClasses<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> classes <span style="color:#719e07">=</span> cachedClasses<span style="color:#719e07">.</span>get<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>classes <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> classes <span style="color:#719e07">=</span> loadExtensionClasses<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将我们扫描到的扩展类型存入成员变量cachedClasses中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedClasses<span style="color:#719e07">.</span>set<span style="color:#719e07">(</span>classes<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> classes<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="521-使用不同的的策略加载加载不同目录下的扩展">5.2.1 使用不同的的策略加载加载不同目录下的扩展</h3> |
| <p>加载扩展类型的方法<strong>loadExtensionClasses()</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#719e07">private</span> Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> loadExtensionClasses() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查扩展加载器是否被销毁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存默认的扩展名到成员变量cachedDefaultName中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheDefaultExtensionName(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载到的扩展集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展策略,在4.3章节中我们介绍了这个类型的UML与说明 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//LoadingStrategy扩展加载策略,目前有3个扩展加载策略 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//DubboInternalLoadingStrategy:Dubbo内置的扩展加载策略,将加载文件目录为META-INF/dubbo/internal/的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//DubboLoadingStrategy:Dubbo普通的扩展加载策略,将加载目录为META-INF/dubbo/的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//ServicesLoadingStrategy:JAVA SPI加载策略 ,将加载目录为META-INF/services/的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//扩展策略集合对象在什么时候初始化的呢在成员变量初始化的时候就创建了集合对象,这个可以看方法loadLoadingStrategies() 通过Java的 SPI加载策略 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> (LoadingStrategy strategy : strategies) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据策略从指定文件目录中加载扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadDirectory(extensionClasses, strategy, type.getName()); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// compatible with old ExtensionFactory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//如果当前要加载的扩展类型是扩展注入类型则扫描下ExtensionFactory类型的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.type <span style="color:#719e07">==</span> ExtensionInjector.<span style="color:#719e07">class</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个方法和上面那个方法是一样的就不详细说了 扫描文件 找到扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadDirectory(extensionClasses, strategy, ExtensionFactory.<span style="color:#719e07">class</span>.<span style="color:#268bd2">getName</span>()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过loadDirectory扫描 扫描到了ExtensionInjector类型的扩展实现类有3个 我们将会得到这样一个集合例子: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//&#34;spring&#34; -&gt; &#34;class org.apache.dubbo.config.spring.extension.SpringExtensionInjector&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//&#34;scopeBean&#34; -&gt; &#34;class org.apache.dubbo.common.beans.ScopeBeanExtensionInjector&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//&#34;spi&#34; -&gt; &#34;class org.apache.dubbo.common.extension.inject.SpiExtensionInjector&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> extensionClasses; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>从文件中加载扩展实现loadDirectory方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadDirectory</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> LoadingStrategy strategy<span style="color:#719e07">,</span> String type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载并根据策略的参数来加载 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadDirectory<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> strategy<span style="color:#719e07">.</span>directory<span style="color:#719e07">(),</span> type<span style="color:#719e07">,</span> strategy<span style="color:#719e07">.</span>preferExtensionClassLoader<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> strategy<span style="color:#719e07">.</span>overridden<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>includedPackages<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>excludedPackages<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>onlyExtensionClassLoaderPackages<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//下面两行就是要兼容alibaba的扩展包了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String oldType <span style="color:#719e07">=</span> type<span style="color:#719e07">.</span>replace<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;org.apache&#34;</span><span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;com.alibaba&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> loadDirectory<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> strategy<span style="color:#719e07">.</span>directory<span style="color:#719e07">(),</span> oldType<span style="color:#719e07">,</span> strategy<span style="color:#719e07">.</span>preferExtensionClassLoader<span style="color:#719e07">(),</span> |
| </span></span><span style="display:flex;"><span> strategy<span style="color:#719e07">.</span>overridden<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>includedPackagesInCompatibleType<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>excludedPackages<span style="color:#719e07">(),</span> strategy<span style="color:#719e07">.</span>onlyExtensionClassLoaderPackages<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>带扩展策略参数的loadDirectory方法</p> |
| <p>关于扩展策略的参数列表我这里列个表格方便大家来看</p> |
| <table> |
| <thead> |
| <tr> |
| <th>扩展类型</th> |
| <th>dir(目录)</th> |
| <th>extensionLoaderClassLoaderFirst(优先扩展类型的类加载器)</th> |
| <th>overridden(是否允许覆盖同名扩展)</th> |
| <th>includedPackages (明确包含的扩展包)</th> |
| <th>excludedPackages (明确排除的扩展包)</th> |
| <th>onlyExtensionClassLoaderPackages(限制应该从Dubbo的类加载器加载的类)</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>DubboInternalLoadingStrategy</td> |
| <td>META-INF/dubbo/internal/</td> |
| <td>false</td> |
| <td>false</td> |
| <td>null</td> |
| <td>null</td> |
| <td>[]</td> |
| </tr> |
| <tr> |
| <td>DubboLoadingStrategy</td> |
| <td>META-INF/dubbo/</td> |
| <td>false</td> |
| <td>true</td> |
| <td>null</td> |
| <td>null</td> |
| <td>[]</td> |
| </tr> |
| <tr> |
| <td>ServicesLoadingStrategy</td> |
| <td>META-INF/services/</td> |
| <td>false</td> |
| <td>true</td> |
| <td>null</td> |
| <td>null</td> |
| <td>[]</td> |
| </tr> |
| </tbody> |
| </table> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 不同的扩展策略传递了不同的参数,但是扩展的加载流程是相同的,这里我们可以参考上面表格 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param extensionClasses |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param dir |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param type 这里我们参考的示例这个值为org.apache.dubbo.common.extension.ExtensionInjector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param extensionLoaderClassLoaderFirst |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param overridden false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param includedPackages |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param excludedPackages |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param onlyExtensionClassLoaderPackages |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadDirectory</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> String dir<span style="color:#719e07">,</span> String type<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> extensionLoaderClassLoaderFirst<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> includedPackages<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> excludedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展目录 + 扩展类型全路径 比如: META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String fileName <span style="color:#719e07">=</span> dir <span style="color:#719e07">+</span> type<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoadersToLoad <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// try to load from ExtensionLoader&#39;s ClassLoader first |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//是否优先使用扩展加载器的 类加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>extensionLoaderClassLoaderFirst<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ClassLoader extensionLoaderClassLoader <span style="color:#719e07">=</span> ExtensionLoader<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ClassLoader<span style="color:#719e07">.</span>getSystemClassLoader<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> extensionLoaderClassLoader<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>extensionLoaderClassLoader<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load from scope model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取域模型对象的类型加载器 ,这个域模型对象在初始化的时候会将自己的类加载器放入集合中可以参考《3.2.2 初始化ScopeModel》章节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">.</span>getClassLoaders<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//没有可用的类加载器则从使用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从用于加载类的搜索路径中查找指定名称的所有资源。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Enumeration<span style="color:#719e07">&lt;</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL<span style="color:#719e07">&gt;</span> resources <span style="color:#719e07">=</span> ClassLoader<span style="color:#719e07">.</span>getSystemResources<span style="color:#719e07">(</span>fileName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>resources <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> <span style="color:#719e07">(</span>resources<span style="color:#719e07">.</span>hasMoreElements<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> loadResource<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> resources<span style="color:#719e07">.</span>nextElement<span style="color:#719e07">(),</span> overridden<span style="color:#719e07">,</span> includedPackages<span style="color:#719e07">,</span> excludedPackages<span style="color:#719e07">,</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用类加载资源加载器(ClassLoaderResourceLoader)来加载具体的资源 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">,</span> Set<span style="color:#719e07">&lt;</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL<span style="color:#719e07">&gt;&gt;</span> resources <span style="color:#719e07">=</span> ClassLoaderResourceLoader<span style="color:#719e07">.</span>loadResources<span style="color:#719e07">(</span>fileName<span style="color:#719e07">,</span> classLoadersToLoad<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历从所有资源文件中读取到资源url地址,key为类加载器,值为扩展文件url如夏所示 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//jar:file:/Users/song/.m2/repository/org/apache/dubbo/dubbo/3.0.7/dubbo-3.0.7.jar!/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> resources<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(((</span>classLoader<span style="color:#719e07">,</span> urls<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从文件中加载完资源之后开始根据类加载器和url加载具体的扩展类型,最后将扩展存放进extensionClasses集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadFromClass<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> overridden<span style="color:#719e07">,</span> urls<span style="color:#719e07">,</span> classLoader<span style="color:#719e07">,</span> includedPackages<span style="color:#719e07">,</span> excludedPackages<span style="color:#719e07">,</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Exception occurred when loading extension class (interface: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, description file: &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;).&#34;</span><span style="color:#719e07">,</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="522-借助类加载器的getresources方法遍历所有文件进行扩展文件的查询">5.2.2 借助类加载器的getResources方法遍历所有文件进行扩展文件的查询</h3> |
| <p>查找扩展类型对应的扩展文件的url方法:ClassLoaderResourceLoader类型的loadResources源码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">public</span> <span style="color:#719e07">static</span> Map<span style="color:#719e07">&lt;</span>ClassLoader, Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;&gt;</span> loadResources(String fileName, List<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>ClassLoader, Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;&gt;</span> resources <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不同的类加载器之间使用不同的线程异步的方式进行扫描 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> CountDownLatch countDownLatch <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CountDownLatch(classLoaders.size()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ClassLoader classLoader : classLoaders) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多线程扫描,这个是个newCachedThreadPool的类型的线程池 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> GlobalResourcesRepository.getGlobalExecutorService().submit(() <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> resources.put(classLoader, loadResources(fileName, classLoader)); |
| </span></span><span style="display:flex;"><span> countDownLatch.countDown(); |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> countDownLatch.await(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (InterruptedException e) { |
| </span></span><span style="display:flex;"><span> e.printStackTrace(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> Collections.unmodifiableMap(<span style="color:#719e07">new</span> LinkedHashMap<span style="color:#719e07">&lt;&gt;</span>(resources)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>加载具体类加载器中的资源文件的loadResources方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">public</span> <span style="color:#719e07">static</span> Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> loadResources(String fileName, ClassLoader currentClassLoader) { |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>ClassLoader, Map<span style="color:#719e07">&lt;</span>String, Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;&gt;&gt;</span> classLoaderCache; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次进来类加载器资源缓存是空的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (classLoaderResourcesCache <span style="color:#719e07">==</span> null <span style="color:#719e07">||</span> (classLoaderCache <span style="color:#719e07">=</span> classLoaderResourcesCache.get()) <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//类对象锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> synchronized (ClassLoaderResourceLoader.<span style="color:#719e07">class</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (classLoaderResourcesCache <span style="color:#719e07">==</span> null <span style="color:#719e07">||</span> (classLoaderCache <span style="color:#719e07">=</span> classLoaderResourcesCache.get()) <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> classLoaderCache <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个类资源映射url的软引用缓存对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//软引用(soft references),用于帮助垃圾收集器管理内存使用和消除潜在的内存泄漏。当内存快要不足的时候,GC会迅速的把所有的软引用清除掉,释放内存空间 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> classLoaderResourcesCache <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SoftReference<span style="color:#719e07">&lt;&gt;</span>(classLoaderCache); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次进来时候类加载器url映射缓存是空的,给类加载器缓存对象新增一个值,key是类加载器,值是map类型用来存储文件名对应的url集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>classLoaderCache.containsKey(currentClassLoader)) { |
| </span></span><span style="display:flex;"><span> classLoaderCache.putIfAbsent(currentClassLoader, <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;&gt;</span> urlCache <span style="color:#719e07">=</span> classLoaderCache.get(currentClassLoader); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中没有就从文件里面找 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>urlCache.containsKey(fileName)) { |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> set <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedHashSet<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> Enumeration<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> urls; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//getResources这个方法是这样的:加载当前类加载器以及父类加载器所在路径的资源文件,将遇到的所有资源文件全部返回!这个可以理解为使用双亲委派模型中的类加载器 加载各个位置的资源文件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> urls <span style="color:#719e07">=</span> currentClassLoader.getResources(fileName); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//native配置 是否为本地镜像(k可以参考官方文档:https://dubbo.apache.org/zh-cn/docs/references/graalvm/support-graalvm/ |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> boolean isNative <span style="color:#719e07">=</span> NativeUtils.isNative(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (urls <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历找到的对应扩展的文件url将其加入集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">while</span> (urls.hasMoreElements()) { |
| </span></span><span style="display:flex;"><span> URL url <span style="color:#719e07">=</span> urls.nextElement(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isNative) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//In native mode, the address of each URL is the same instead of different paths, so it is necessary to set the ref to make it different |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//动态修改jdk底层url对象的ref变量为可访问,让我们在用反射时访问私有变量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> setRef(url); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> set.add(url); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (IOException e) { |
| </span></span><span style="display:flex;"><span> e.printStackTrace(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存入缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> urlCache.put(fileName, set); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//返回结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> urlCache.get(fileName); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="523-使用找到的扩展资源url加载具体扩展类型到内存">5.2.3 使用找到的扩展资源url加载具体扩展类型到内存</h3> |
| <p>ExtensionLoader类型中的loadFromClass方法 遍历url 开始加载扩展类型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadFromClass</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">,</span> Set<span style="color:#719e07">&lt;</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL<span style="color:#719e07">&gt;</span> urls<span style="color:#719e07">,</span> ClassLoader classLoader<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> includedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> excludedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>urls<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL url <span style="color:#719e07">:</span> urls<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> loadResource<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> classLoader<span style="color:#719e07">,</span> url<span style="color:#719e07">,</span> overridden<span style="color:#719e07">,</span> includedPackages<span style="color:#719e07">,</span> excludedPackages<span style="color:#719e07">,</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中的loadResource方法 使用IO流读取扩展文件的内容 |
| 读取内容之前我这里先贴一下我们参考的扩展注入类型的文件中的内容如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>adaptive<span style="color:#719e07">=</span>org.apache.dubbo.common.extension.inject.AdaptiveExtensionInjector |
| </span></span><span style="display:flex;"><span>spi<span style="color:#719e07">=</span>org.apache.dubbo.common.extension.inject.SpiExtensionInjector |
| </span></span><span style="display:flex;"><span>scopeBean<span style="color:#719e07">=</span>org.apache.dubbo.common.beans.ScopeBeanExtensionInjector |
| </span></span></code></pre></div><p>扩展中的文件都是一行一行的,并且扩展名字和扩展类型之间使用等号隔开= |
| 了解了文件内容之后 应该下面的代码大致思路就知道了,我们可以详细看下</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadResource</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> ClassLoader classLoader<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL resourceURL<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> includedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> excludedPackages<span style="color:#719e07">,</span> String<span style="color:#719e07">[]</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里固定了文件的格式为utf8 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>BufferedReader reader <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> BufferedReader<span style="color:#719e07">(</span><span style="color:#719e07">new</span> InputStreamReader<span style="color:#719e07">(</span>resourceURL<span style="color:#719e07">.</span>openStream<span style="color:#719e07">(),</span> StandardCharsets<span style="color:#719e07">.</span>UTF_8<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String line<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> String clazz<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//按行读取 例如读取到的内容:spring=org.apache.dubbo.config.spring.extension.SpringExtensionInjector |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">while</span> <span style="color:#719e07">((</span>line <span style="color:#719e07">=</span> reader<span style="color:#719e07">.</span>readLine<span style="color:#719e07">())</span> <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不知道为何会有这么一行代码删除#之后的字符串 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">int</span> ci <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>indexOf<span style="color:#719e07">(</span><span style="color:#2aa198">&#39;#&#39;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ci <span style="color:#719e07">&gt;=</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> line <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>substring<span style="color:#719e07">(</span>0<span style="color:#719e07">,</span> ci<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> line <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>trim<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>line<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展文件可能如上面我贴的那样 名字和类型等号隔开,也可能是无类型的,例如扩展加载策略使用的是JDK自带的方式services内容中只包含具体的扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>indexOf<span style="color:#719e07">(</span><span style="color:#2aa198">&#39;=&#39;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>i <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> name <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>substring<span style="color:#719e07">(</span>0<span style="color:#719e07">,</span> i<span style="color:#719e07">).</span>trim<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> clazz <span style="color:#719e07">=</span> line<span style="color:#719e07">.</span>substring<span style="color:#719e07">(</span>i <span style="color:#719e07">+</span> 1<span style="color:#719e07">).</span>trim<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> clazz <span style="color:#719e07">=</span> line<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//isExcluded是否为加载策略要排除的配置,参数这里为空代表全部类型不排除 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//isIncluded是否为加载策略包含的类型,参数这里为空代表全部文件皆可包含 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//onlyExtensionClassLoaderPackages参数是否只有扩展类的类加载器可以加载扩展,其他扩展类型的类加载器不能加载扩展 这里结果为false 不排除任何类加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>clazz<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isExcluded<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> excludedPackages<span style="color:#719e07">)</span> <span style="color:#719e07">&amp;&amp;</span> isIncluded<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> includedPackages<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isExcludedByClassLoader<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> classLoader<span style="color:#719e07">,</span> onlyExtensionClassLoaderPackages<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据类全路径加载类到内存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadClass<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> resourceURL<span style="color:#719e07">,</span> Class<span style="color:#719e07">.</span>forName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">,</span> classLoader<span style="color:#719e07">),</span> name<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> IllegalStateException e <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load extension class (interface: &#34;</span> <span style="color:#719e07">+</span> type <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;, class line: &#34;</span> <span style="color:#719e07">+</span> line <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) in &#34;</span> <span style="color:#719e07">+</span> resourceURL <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, cause: &#34;</span> <span style="color:#719e07">+</span> t<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> exceptions<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>line<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable t<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Exception occurred when loading extension class (interface: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, class file: &#34;</span> <span style="color:#719e07">+</span> resourceURL <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) in &#34;</span> <span style="color:#719e07">+</span> resourceURL<span style="color:#719e07">,</span> t<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中的loadClass方法加载具体的类到内存</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadClass</span><span style="color:#719e07">(</span>Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses<span style="color:#719e07">,</span> java<span style="color:#719e07">.</span>net<span style="color:#719e07">.</span>URL resourceURL<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">,</span> String name<span style="color:#719e07">,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> NoSuchMethodException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前clazz是否为type的子类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这里第一次访问到的type是ExtensionInjector,clazz是SpringExtensionInjector 父子类型关系满足情况 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>type<span style="color:#719e07">.</span>isAssignableFrom<span style="color:#719e07">(</span>clazz<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Error occurred when loading extension class (interface: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, class line: &#34;</span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;), class &#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is not subtype of interface.&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展子类型是否存在这个注解@Adaptive |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>clazz<span style="color:#719e07">.</span>isAnnotationPresent<span style="color:#719e07">(</span>Adaptive<span style="color:#719e07">.</span>class<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cacheAdaptiveClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isWrapperClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展子类型构造器中是否有这个类型的接口 (这个可以想象下我们了解的Java IO流中的类型使用到的装饰器模式 构造器传个类型) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheWrapperClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//无自适应注解,也没有构造器是扩展类型参数 ,这个name我们在扩展文件中找到了就是等号前面那个 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>name<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//低版本中可以使用@Extension 扩展注解来标注扩展类型,这里获取注解有两个渠道: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//先查询@Extension注解是否存在如果存在则取value值,如果不存在@Extension注解则获取当前类型的名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> name <span style="color:#719e07">=</span> findAnnotationName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>name<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;No such extension name for the class &#34;</span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in the config &#34;</span> <span style="color:#719e07">+</span> resourceURL<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取扩展名字数组,扩展名字可能为逗号隔开的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String<span style="color:#719e07">[]</span> names <span style="color:#719e07">=</span> NAME_SEPARATOR<span style="color:#719e07">.</span>split<span style="color:#719e07">(</span>name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ArrayUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span>names<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//@Activate注解修饰的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheActivateClass<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> names<span style="color:#719e07">[</span>0<span style="color:#719e07">]);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String n <span style="color:#719e07">:</span> names<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//cachedNames缓存集合缓存当前扩展类型的扩展名字 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cacheName<span style="color:#719e07">(</span>clazz<span style="color:#719e07">,</span> n<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将扩展类型加入结果集合extensionClasses中,不允许覆盖的话出现同同名字扩展将抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> saveInExtensionClass<span style="color:#719e07">(</span>extensionClasses<span style="color:#719e07">,</span> clazz<span style="color:#719e07">,</span> n<span style="color:#719e07">,</span> overridden<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中cacheAdaptiveClass |
| Adaptive 机制,即扩展类的自适应机制。即其可以指定想要加载的扩展名,也可以不指定。若不指定,则直接加载默认的扩展类。即其会自动匹配,做到自适应。其是通过@Adaptive注解实现的。 |
| 自适应注解修饰的扩展同一个扩展名字只能有一个扩展实现类型, 扩展策略中提供的参数overridden是否允许覆盖扩展覆盖</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">cacheAdaptiveClass</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> overridden<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedAdaptiveClass <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> overridden<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//成员变量存储这个自适应扩展类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedAdaptiveClass <span style="color:#719e07">=</span> clazz<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>cachedAdaptiveClass<span style="color:#719e07">.</span>equals<span style="color:#719e07">(</span>clazz<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;More than 1 adaptive class found: &#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> cachedAdaptiveClass<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, &#34;</span> <span style="color:#719e07">+</span> clazz<span style="color:#719e07">.</span>getName<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中cacheWrapperClass |
| Wrapper 机制,即扩展类的包装机制。就是对扩展类中的 SPI 接口方法进行增强,进行包装,是 AOP 思想的体现,是 Wrapper 设计模式的应用。一个 SPI 可以包含多个 Wrapper。这个也是可以同一个类型多个</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">cacheWrapperClass</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedWrapperClasses <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> cachedWrapperClasses <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashSet<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存这个Wrapper类型的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedWrapperClasses<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型中cacheActivateClass |
| Activate用于激活扩展类的。 这个扩展类型可以出现多个比如过滤器可以同一个扩展名字多个过滤器实现,所以不需要有override判断 |
| Activate 机制,即扩展类的激活机制。通过指定的条件来激活当前的扩展类。其是通过@Activate 注解实现的。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">cacheActivateClass</span>(Class<span style="color:#719e07">&lt;?&gt;</span> clazz, String name) { |
| </span></span><span style="display:flex;"><span> Activate activate <span style="color:#719e07">=</span> clazz.getAnnotation(Activate.<span style="color:#719e07">class</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (activate <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存Activate类型的扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedActivates.put(name, activate); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// support com.alibaba.dubbo.common.extension.Activate |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> com.alibaba.dubbo.common.extension.Activate oldActivate <span style="color:#719e07">=</span> clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.<span style="color:#719e07">class</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (oldActivate <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> cachedActivates.put(name, oldActivate); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>ExtensionLoader类型中的saveInExtensionClass方法</p> |
| <p>上面扩展对象加载了这么多最终的目的就是将这个扩展类型存放进结果集合extensionClasses中,扩展策略中提供的参数overridden是否允许覆盖扩展覆盖</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">saveInExtensionClass</span>(Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses, Class<span style="color:#719e07">&lt;?&gt;</span> clazz, String name, boolean overridden) { |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;</span> c <span style="color:#719e07">=</span> extensionClasses.get(name); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (c <span style="color:#719e07">==</span> null <span style="color:#719e07">||</span> overridden) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//上面扩展对象加载了这么多最终的目的就是将这个扩展类型存放进结果集合中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extensionClasses.put(name, clazz); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> (c <span style="color:#719e07">!=</span> clazz) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// duplicate implementation is unacceptable |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> unacceptableExceptions.add(name); |
| </span></span><span style="display:flex;"><span> String duplicateMsg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Duplicate extension &#34;</span> <span style="color:#719e07">+</span> type.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; name &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; on &#34;</span> <span style="color:#719e07">+</span> c.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; and &#34;</span> <span style="color:#719e07">+</span> clazz.getName(); |
| </span></span><span style="display:flex;"><span> logger.error(duplicateMsg); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException(duplicateMsg); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h2 id="53-自适应扩展代理对象的代码生成与编译">5.3 自适应扩展代理对象的代码生成与编译</h2> |
| <h3 id="531-自适应扩展对象的创建">5.3.1 自适应扩展对象的创建</h3> |
| <p>Dubbo 的<strong>自适应扩展机制</strong>中如果 <strong>自己生成了自适应扩展的代理类</strong></p> |
| <p>Dubbo 的自适应扩展为了做什么:<strong>在运行时动态调用扩展方法</strong>。以及怎么做的:生成扩展代理类。比如: 代理类中根据 URL 获取扩展名,使用 SPI 加载扩展类,并调用同名方法,返回执行结果。</p> |
| <p>看了上一个章节,我们了解到了Dubbo是如何通过扫描目录来查询扩展实现类的这一次我们看下扩展类我们找到了之后,如果这个扩展类型未加上这个@Adaptive注解那么是如何创建这个类型的,接下来看createAdaptiveExtensionClass方法,这个方法是借助字节码工具来动态生成所需要的扩展类型的包装类型的代码,这个代码在编译时我们可能看不到,但是在Debug的时候,我们还是可以看到这个对象名字的,但是往往Debug的时候又进不到具体的代码位置,这里可以注意下</p> |
| <p>当扩展点的方法被@Adaptive修饰时,在Dubbo初始化扩展点时会自动生成和编译一个动态的Adaptive类。</p> |
| <p>下面我们可以以interface org.apache.dubbo.rpc.Protocol 这个协议扩展类型来看 协议扩展类型目前没有一个是带有自适应注解的</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> Class<span style="color:#719e07">&lt;?&gt;</span> createAdaptiveExtensionClass<span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Adaptive Classes&#39; ClassLoader should be the same with Real SPI interface classes&#39; ClassLoader |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ClassLoader classLoader <span style="color:#719e07">=</span> type<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// //native配置 是否为本地镜像(可以参考官方文档:https://dubbo.apache.org/zh-cn/docs/references/graalvm/support-graalv |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>NativeUtils<span style="color:#719e07">.</span>isNative<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> classLoader<span style="color:#719e07">.</span>loadClass<span style="color:#719e07">(</span>type<span style="color:#719e07">.</span>getName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;$Adaptive&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable ignore<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个代码生成器,来生成代码 详细内容我们就下一章来看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String code <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> AdaptiveClassCodeGenerator<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> cachedDefaultName<span style="color:#719e07">).</span>generate<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取编译器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>compiler<span style="color:#719e07">.</span>Compiler compiler <span style="color:#719e07">=</span> extensionDirector<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> org<span style="color:#719e07">.</span>apache<span style="color:#719e07">.</span>dubbo<span style="color:#719e07">.</span>common<span style="color:#719e07">.</span>compiler<span style="color:#719e07">.</span>Compiler<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成的代码进行编译 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> compiler<span style="color:#719e07">.</span>compile<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> code<span style="color:#719e07">,</span> classLoader<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="54-为扩展对象的set方法注入自适应扩展对象">5.4 为扩展对象的set方法注入自适应扩展对象</h2> |
| <p>在4.4.5小节中我们已经讲解了获取扩展类型实现类, 创建扩展对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> T instance <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>T<span style="color:#719e07">)</span> getAdaptiveExtensionClass<span style="color:#719e07">().</span>newInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>接下来就让我们来看下为扩展对象的set方法注入自适应的扩展对象 |
| 调用方法代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象之前的回调方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>injectExtension<span style="color:#719e07">(</span>instance<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>ExtensionLoader类型的injectExtension方法具体代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> T <span style="color:#268bd2">injectExtension</span>(T instance) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果注入器为空则直接返回当前对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (injector <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取当前对象的当前类的所有方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> (Method method : instance.getClass().getMethods()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否为set方法 不是的话则跳过,在这里合法的set方法满足3个条件: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//set开头,参数只有一个,public修饰 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isSetter(method)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Check {@link DisableInject} to see if we need auto injection for this property |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//方法上面是否有注解DisableInject修饰,这种情况也直接跳过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (method.isAnnotationPresent(DisableInject.<span style="color:#719e07">class</span>)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//方法的参数如果是原生类型也跳过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Class<span style="color:#719e07">&lt;?&gt;</span> pt <span style="color:#719e07">=</span> method.getParameterTypes()[<span style="color:#2aa198">0</span>]; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ReflectUtils.isPrimitives(pt)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取set方法对应的成员变量如setProtocol 属性为protocol |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String property <span style="color:#719e07">=</span> getSetterProperty(method); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据参数类型如Protocol和属性名字如protocol获取应该注入的对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Object object <span style="color:#719e07">=</span> injector.getInstance(pt, property); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (object <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//执行对应对象和对应参数的这个方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> method.invoke(instance, object); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> logger.error(<span style="color:#2aa198">&#34;Failed to inject via method &#34;</span> <span style="color:#719e07">+</span> method.getName() |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; of interface &#34;</span> <span style="color:#719e07">+</span> type.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;: &#34;</span> <span style="color:#719e07">+</span> e.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> logger.error(e.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="541-获取注入对象">5.4.1 获取注入对象</h3> |
| <p>这里我们主要来看下如何通过注入器找到需要注入的那个对象 调用代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> Object object <span style="color:#719e07">=</span> injector.getInstance(pt, property); |
| </span></span></code></pre></div><p>在前面看注入器扩展对象的获取的时候是会获取到ExtensionInjector扩展的一个自适应扩展注入器实现类型 AdaptiveExtensionInjector,这个地方对应的getInstance也是这个扩展里面的,我们来看下它的方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T <span style="color:#268bd2">getInstance</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">,</span> String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的扩展注入器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ExtensionInjector injector <span style="color:#719e07">:</span> injectors<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的扩展注入器,如果可以获取到扩展对象则直接返回 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T extension <span style="color:#719e07">=</span> injector<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>extension <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extension<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看到上面代码按扩展注入器顺序来遍历的第一个找到的对象就直接返回了,</p> |
| <p>这个AdaptiveExtensionInjector在初始化的时候会获取所有的ExtensionInjector的扩展,非自适应的,它本身自适应的扩展,这里会获取非自适应的扩展列表一共有3个按顺序为:</p> |
| <ul> |
| <li>ScopeBeanExtensionInjector</li> |
| <li>SpiExtensionInjector</li> |
| <li>SpringExtensionInjector</li> |
| </ul> |
| <p>接下来我们详细看下每种扩展注入器加载扩展对象的策略:</p> |
| <h3 id="542-域模型中的bean扩展注入器scopebeanextensioninjector">5.4.2 域模型中的Bean扩展注入器ScopeBeanExtensionInjector</h3> |
| <p>ScopeBeanExtensionInjector的getInstance方法: |
| 每个域模型都会有个ScopeBeanFactory类型的对象用于存储共享对象,并且域模型之间按照层级子类型的Bean工厂可以从父域的Bean工厂中查询对象,</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>@Override |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getInstance(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> beanFactory.getBean(name, type); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>ScopeBeanFactory的getBean方法 |
| 先从当前域空间查询对象,如果找不到对应类型的扩展对象则从父域工厂查询扩展对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getBean(String name, Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前域下注册的扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T bean <span style="color:#719e07">=</span> getBeanInternal(name, type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (bean <span style="color:#719e07">==</span> null <span style="color:#719e07">&amp;&amp;</span> parent <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//父域中查找扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> parent.getBean(name, type); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> bean; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>ScopeBeanFactory的getBeanInternal方法 |
| 从当前域下找注册的参数类型的对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getBeanInternal(String name, Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type) { |
| </span></span><span style="display:flex;"><span> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// All classes are derived from java.lang.Object, cannot filter bean by it |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (type <span style="color:#719e07">==</span> Object.<span style="color:#719e07">class</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>BeanInfo<span style="color:#719e07">&gt;</span> candidates <span style="color:#719e07">=</span> null; |
| </span></span><span style="display:flex;"><span> BeanInfo firstCandidate <span style="color:#719e07">=</span> null; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历列表查询 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> (BeanInfo beanInfo : registeredBeanInfos) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if required bean type is same class/superclass/interface of the registered bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (type.isAssignableFrom(beanInfo.instance.getClass())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEquals(beanInfo.name, name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) beanInfo.instance; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// optimize for only one matched bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (firstCandidate <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> firstCandidate <span style="color:#719e07">=</span> beanInfo; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (candidates <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> candidates <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> candidates.add(firstCandidate); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> candidates.add(beanInfo); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if bean name not matched and only single candidate |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (candidates <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (candidates.size() <span style="color:#719e07">==</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) candidates.get(<span style="color:#2aa198">0</span>).instance; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (candidates.size() <span style="color:#719e07">&gt;</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> candidateBeanNames <span style="color:#719e07">=</span> candidates.stream().map(beanInfo <span style="color:#719e07">-&gt;</span> beanInfo.name).collect(Collectors.toList()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> ScopeBeanException(<span style="color:#2aa198">&#34;expected single matching bean but found &#34;</span> <span style="color:#719e07">+</span> candidates.size() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; candidates for type [&#34;</span> <span style="color:#719e07">+</span> type.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;]: &#34;</span> <span style="color:#719e07">+</span> candidateBeanNames); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (firstCandidate <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) firstCandidate.instance; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="543-spi扩展机制注入器spiextensioninjector">5.4.3 SPI扩展机制注入器SpiExtensionInjector</h3> |
| <p>SPI是Dubbo自行实现的一套扩展机制,我们来看下它是如何查找扩展对象的</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>@Override |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getInstance(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果是一个标准的被@SPI注解修饰的扩展接口则满足条件 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (type.isInterface() <span style="color:#719e07">&amp;&amp;</span> type.isAnnotationPresent(SPI.<span style="color:#719e07">class</span>)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用扩展访问器来获取对应类型的扩展加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> loader <span style="color:#719e07">=</span> extensionAccessor.getExtensionLoader(type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (loader <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用对应类型的扩展加载器来加载自适应扩展 这个加载的扩展可以参考4.4.6小节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>loader.getSupportedExtensions().isEmpty()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader.getAdaptiveExtension(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="544-spring扩展注入器">5.4.4 Spring扩展注入器</h3> |
| <p>SpringExtensionInjector</p> |
| <p>Spring扩展注入器主要是用来从Spring容器中查询当前类型的Bean是否存在的,如下代码直接看代码吧</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>@Override |
| </span></span><span style="display:flex;"><span> @SuppressWarnings(<span style="color:#2aa198">&#34;unchecked&#34;</span>) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getInstance(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, String name) { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (context <span style="color:#719e07">==</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ignore if spring context is not bound |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check @SPI annotation ,类型需要满足SPI机制 @SPI修饰的接口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (type.isInterface() <span style="color:#719e07">&amp;&amp;</span> type.isAnnotationPresent(SPI.<span style="color:#719e07">class</span>)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从Spring容器中查询Bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> T bean <span style="color:#719e07">=</span> getOptionalBean(context, name, type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (bean <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> bean; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//logger.warn(&#34;No spring extension (bean) named:&#34; + name + &#34;, try to find an extension (bean) of type &#34; + type.getName()); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#719e07">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> T getOptionalBean(ListableBeanFactory beanFactory, String name, Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//要搜索的扩展名字为空就根据类型搜索 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//返回与给定类型(包括子类)匹配的bean的名称,对于FactoryBeans |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String[] beanNamesForType <span style="color:#719e07">=</span> beanFactory.getBeanNamesForType(type, <span style="color:#b58900">true</span>, <span style="color:#b58900">false</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (beanNamesForType <span style="color:#719e07">!=</span> null) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (beanNamesForType.length <span style="color:#719e07">==</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//返回指定bean的实例,该实例可以是共享的,也可以是独立的。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//根据Bean Name和类型 查询具体的扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> beanFactory.getBean(beanNamesForType[<span style="color:#2aa198">0</span>], type); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#268bd2">if</span> (beanNamesForType.length <span style="color:#719e07">&gt;</span> <span style="color:#2aa198">1</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException(<span style="color:#2aa198">&#34;Expect single but found &#34;</span> <span style="color:#719e07">+</span> beanNamesForType.length <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; beans in spring context: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> Arrays.toString(beanNamesForType)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展名字不为空则直接通过名字搜索Bean |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> (beanFactory.containsBean(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> beanFactory.getBean(name, type); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> null; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/5-dubbo-de-spi-kuo-zhan-ji-zhi-yu-zi-gua-ying-kuo-zhan-dui-xiang-de-chuang-jian-yu-kuo-zhan-wen-jian-de-sao-miao-yuan-ma-jie-xi/">《自适应扩展对象的创建getAdaptiveExtension方法》</a></p></description></item><item><title>Blog: 04-Dubbo的扩展机制</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/04/04-dubbo%E7%9A%84%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6/</link><pubDate>Thu, 04 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/04/04-dubbo%E7%9A%84%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6/</guid><description> |
| <h1 id="4-dubbo的扩展机制">4-Dubbo的扩展机制</h1> |
| <h2 id="41-回顾我们前面使用到扩展场景">4.1 回顾我们前面使用到扩展场景</h2> |
| <p>在上一章中我们初始化应用模型对象的时候,了解到有几个地方用到了扩展机制来创建对象,这一章我们会详细来讲一下这个扩展对象的加载过程,这里我们先来回顾下哪些地方用到了扩展机制:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// 使用扩展机制获取TypeBuilder |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>Set<span style="color:#719e07">&lt;</span>TypeBuilder<span style="color:#719e07">&gt;</span> tbs <span style="color:#719e07">=</span> model<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>TypeBuilder<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//获取域模型初始化器ScopeModelInitializer扩展对象,执行初始化方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// OrderedPropertiesConfiguration 中获取有序配置提供器对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>ExtensionLoader<span style="color:#719e07">&lt;</span>OrderedPropertiesProvider<span style="color:#719e07">&gt;</span> propertiesProviderExtensionLoader <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>OrderedPropertiesProvider<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// ApplicationModel中获取配置管理器对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> configManager <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ConfigManager<span style="color:#719e07">)</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationExt<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>ConfigManager<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//ModuleModel中获取模块扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>Set<span style="color:#719e07">&lt;</span>ModuleExt<span style="color:#719e07">&gt;</span> exts <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ModuleExt<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// ApplicationModel中获Environment对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>environment <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>Environment<span style="color:#719e07">)</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationExt<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>Environment<span style="color:#719e07">.</span>NAME<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// ApplicationModel中获取应用初始化监听器ApplicationInitListener扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>ExtensionLoader<span style="color:#719e07">&lt;</span>ApplicationInitListener<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationInitListener<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span>Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> listenerNames <span style="color:#719e07">=</span> extensionLoader<span style="color:#719e07">.</span>getSupportedExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//ScopeModel中创建扩展访问器: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ExtensionDirector<span style="color:#719e07">(</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent<span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> scope<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>有了以上的应用场景我们可以来看下扩展机制了</p> |
| <h2 id="42-为什么要用到扩展机制">4.2 为什么要用到扩展机制?</h2> |
| <p>为什么要用到扩展这个想必每个编程人员都比较了解,一个好的程序是要遵循一定的设计规范比如设计模式中的<strong>开闭原则</strong> 英文全称是 Open Closed Principle,简写为 OCP,对扩展开放、对修改关闭:</p> |
| <p><strong>对扩展开放:</strong> 指的是我们系统中的模块、类、方法对它们的提供者(开发者)应该是开放的,提供者可以对系统进行扩展(新增)新的功能。</p> |
| <p><strong>对修改关闭:</strong> 指的是系统中的模块、类、方法对它们的使用者(调用者)应该是关闭的。使用者使用这些功能时,不会因为提供方新增了功能而导致使用者也进行相应修改。</p> |
| <p>我们再来了解下Dubbo的一些基本特性: |
| 下面这句话是我摘自官网的: |
| <em>Apache Dubbo 是一款微服务开发框架,它提供了 <strong>RPC通信</strong> 与 <strong>微服务治理</strong> 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在<strong>任意功能点去定制自己的实现</strong>,以改变框架的默认行为来满足自己的业务需求。 |
| Dubbo3 基于 Dubbo2 演进而来,在保持原有核心功能特性的同时, Dubbo3 在易用性、超大规模微服务实践、云原生基础设施适配、安全设计等几大方向上进行了全面升级。 以下文档都将基于 Dubbo3 展开。</em></p> |
| <p><strong>对修改关闭的地方:</strong> 对于Apache Dubbo来说 不变的是RPC调用流程,微服务治理这些抽象的概念,我们可以用摘自官网的下面几个图表示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-arch.png" alt="在这里插入图片描述"></p> |
| <center>图4.1 Dubbo架构图</center> |
| <p>再来看一个调用链路的架构图</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-arch2.png" alt="在这里插入图片描述"></p> |
| <center>图4.2 Dubbo RPC调用链路</center> |
| <br/> |
| 上面两个图整体来看都是Dubbo不变的地方涉及到服务的RPC调用和服务治理的一些概念与流程,但是对于每个环节又可以使用各种方式实现,比如序列化机制可以是Json,Java序列化,Hession2或者Protobuf等等,网络传输层可以是netty实现的tcp通信,也可以使用http协议,那Dubbo又是如何封装不变部分扩展这种可变部分呢?,那就是接下来要说的**微内核机制**,这个我们待会说 |
| <p><strong>对扩展开放:</strong> : 对于Apache Dubbo来说 变化的是RPC调用流程和微服务治理这些抽象的概念的具体实现,每个点应该用什么技术实现,又是用什么场景,这个可以用如下图来表示下: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-arch3.png" alt="在这里插入图片描述"></p> |
| <center>图4.3 Dubbo的扩展生态</center> |
| </br> |
| <p>看到这里 应该各位就明白了,我们写程序是为了业务,而针对不同的业务需求很多场景下我们是需要使用不同的实现来满足的,Dubbo使用微内核的架构,将具体的实现开放出来,让使用者可以根据自己的需求来选择,定制. Dubbo开放了很多的扩展点供大家扩展,可想而知使用Dubbo的灵活性是非常高的。</p> |
| <p><strong>微内核架构:</strong> |
| 微内核架构由两大架构模块组成:<strong>核心系统</strong>与<strong>插件模块</strong>,设计一个微内核体系关键工作全部集中于核心系统怎么构建。 |
| <strong>核心系统</strong> : 负责和具体业务功能无关的通用功能,例如模块加载、模块间通信等,这个其实对应着Dubbo的SPI机制。 |
| <strong>插件模块</strong> : 负责实现具体的业务逻辑,Dubbo,SPI接口与实现。</p> |
| <h2 id="43-dubbo的扩展机制包含了哪些重要的组成部分">4.3 Dubbo的扩展机制包含了哪些重要的组成部分?</h2> |
| <p>前面我们说了为什么要使用扩展机制,这里我们来看下具体实现</p> |
| <p>先将扩展包里面的代码截个图认识认识各类型的单词 |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-extension.png" alt="在这里插入图片描述"></p> |
| <p>顺便我们先简单看下类结构图,后续再详细看每个类型的解释: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/4-dubbo-extension2.png" alt="在这里插入图片描述"></p> |
| <p>为了后续看具体的扩展加载流程我们先看下以上类型的解释说明:</p> |
| <ul> |
| <li> |
| <p><strong>ExtensionAccessor</strong></p> |
| <ul> |
| <li>扩展的统一访问器</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionDirector</strong></p> |
| <ul> |
| <li>ExtensionDirector是一个作用域扩展加载程序管理器。</li> |
| <li>ExtensionDirector支持多个级别,子级可以继承父级的扩展实例。 |
| 查找和创建扩展实例的方法类似于Java classloader。</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionScope</strong></p> |
| <ul> |
| <li>扩展SPI域,目前有FRAMEWORK,APPLICATION,MODULE,SELF</li> |
| <li><strong>FRAMEWORK</strong> : 扩展实例在框架内使用,与所有应用程序和模块共享。 |
| 框架范围SPI扩展只能获取FrameworkModel,无法获取ApplicationModel和ModuleModel。 |
| 考虑: |
| 一些SPI需要在框架内的应用程序之间共享数据 |
| 无状态SPI在框架内是安全共享的</li> |
| <li><strong>APPLICATION</strong> 扩展实例在一个应用程序中使用,与应用程序的所有模块共享,不同的应用程序创建不同的扩展实例。 |
| 应用范围SPI扩展可以获取FrameworkModel和ApplicationModel,无法获取ModuleModel。 |
| 考虑: |
| 在框架内隔离不同应用程序中的扩展数据 |
| 在应用程序内部的所有模块之间共享扩展数据</li> |
| <li><strong>MODULE</strong> 扩展实例在一个模块中使用,不同的模块创建不同的扩展实例。 |
| 模块范围SPI扩展可以获得FrameworkModel、ApplicationModel和ModuleModel。 |
| 考虑: |
| 隔离应用程序内部不同模块中的扩展数据</li> |
| <li><strong>SELF</strong> 自给自足,为每个作用域创建一个实例,用于特殊的SPI扩展,如ExtensionInjector</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionLoader</strong></p> |
| <ul> |
| <li>ApplicationModel、DubboBootstrap和这个类目前被设计为单例或静态(本身完全静态或使用一些静态字段)。因此,从它们返回的实例属于process或classloader范围。如果想在一个进程中支持多个dubbo服务器,可能需要重构这三个类。</li> |
| <li>加载dubbo扩展</li> |
| <li>自动注入依赖项扩展</li> |
| <li>包装器中的自动包装扩展</li> |
| <li>默认扩展是一个自适应实例</li> |
| <li>JDK自带SPI参考地址 <a href="https://docs.oracle.com/javase/1.5.0/docs/guide/jar/jar.html#Service%20Provider">点击查看</a></li> |
| <li>@SPI 服务扩展接口 详细内容看后面</li> |
| <li>@Adaptive自适应扩展点注解 详细内容看后面</li> |
| <li>@Activate自动激活扩展点注解 详细内容看后面</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionPostProcessor</strong></p> |
| <ul> |
| <li>在扩展初始化之前或之后调用的后处理器。</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>LoadingStrategy</strong></p> |
| <ul> |
| <li>扩展加载策略,目前有3个扩展加载策略分别从不同文件目录加载扩展</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>DubboInternalLoadingStrategy</strong></p> |
| <ul> |
| <li>Dubbo内置的扩展加载策略,将加载文件目录为META-INF/dubbo/internal/的扩展</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>DubboLoadingStrategy</strong></p> |
| <ul> |
| <li>Dubbo普通的扩展加载策略,将加载目录为META-INF/dubbo/的扩展</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ServicesLoadingStrategy</strong></p> |
| <ul> |
| <li>JAVA SPI加载策略 ,将加载目录为META-INF/services/的扩展</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>Wrapper</strong>注解</p> |
| </li> |
| <li> |
| <p>SPI注解</p> |
| </li> |
| <li> |
| <p><strong>ExtensionInjector</strong>接口</p> |
| <ul> |
| <li>为SPI扩展提供资源的注入器。</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ExtensionAccessorAware</strong></p> |
| <ul> |
| <li>SPI扩展可以实现这个感知接口,以获得适当的xtensionAccessor实例。</li> |
| </ul> |
| </li> |
| <li> |
| <p>DisableInject注解</p> |
| </li> |
| <li> |
| <p><strong>AdaptiveClassCodeGenerator</strong></p> |
| <ul> |
| <li>自适应类的代码生成器</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>Adaptive</strong>注解</p> |
| </li> |
| <li> |
| <p>为ExtensionLoader注入依赖扩展实例提供有用信息。</p> |
| </li> |
| <li> |
| <p><strong>Activate</strong>注解</p> |
| <ul> |
| <li>Activate。此注解对于使用给定条件自动激活某些扩展非常有用,例如:@Activate可用于在有多个实现时加载某些筛选器扩展。 |
| **group()**指定组条件。框架SPI定义了有效的组值。 |
| **value()**指定URL条件中的参数键。 |
| SPI提供程序可以调用ExtensionLoader。getActivateExtension(URL、String、String)方法以查找具有给定条件的所有已激活扩展。</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ActivateComparator</strong></p> |
| <ul> |
| <li>Activate扩展的排序器</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>MultiInstanceActivateComparator</strong></p> |
| </li> |
| <li> |
| <p><strong>WrapperComparator</strong></p> |
| </li> |
| <li> |
| <p><strong>AdaptiveExtensionInjector</strong></p> |
| </li> |
| <li> |
| <p><strong>SpiExtensionInjector</strong></p> |
| </li> |
| </ul> |
| <h2 id="44-扩展加载创建之前的调用过程">4.4 扩展加载创建之前的调用过程</h2> |
| <h3 id="441-扩展的调用代码示例">4.4.1 扩展的调用代码示例</h3> |
| <p>了解了这么多与扩展相关的概念,接下来我们就来从前面的代码调用中找几个例子来看下扩展的调用过程:</p> |
| <p>代码来源于FrameworkModel对象的初始化initialize()中的如下代码调用:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> TypeDefinitionBuilder<span style="color:#719e07">.</span>initBuilders<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>TypeDefinitionBuilder中初始化类型构建器代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initBuilders</span><span style="color:#719e07">(</span>FrameworkModel model<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>TypeBuilder<span style="color:#719e07">&gt;</span> tbs <span style="color:#719e07">=</span> model<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>TypeBuilder<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> BUILDERS <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>tbs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="442-dubbo的分层模型获取扩展加载器对象">4.4.2 Dubbo的分层模型获取扩展加载器对象</h3> |
| <p>以上扩展调用的时候对于扩展加载器对象的获取代码如下所示,我们来看下它的调用链路</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>model<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>TypeBuilder<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span></code></pre></div><p>getExtensionLoader方法来源于FrameworkModel类型的父类型ScopeModel的实现的接口ExtensionAccessor中的默认方法(JDK8 默认方法)</p> |
| <p>ExtensionAccessor接口中的getExtensionLoader方法如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">default</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getExtensionLoader</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">().</span>getExtensionLoader<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>获取扩展加载器之前需要先获取扩展访问器: |
| 这里的链路先梳理下:</p> |
| <p><strong>模型对象(FrameworkModel)-</strong>&ndash;&gt; <strong>扩展访问器(ExtensionAccessor)</strong> &mdash;&gt; <strong>作用域扩展加载程序管理器(ExtensionDirector)</strong> &mdash;&gt;</p> |
| <p>这个getExtensionDirector()方法来源于FrameworkModel的抽象父类型ScopeModel中的getExtensionDirector()如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> ExtensionDirector <span style="color:#268bd2">getExtensionDirector</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extensionDirector<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里直接返回了extensionDirector,,不知道介绍到这里记得这个扩展加载程序管理器extensionDirector对象的由来不, 在上个章节<a href="https://blog.elastic.link/2022/07/10/dubbo/3-kuang-jia-ying-yong-cheng-xu-mo-kuai-ling-yu-mo-xing-model-dui-xiang-de-chu-shi-hua/">《3-框架,应用程序,模块领域模型Model对象的初始化》</a>中3.2.2 初始化ScopeModel的章节中的ScopeModel类型的初始化方法initialize()方法中我们提到过这个对象的创建,具体代码如下所示(这个代码比较简单):</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ExtensionDirector<span style="color:#719e07">(</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent<span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> scope<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>我们继续前面getExtensionLoader(type)方法调用逻辑,前面我们知道了这个扩展访问器的对象是ExtensionDirector,接下来我们看下ExtensionDirector中获取扩展加载器的代码(如下所示): |
| 在详细介绍扩展加载器对象获取之前我们先来看下当前我们要加载的扩展类型的源码,后续会用到: |
| 我们要加载的扩展类型TypeBuilder接口</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@SPI</span><span style="color:#719e07">(</span>scope <span style="color:#719e07">=</span> ExtensionScope<span style="color:#719e07">.</span>FRAMEWORK<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">TypeBuilder</span> <span style="color:#268bd2">extends</span> Prioritized <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Whether the build accept the class passed in. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">accept</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Build type definition with the type or class. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> TypeDefinition <span style="color:#268bd2">build</span><span style="color:#719e07">(</span>Type type<span style="color:#719e07">,</span> Class<span style="color:#719e07">&lt;?&gt;</span> clazz<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> TypeDefinition<span style="color:#719e07">&gt;</span> typeCache<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionDirector类型中获取扩展加载器的代码 |
| 这个代码非常有意思 <strong>其实就是前面说到的域模型架构的数据访问架构</strong>类似于JVM类加载器访问加载类的情况,但是这个顺序可能有所不同,Dubbo的扩展加载器是如何访问的呢? 遵循以下顺序:</p> |
| <ul> |
| <li>先从<strong>缓存中</strong>查询扩展加载器</li> |
| <li>如果前面没找到则查询扩展类型的scope所属域,如果是<strong>当前域扩展</strong>则从直接创建扩展加载器</li> |
| <li>如果前面没找到就从<strong>父扩展访问器</strong>中查询,查询这个扩展是否数据父扩展域</li> |
| <li>前面都没找到就尝试创建</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getExtensionLoader</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果扩展加载器已经被销毁则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里参数类型传的是TypeBuilder.class不为空 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>type <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Extension type == null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展类型不为接口也要抛出异常,这个TypeBuilder.class具体类型代码往上看,这个类型是一个接口 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>type<span style="color:#719e07">.</span>isInterface<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Extension type (&#34;</span> <span style="color:#719e07">+</span> type <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;) is not an interface!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个判断逻辑是判断这个扩展接口是有有@SPI注解,TypeBuilder是有的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>withExtensionAnnotation<span style="color:#719e07">(</span>type<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Extension type (&#34;</span> <span style="color:#719e07">+</span> type <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;) is not an extension, because it is NOT annotated with @&#34;</span> <span style="color:#719e07">+</span> SPI<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getSimpleName<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 1. find in local cache |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//被加载的扩展类型对应的扩展加载器会放到extensionLoadersMap这个ConcurrentHashMap类型的集合中方便缓存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> loader <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> extensionLoadersMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//查询扩展所属域,这个类型的扩展域是框架级别的ExtensionScope.FRAMEWORK |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//extensionScopeMap为ConcurrentHashMap类型的扩展域缓存集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionScope scope <span style="color:#719e07">=</span> extensionScopeMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scope <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> SPI annotation <span style="color:#719e07">=</span> type<span style="color:#719e07">.</span>getAnnotation<span style="color:#719e07">(</span>SPI<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> scope <span style="color:#719e07">=</span> annotation<span style="color:#719e07">.</span>scope<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> extensionScopeMap<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> scope<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//首次访问的时候当前类型的扩展加载器类型肯定是空的,会走如下两个逻辑中的其中一个进行创建扩展加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//1)如果 扩展域为SELF 自给自足,为每个作用域创建一个实例,用于特殊的SPI扩展,如{@link ExtensionInjector} |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> scope <span style="color:#719e07">==</span> ExtensionScope<span style="color:#719e07">.</span>SELF<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// create an instance in self scope |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loader <span style="color:#719e07">=</span> createExtensionLoader0<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 2. find in parent |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//3) 从父扩展加载器中查询当前扩展加载器是否存在,这里parent是空的先不考虑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> loader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>parent<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 3. create it |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//4) 这个是我们本次会走的逻辑,大部分是会走这个逻辑来创建扩展加载器对象的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> loader <span style="color:#719e07">=</span> createExtensionLoader<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>前面提到的withExtensionAnnotation判断代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">withExtensionAnnotation</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> type<span style="color:#719e07">.</span>isAnnotationPresent<span style="color:#719e07">(</span>SPI<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionDirector类型的createExtensionLoader方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">createExtensionLoader</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> loader <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前类型注解的scope与当前扩展访问器ExtensionDirector的scope是否一致,不一致则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//当前类型ExtensionDirector的scope是在构造器中传递的,在Model对象初始化的时候创建的本类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>isScopeMatched<span style="color:#719e07">(</span>type<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if scope is matched, just create it |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loader <span style="color:#719e07">=</span> createExtensionLoader0<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// if scope is not matched, ignore it |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionDirector类型的createExtensionLoader0方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">createExtensionLoader0</span><span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查当前扩展访问器是否被销毁掉了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> loader<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为当前扩展类型创建一个扩展访问器并缓存到,当前成员变量extensionLoadersMap中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extensionLoadersMap<span style="color:#719e07">.</span>putIfAbsent<span style="color:#719e07">(</span>type<span style="color:#719e07">,</span> <span style="color:#719e07">new</span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;(</span>type<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">,</span> scopeModel<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> loader <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;)</span> extensionLoadersMap<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>type<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="443-扩展加载器对象extensionloader的构造器">4.4.3 扩展加载器对象ExtensionLoader的构造器</h3> |
| <p>扩展加载器相对来说是比较复杂的实现内容比较多,用到哪里我们说下哪里,这里先来看ExtensionLoader的构造器代码如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">(</span>Class<span style="color:#719e07">&lt;?&gt;</span> type<span style="color:#719e07">,</span> ExtensionDirector extensionDirector<span style="color:#719e07">,</span> ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前扩展加载器,需要加载的扩展的类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>type <span style="color:#719e07">=</span> type<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建扩展加载器的扩展访问器对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector <span style="color:#719e07">=</span> extensionDirector<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从扩展访问器中获取扩展执行前后的回调器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionPostProcessors <span style="color:#719e07">=</span> extensionDirector<span style="color:#719e07">.</span>getExtensionPostProcessors<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建实例化对象的策略对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initInstantiationStrategy<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果当前扩展类型为扩展注入器类型则设置当前注入器变量为空,否则的话获取一个扩展注入器扩展对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>injector <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>type <span style="color:#719e07">==</span> ExtensionInjector<span style="color:#719e07">.</span>class <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">:</span> extensionDirector<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExtensionInjector<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getAdaptiveExtension<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建Activate注解的排序器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>activateComparator <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ActivateComparator<span style="color:#719e07">(</span>extensionDirector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为扩展加载器下的域模型对象赋值 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>先来看 创建实例化对象的策略对象代码 initInstantiationStrategy();</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initInstantiationStrategy</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ExtensionPostProcessor extensionPostProcessor <span style="color:#719e07">:</span> extensionPostProcessors<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ScopeModelAwareExtensionProcessor在域模型对象时候为扩展访问器添加了这个域模型扩展处理器对象ScopeModelAwareExtensionProcessor,这个类型实现了ScopeModelAccessor域模型访问器可以用来获取域模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>extensionPostProcessor <span style="color:#719e07">instanceof</span> ScopeModelAccessor<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> instantiationStrategy <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InstantiationStrategy<span style="color:#719e07">((</span>ScopeModelAccessor<span style="color:#719e07">)</span> extensionPostProcessor<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instantiationStrategy <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> instantiationStrategy <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InstantiationStrategy<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>再来看ExtensionInjector扩展对象的获取</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//1)这里有个type为空的判断,普通的扩展类型肯定不是ExtensionInjector类型 这里必定会为每个非扩展注入ExtensionInjector类型创建一个ExtensionInjector类型的扩展对象, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">//2) 这里代码会走extensionDirector.getExtensionLoader(ExtensionInjector.class)这一步进去之后的代码刚刚看过就不再看了,这个代码会创建一个为ExtensionInjector扩展对象的加载器对象ExtensionLoader |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">//3) getAdaptiveExtension() 这个方法就是通过扩展加载器获取具体的扩展对象的方法我们会详细说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>injector <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>type <span style="color:#719e07">==</span> ExtensionInjector<span style="color:#719e07">.</span>class <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">:</span> extensionDirector<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ExtensionInjector<span style="color:#719e07">.</span>class<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getAdaptiveExtension<span style="color:#719e07">());</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/4-dubbo-de-spi-kuo-zhan-ji-zhi-yu-extensionloader-dui-xiang-de-chuang-jian-yuan-ma-jie-xi/">&laquo;Dubbo的扩展机制&raquo;</a></p></description></item><item><title>Blog: 03-框架,应用程序,模块领域模型Model对象的初始化</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/03/03-%E6%A1%86%E6%9E%B6%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E6%A8%A1%E5%9D%97%E9%A2%86%E5%9F%9F%E6%A8%A1%E5%9E%8Bmodel%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96/</link><pubDate>Wed, 03 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/03/03-%E6%A1%86%E6%9E%B6%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E6%A8%A1%E5%9D%97%E9%A2%86%E5%9F%9F%E6%A8%A1%E5%9E%8Bmodel%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96/</guid><description> |
| <h1 id="3-框架应用程序模块领域模型model对象的初始化">3-框架,应用程序,模块领域模型Model对象的初始化</h1> |
| <p>在上一章中我们详细看了服务配置ServiceConfig类型的初始化,不过我们跳过了AbstractMethodConfig的构造器中创建模块模型对象的过程,那这一章我们就来看下模块模型对象的初始化过程:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractMethodConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">().</span>getDefaultModule<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>那为什么会在Dubbo3的新版本中加入这个域模型呢</strong>,主要有如下原因 |
| 之前dubbo都是只有一个作用域的,通过静态类 属性共享 |
| 增加域模型是为了:</p> |
| <ol> |
| <li>让Dubbo支持多应用的部署,这块一些大企业有诉求</li> |
| <li>从架构设计上,解决静态属性资源共享、清理的问题</li> |
| <li>分层模型将应用的管理和服务的管理分开</li> |
| </ol> |
| <p>可能比较抽象,可以具体点来看。Dubbo3中在启动时候需要启动配置中心、元数据中心,这个配置中心和元数据中心可以归应用模型来管理。Dubbo作为RPC框架又需要启动服务和引用服务,服务级别的管理就交给了这个模块模型来管理。分层次的管理方便我们理解和处理逻辑,父子级别的模型又方便了数据传递。</p> |
| <p>了解过JVM类加载机制的同学应该就比较清楚JVM类加载过程中的数据访问模型。子类加载器先交给父类加载器查找,找不到再从子类加载器中查找。Dubbo的分层模型类似这样一种机制,这一章先来简单了解下,后面用到时候具体细说。</p> |
| <h2 id="31-模型对象的关系">3.1 模型对象的关系</h2> |
| <p>为了不增加复杂性,我们这里仅仅列出模型对象类型类型之间的继承关系如下所示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/3-model.png" alt="在这里插入图片描述"></p> |
| <center>图3.1 模型对象的继承关系</center> |
| <p>模型对象一共有4个,公共的属性和操作放在了域模型类型中,下面我们来详细说下这几个模型类型:</p> |
| <ul> |
| <li> |
| <p><strong>ExtensionAccessor</strong> 扩展的统一访问器</p> |
| <ul> |
| <li>用于获取扩展加载管理器ExtensionDirector对象</li> |
| <li><strong>获取扩展对象ExtensionLoader</strong></li> |
| <li>根据扩展名字<strong>获取具体扩展对象</strong></li> |
| <li>获取自适应扩展对象</li> |
| <li>获取默认扩展对象</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ScopeModel</strong> 模型对象的公共抽象父类型</p> |
| <ul> |
| <li>内部id用于表示模型树的层次结构</li> |
| <li>公共模型名称,可以被用户设置</li> |
| <li>描述信息</li> |
| <li>类加载器管理</li> |
| <li>父模型管理parent</li> |
| <li>当前模型的所属域ExtensionScope有:<strong>FRAMEWORK(框架)</strong>,<strong>APPLICATION(应用)</strong>,<strong>MODULE(模块)</strong>,<strong>SELF(自给自足</strong>,为每个作用域创建一个实例,用于特殊的SPI扩展,如ExtensionInjector)</li> |
| <li>具体的扩展加载程序管理器对象的管理:<strong>ExtensionDirector</strong></li> |
| <li>域Bean工厂管理,一个内部共享的Bean工厂<strong>ScopeBeanFactory</strong></li> |
| <li>等等</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>FrameworkModel</strong> dubbo框架模型,可与多个应用程序共享</p> |
| <ul> |
| <li>FrameworkModel实例对象集合,allInstances</li> |
| <li>所有ApplicationModel实例对象集合,applicationModels</li> |
| <li>发布的ApplicationModel实例对象集合pubApplicationModels</li> |
| <li>框架的服务存储库<strong>FrameworkServiceRepository</strong>类型对象(数据存储在内存中)</li> |
| <li>内部的应用程序模型对象internalApplicationModel</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ApplicationModel</strong> 表示正在使用Dubbo的应用程序,并存储基本<strong>元数据信息</strong>,以便在RPC调用过程中使用。 |
| ApplicationModel包括许多关于<strong>发布服务</strong>的ProviderModel和许多关于订阅服务的Consumer Model。</p> |
| <ul> |
| <li>ExtensionLoader、DubboBootstrap和这个类目前被设计为单例或静态(本身完全静态或使用一些静态字段)。因此,从它们返回的实例属于流程范围。如果想在一个进程中支持多个dubbo服务器,可能需要重构这三个类。</li> |
| <li><strong>所有ModuleModel实例</strong>对象集合moduleModels</li> |
| <li><strong>发布的ModuleModel实例</strong>对象集合pubModuleModels</li> |
| <li><strong>环境信息Environment实例</strong>对象environment</li> |
| <li><strong>配置管理ConfigManager实例</strong>对象configManager</li> |
| <li><strong>服务存储库ServiceRepository实例</strong>对象serviceRepository</li> |
| <li><strong>应用程序部署器ApplicationDeployer实例</strong>对象deployer</li> |
| <li><strong>所属框架FrameworkModel实例</strong>对象frameworkModel</li> |
| <li><strong>内部的模块模型ModuleModel实例</strong>对象internalModule</li> |
| <li><strong>默认的模块模型ModuleModel实例</strong>对象defaultModule</li> |
| </ul> |
| </li> |
| <li> |
| <p><strong>ModuleModel</strong> 服务模块的模型</p> |
| <ul> |
| <li><strong>所属应用程序模型ApplicationModel实例</strong>对象applicationModel</li> |
| <li><strong>模块环境信息ModuleEnvironment实例</strong>对象moduleEnvironment</li> |
| <li><strong>模块服务存储库ModuleServiceRepository实例</strong>对象serviceRepository</li> |
| <li><strong>模块的服务配置管理ModuleConfigManager实例</strong>对象moduleConfigManager</li> |
| <li><strong>模块部署器ModuleDeployer实例</strong>对象deployer用于导出和引用服务</li> |
| </ul> |
| </li> |
| </ul> |
| <p>了解了这几个模型对象的关系我们可以了解到这几个模型对象的管理层级从框架到应用程序,然后到模块的管理(FrameworkModel-&gt;ApplicationModel-&gt;ModuleModel),他们主要用来针对框架,应用程序,模块的<strong>存储</strong>,<strong>发布管理,</strong>,<strong>配置管理</strong></p> |
| <p>看来Dubbo3 针对应用服务治理与运维这一块也是在努力尝试.</p> |
| <h3 id="311-abstractmethodconfig-配置对象中获取模型对象的调用">3.1.1 AbstractMethodConfig 配置对象中获取模型对象的调用</h3> |
| <p>模块模型(ModuleModel)参数对象的创建 |
| 这个AbstractMethodConfig构造器在初始化的时候调调用了这么一行代码做为参数向父类型传递对象.</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">().</span>getDefaultModule<span style="color:#719e07">()</span> |
| </span></span></code></pre></div><p>默认情况下使用ApplicationModel的静态方法获取默认的模型对象和默认的模块对象</p> |
| <p><strong>ApplicationModel</strong>(应用程序领域模型)类型中获取默认模型对象的方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> ApplicationModel <span style="color:#268bd2">defaultModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// should get from default FrameworkModel, avoid out of sync |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> FrameworkModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">().</span>defaultApplication<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>这里可以看到要想获取应用程序模型必须先通过框架领域模型来获取层级也是框架领域模型到应用程序领域模型</p> |
| <h3 id="312-使用双重校验锁获取框架模型对象">3.1.2 使用双重校验锁获取框架模型对象</h3> |
| <p>FrameworkModel(框架模型)的默认模型获取工厂方法defaultModel()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 在源码的注释上有这么一句话:在销毁默认的 FrameworkModel 时, FrameworkModel.defaultModel() |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> *或ApplicationModel.defaultModel() 将返回一个损坏的模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> *可能会导致不可预知的问题。建议:尽量避免使用默认模型。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> FrameworkModel <span style="color:#268bd2">defaultModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁的形式创建单例对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> FrameworkModel instance <span style="color:#719e07">=</span> defaultInstance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>globalLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重置默认框架模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> resetDefaultFrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultInstance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> defaultInstance <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> defaultInstance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>instance<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;Default FrameworkModel is null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="313-刷新重置默认框架模型对象">3.1.3 刷新重置默认框架模型对象</h3> |
| <p>FrameworkModel中的重置默认框架模型resetDefaultFrameworkModel</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">resetDefaultFrameworkModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//全局悲观锁,同一个时刻只能有一个线程执行重置操作 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>globalLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//defaultInstance为当前成员变量FrameworkModel类型代表当前默认的FrameworkModel类型的实例对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultInstance <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>defaultInstance<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> FrameworkModel oldDefaultFrameworkModel <span style="color:#719e07">=</span> defaultInstance<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在实例模型列表则直接从内存缓存中查后续不需要创建了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>allInstances<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前存在的有FrameworkModel框架实例多个列表则取第一个为默认的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> defaultInstance <span style="color:#719e07">=</span> allInstances<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> defaultInstance <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>oldDefaultFrameworkModel <span style="color:#719e07">!=</span> defaultInstance<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Reset global default framework from &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc<span style="color:#719e07">(</span>oldDefaultFrameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc<span style="color:#719e07">(</span>defaultInstance<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面单例做了很多的初始化操作,这里开始调用构造器来创建框架模型对象,如下代码:</p> |
| <h2 id="32--创建frameworkmodel对象">3.2 创建FrameworkModel对象</h2> |
| <p>FrameworkModel()构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">FrameworkModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用父类型ScopeModel传递参数,这个构造器的第一个参数为空代表这是一个顶层的域模型,第二个代表了这个是框架FRAMEWORK域,第三个false不是内部域 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span><span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> ExtensionScope<span style="color:#719e07">.</span>FRAMEWORK<span style="color:#719e07">,</span> <span style="color:#cb4b16">false</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内部id用于表示模型树的层次结构,如层次结构: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//FrameworkModel(索引=1)-&gt;ApplicationModel(索引=2)-&gt;ModuleModel(索引=1,第一个用户模块) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//这个index变量是static类型的为静态全局变量默认值从1开始,如果有多个框架模型对象则internalId编号从1开始依次递增 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>setInternalId<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>index<span style="color:#719e07">.</span>getAndIncrement<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register FrameworkModel instance early |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将当前新创建的框架实例对象添加到容器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>globalLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将当前框架模型实例添加到所有框架模型缓存对象中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> allInstances<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如上面代码所示重置默认的框架模型对象,这里将会是缓存实例列表的第一个,新增了一个刷新默认实例对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> resetDefaultFrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getDesc<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is created&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化框架模型领域对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ExtensionScope.FRAMEWORK</p> |
| <h3 id="321-初始化frameworkmodel">3.2.1 初始化FrameworkModel</h3> |
| <p>FrameworkModel框架模型的初始化方法initialize()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里初始化之前先调用下父类型ScopeModel的初始化方法我们在下面来看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用TypeDefinitionBuilder的静态方法initBuilders来初始化类型构建器TypeBuilder类型集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> TypeDefinitionBuilder<span style="color:#719e07">.</span>initBuilders<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//框架服务存储仓库对象,可以用于快速查询服务提供者信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FrameworkServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取ScopeModelInitializer类型(域模型初始化器)的扩展加载器ExtensionLoader,每个扩展类型都会创建一个扩展加载器缓存起来 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取ScopeModelInitializer类型的支持的扩展集合,这里当前版本存在8个扩展类型实现 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历这些扩展实现调用他们的initializeFrameworkModel方法来传递FrameworkModel类型对象,细节我们待会再详细说下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ScopeModelInitializer initializer <span style="color:#719e07">:</span> initializers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> initializer<span style="color:#719e07">.</span>initializeFrameworkModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个内部的ApplicationModel类型,细节下面说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建ApplicationConfig类型对象同时传递应用程序模型对象internalApplicationModel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取ConfigManager类型对象,然后设置添加当前应用配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">().</span>setApplication<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span>internalApplicationModel<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置公开的模块名字为常量DUBBO_INTERNAL_APPLICATION |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>继续上面代码的调用链路,我们来看 |
| FrameworkModel的super.initialize();方法 调用父类型ScopeModel的initialize()方法</p> |
| <h3 id="322-初始化scopemodel">3.2.2 初始化ScopeModel</h3> |
| <p>ScopeModel类型的初始化方法initialize():</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化ExtensionDirector是一个作用域扩展加载程序管理器。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//ExtensionDirector支持多个级别,子级可以继承父级的扩展实例。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//查找和创建扩展实例的方法类似于Java classloader。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ExtensionDirector<span style="color:#719e07">(</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent<span style="color:#719e07">.</span>getExtensionDirector<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> scope<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个参考了Spring的生命周期回调思想,添加一个扩展初始化的前后调用的处理器,在扩展初始化之前或之后调用的后处理器,参数类型为ExtensionPostProcessor |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>extensionDirector<span style="color:#719e07">.</span>addExtensionPostProcessor<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ScopeModelAwareExtensionProcessor<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个内部共享的域工厂对象,用于注册Bean,创建Bean,获取Bean,初始化Bean等 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>beanFactory <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ScopeBeanFactory<span style="color:#719e07">(</span>parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">()</span> <span style="color:#719e07">:</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">,</span> extensionDirector<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用数据结构链表,创建销毁监听器容器,一般用于关闭进程,重置应用程序对象等操作时候调用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>destroyListeners <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用ConcurrentHashMap属性集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>attributes <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用ConcurrentHashSet存储当前域下的类加载器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>classLoaders <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashSet<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Add Framework&#39;s ClassLoader by default |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//将当前类的加载器存入加载器集合classLoaders中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ClassLoader dubboClassLoader <span style="color:#719e07">=</span> ScopeModel<span style="color:#719e07">.</span>class<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>dubboClassLoader <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>addClassLoader<span style="color:#719e07">(</span>dubboClassLoader<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="323-初始类型定义构建器">3.2.3 初始类型定义构建器</h3> |
| <p>TypeDefinitionBuilder的初始化类型构造器方法initBuilders</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initBuilders</span><span style="color:#719e07">(</span>FrameworkModel model<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>TypeBuilder<span style="color:#719e07">&gt;</span> tbs <span style="color:#719e07">=</span> model<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>TypeBuilder<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> BUILDERS <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;(</span>tbs<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3231-服务存储仓库对象的创建">3.2.3.1 服务存储仓库对象的创建</h4> |
| <p>FrameworkServiceRepository对象的初始化</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">FrameworkServiceRepository</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>frameworkModel <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="324--域模型初始化器的获取与初始化回调">3.2.4 域模型初始化器的获取与初始化回调</h3> |
| <p>域模型初始化器的获取与初始化(ScopeModelInitializer类型和initializeFrameworkModel方法) |
| 加载到的ScopeModelInitializer类型的SPI扩展实现</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取ScopeModelInitializer类型的支持的扩展集合,这里当前版本存在8个扩展类型实现 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历这些扩展实现调用他们的initializeFrameworkModel方法来传递FrameworkModel类型对象,细节我们待会再详细说下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ScopeModelInitializer initializer <span style="color:#719e07">:</span> initializers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> initializer<span style="color:#719e07">.</span>initializeFrameworkModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>通过Debug查到域模型初始化器的SPI扩展类型有如下8个:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/source-blog/3-initextent.png" alt="在这里插入图片描述"></p> |
| <p>这里我随机找两个说一下吧: |
| 容错域模型初始化器:ClusterScopeModelInitializer的initializeFrameworkModel方法:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ClusterScopeModelInitializer</span> <span style="color:#268bd2">implements</span> ScopeModelInitializer <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initializeFrameworkModel</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ScopeBeanFactory beanFactory <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> beanFactory<span style="color:#719e07">.</span>registerBean<span style="color:#719e07">(</span>RouterSnapshotSwitcher<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">CommonScopeModelInitializer</span> <span style="color:#268bd2">implements</span> ScopeModelInitializer <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initializeFrameworkModel</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ScopeBeanFactory beanFactory <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">.</span>getBeanFactory<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> beanFactory<span style="color:#719e07">.</span>registerBean<span style="color:#719e07">(</span>FrameworkExecutorRepository<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ConfigScopeModelInitializer</span> <span style="color:#268bd2">implements</span> ScopeModelInitializer <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initializeFrameworkModel</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> frameworkModel<span style="color:#719e07">.</span>addDestroyListener<span style="color:#719e07">(</span><span style="color:#719e07">new</span> FrameworkModelCleaner<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="325-将内部应用配置对象创建与添加至应用模型中">3.2.5 将内部应用配置对象创建与添加至应用模型中</h3> |
| <p>创建ApplicationConfig对象让后将其添加至应用模型中 |
| 内部应用程序模型,这里为应用配置管理器设置一个应用配置对象,将这个应用配置的模块名字配置名字设置为DUBBO_INTERNAL_APPLICATION,应用配置记录着我们常见的应用配置信息,如下面表格所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//获取ConfigManager类型对象,然后设置添加当前应用配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">().</span>setApplication<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span>internalApplicationModel<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置公开的模块名字为常量DUBBO_INTERNAL_APPLICATION |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalApplicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>来自官网目前版本的配置解释: |
| 官网当前的配置描述知道到了元数据类型,后面我再补充几个</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>name</td> |
| <td>application</td> |
| <td>string</td> |
| <td>必填</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样,此参数不是匹配条件,你当前项目叫什么名字就填什么,和提供者消费者角色无关,比如:kylin应用调用了morgan应用的服务,则kylin项目配成kylin,morgan项目配成morgan,可能kylin也提供其它服务给别人使用,但kylin项目永远配成kylin,这样注册中心将显示kylin依赖于morgan</td> |
| <td>1.0.16以上版本</td> |
| </tr> |
| <tr> |
| <td>version</td> |
| <td>application.version</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>当前应用的版本</td> |
| <td>2.2.0以上版本</td> |
| </tr> |
| <tr> |
| <td>owner</td> |
| <td>owner</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>应用负责人,用于服务治理,请填写负责人公司邮箱前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>organization</td> |
| <td>organization</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>组织名称(BU或部门),用于注册中心区分服务来源,此配置项建议不要使用autoconfig,直接写死在配置中,比如china,intl,itu,crm,asc,dw,aliexpress等</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>architecture</td> |
| <td>architecture</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>用于服务分层对应的架构。如,intl、china。不同的架构使用不同的分层。</td> |
| <td>2.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>environment</td> |
| <td>environment</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>应用环境,如:develop/test/product,不同环境使用不同的缺省值,以及作为只用于开发测试功能的限制条件</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>compiler</td> |
| <td>compiler</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>javassist</td> |
| <td>性能优化</td> |
| <td>Java字节码编译器,用于动态类的生成,可选:jdk或javassist</td> |
| <td>2.1.0以上版本</td> |
| </tr> |
| <tr> |
| <td>logger</td> |
| <td>logger</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>slf4j</td> |
| <td>性能优化</td> |
| <td>日志输出方式,可选:slf4j,jcl,log4j,log4j2,jdk</td> |
| <td>2.2.0以上版本</td> |
| </tr> |
| <tr> |
| <td>metadata-type</td> |
| <td>metadata-type</td> |
| <td>String</td> |
| <td>可选</td> |
| <td>local</td> |
| <td>服务治理</td> |
| <td>metadata 传递方式,是以 Provider 视角而言的,Consumer 侧配置无效,可选值有:remote - Provider 把 metadata 放到远端注册中心,Consumer 从注册中心获取local - Provider 把 metadata 放在本地,Consumer 从 Provider 处直接获取</td> |
| <td>2.7.6以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>当前在Dubbo3.0.7中还有一些的配置我下面列举下:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>register-consumer</td> |
| <td>register-consumer</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>是否注册使用者实例,默认为false。</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>register-mode</td> |
| <td>register-mode</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>all</td> |
| <td>服务治理</td> |
| <td>将interface/instance/all 地址注册到注册中心,默认为all。</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>enable-empty-protection</td> |
| <td>enable-empty-protection</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>在空地址通知上启用空保护,默认为true</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td>protocol</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>dubbo</td> |
| <td>服务治理</td> |
| <td>此应用程序的首选协议(名称)</td> |
| <td></td> |
| </tr> |
| </tbody> |
| </table> |
| <h2 id="33-创建applicationmodel对象">3.3 创建ApplicationModel对象</h2> |
| <p>ApplicationModel对象的初始化调用 |
| 在前面 3.2.4 FrameworkModel框架模型的初始化方法initialize() 章节中,我们看到了代码ApplicationModel对象的初始化调用如下代码,这里我们来详细说一下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> internalApplicationModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> internalApplicationModel<span style="color:#719e07">.</span>getApplicationConfigManager<span style="color:#719e07">().</span>setApplication<span style="color:#719e07">(</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span>internalApplicationModel<span style="color:#719e07">,</span> CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> internalApplicationModel<span style="color:#719e07">.</span>setModelName<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_INTERNAL_APPLICATION<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><h3 id="331-applicationmodel的构造器">3.3.1 ApplicationModel的构造器</h3> |
| <p>ApplicationModel(FrameworkModel frameworkModel, boolean isInternal) |
| 刚刚3.2.9那个地方我们看到了使用代码<strong>new ApplicationModel(this, true)</strong> 来创建对象这里我们详细看下代码细节:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ApplicationModel</span><span style="color:#719e07">(</span>FrameworkModel frameworkModel<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> isInternal<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用父类型ScopeModel传递参数,这个构造器的传递没与前面看到的FrameworkModel构造器的中的调用参数有些不同第一个参数我们为frameworkModel代表父域模型,第二个参数标记域为应用程序级别APPLICATION,第三个参数我们传递的为true代表为内部域 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>frameworkModel<span style="color:#719e07">,</span> ExtensionScope<span style="color:#719e07">.</span>APPLICATION<span style="color:#719e07">,</span> isInternal<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>frameworkModel<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;FrameworkModel can not be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用程序域成员变量记录frameworkModel对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>frameworkModel <span style="color:#719e07">=</span> frameworkModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//frameworkModel对象添加当前应用程序域对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> frameworkModel<span style="color:#719e07">.</span>addApplication<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getDesc<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is created&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化应用程序 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3311-将applicationmodel添加至frameworkmodel容器中">3.3.1.1 将ApplicationModel添加至FrameworkModel容器中</h4> |
| <p>FrameworkModel的添加应用程序方法addApplication:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#dc322f">void</span> <span style="color:#268bd2">addApplication</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// can not add new application if it&#39;s destroying |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//检查FrameworkModel对象是否已经被标记为销毁状态,如果已经被销毁了则抛出异常无需执行逻辑 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>instLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果还未添加过当前参数传递应用模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModels<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为当前应用模型生成内部id |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationModel<span style="color:#719e07">.</span>setInternalId<span style="color:#719e07">(</span>buildInternalId<span style="color:#719e07">(</span>getInternalId<span style="color:#719e07">(),</span> appIndex<span style="color:#719e07">.</span>getAndIncrement<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//添加到成员变量集合applicationModels中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModels<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果非内部的则也向公开应用模型集合pubApplicationModels中添加一下 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>applicationModel<span style="color:#719e07">.</span>isInternal<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>pubApplicationModels<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> resetDefaultAppModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>内部id生成算法buildInternalId方法代码如下: |
| 看代码胜过,文字解释</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> String <span style="color:#268bd2">buildInternalId</span><span style="color:#719e07">(</span>String parentInternalId<span style="color:#719e07">,</span> <span style="color:#dc322f">long</span> childIndex<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// FrameworkModel 1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// ApplicationModel 1.1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// ModuleModel 1.1.1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span>parentInternalId<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> parentInternalId <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.&#34;</span> <span style="color:#719e07">+</span> childIndex<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;&#34;</span> <span style="color:#719e07">+</span> childIndex<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>重置默认的应用模型对象</strong> |
| FrameworkModel 重置默认的应用模型对象 resetDefaultAppModel()方法 |
| 与默认框架模型设置方式类似取集合的第一个,这里应用模型需要使用公开的应用模型的第一个做为默认应用模型,代码如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">resetDefaultAppModel</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>instLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel<span style="color:#719e07">.</span>isDestroyed<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//取第一个公开的应用模型做为默认应用模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationModel oldDefaultAppModel <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>pubApplicationModels<span style="color:#719e07">.</span>size<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel <span style="color:#719e07">=</span> pubApplicationModels<span style="color:#719e07">.</span>get<span style="color:#719e07">(</span>0<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>defaultInstance <span style="color:#719e07">==</span> <span style="color:#719e07">this</span> <span style="color:#719e07">&amp;&amp;</span> oldDefaultAppModel <span style="color:#719e07">!=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Reset global default application from &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc<span style="color:#719e07">(</span>oldDefaultAppModel<span style="color:#719e07">)</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>defaultAppModel<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="332-初始化applicationmodel">3.3.2 初始化ApplicationModel</h3> |
| <p>ApplicationModel的初始化initialize()方法 |
| 在前面<strong>3.2.10 ApplicationModel的构造器ApplicationModel(FrameworkModel frameworkModel, boolean isInternal)</strong> 中的最后一行开始初始化应用模型我们还未详细说明下面可以来看下</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个是调用域模型来初始化基础信息如扩展访问器等,可以参考 3.2.5 ScopeModel类型的初始化方法initialize()章节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个内部的模块模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> internalModule <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个独立服务存储对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用程序初始化监听器ApplicationInitListener扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ApplicationInitListener<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationInitListener<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果存在应用程序初始化监听器扩展则执行这个初始化方法,在当前的版本还未看到有具体的扩展实现类型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> listenerNames <span style="color:#719e07">=</span> extensionLoader<span style="color:#719e07">.</span>getSupportedExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String listenerName <span style="color:#719e07">:</span> listenerNames<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> extensionLoader<span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>listenerName<span style="color:#719e07">).</span>init<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化扩展(这个是应用程序生命周期的方法调用,这里调用初始化方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initApplicationExts<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取域模型初始化器扩展对象列表,然后执行初始化方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ScopeModelInitializer initializer <span style="color:#719e07">:</span> initializers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> initializer<span style="color:#719e07">.</span>initializeApplicationModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="334-initapplicationexts--初始化应用程序扩展方法">3.3.4 initApplicationExts() 初始化应用程序扩展方法</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initApplicationExts</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个扩展实现一共有两个可以看下面那个图扩展类型为ConfigManager和Environment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ApplicationExt<span style="color:#719e07">&gt;</span> exts <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ApplicationExt<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ApplicationExt ext <span style="color:#719e07">:</span> exts<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ext<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/source-blog/3-extension.png" alt="在这里插入图片描述"></p> |
| <h4 id="3341-configmanager类型的initialize方法">3.3.4.1 ConfigManager类型的initialize方法</h4> |
| <p>先简单说下ConfigManager的作用,无锁配置管理器(通过ConcurrentHashMap),用于快速读取操作。写入操作锁带有配置类型的子配置映射,用于安全检查和添加新配置。 |
| 其实ConfigManager实现类中并没有这个初始化方法initialize,不过ConfigManager的父类型AbstractConfigManager中是有initialize方法的,如下所示:</p> |
| <p>AbstractConfigManager的初始化方法initialize</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//乐观锁判断是否初始化过 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从模块环境中获取组合配置,目前Environment中有6种重要的配置,我们后面详细说 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> CompositeConfiguration configuration <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">.</span>getModelEnvironment<span style="color:#719e07">().</span>getConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// dubbo.config.mode获取配置模式,配置模式对应枚举类型ConfigMode,目前有这么几个STRICT,OVERRIDE,OVERRIDE_ALL,OVERRIDE_IF_ABSENT,IGNORE,这个配置决定了属性覆盖的顺序,当有同一个配置key多次出现时候,以最新配置为准,还是以最老的那个配置为准,还是配置重复则抛出异常,默认值为严格模式STRICT重复则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String configModeStr <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> configuration<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span>configModeStr<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>configMode <span style="color:#719e07">=</span> ConfigMode<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>configModeStr<span style="color:#719e07">.</span>toUpperCase<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String msg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Illegal &#39;&#34;</span> <span style="color:#719e07">+</span> ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;&#39; config value [&#34;</span> <span style="color:#719e07">+</span> configModeStr <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;], available values &#34;</span> <span style="color:#719e07">+</span> Arrays<span style="color:#719e07">.</span>toString<span style="color:#719e07">(</span>ConfigMode<span style="color:#719e07">.</span>values<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>msg<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span>msg<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// dubbo.config.ignore-duplicated-interface |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//忽略重复的接口(服务/引用)配置。默认值为false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String ignoreDuplicatedInterfaceStr <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> configuration |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ignoreDuplicatedInterfaceStr <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>ignoreDuplicatedInterface <span style="color:#719e07">=</span> Boolean<span style="color:#719e07">.</span>parseBoolean<span style="color:#719e07">(</span>ignoreDuplicatedInterfaceStr<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// print 打印配置信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> map <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE<span style="color:#719e07">,</span> configMode<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>ignoreDuplicatedInterface<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Config settings: &#34;</span> <span style="color:#719e07">+</span> map<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3342-environment类型的initialize方法">3.3.4.2 Environment类型的initialize方法</h4> |
| <p>这是一个与环境配置有关系的类型,我们先来简单了解下它的初始化方法,后期再详细说明:</p> |
| <p>Environment类型的initialize方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//乐观锁判断是否进行过初始化 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//PropertiesConfiguration从系统属性和dubbo.properties中获取配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>propertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> PropertiesConfiguration<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//SystemConfiguration获取的是JVM参数 启动命令中-D指定的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>systemConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SystemConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//EnvironmentConfiguration是从环境变量中获取的配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>environmentConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> EnvironmentConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//外部的Global配置config-center global/default config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>externalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;ExternalConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//外部的应用配置如:config-center中的应用配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>appExternalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;AppExternalConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地应用配置 , 如Spring Environment/PropertySources/application.properties |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>appConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;AppConfig&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务迁移配置加载 dubbo2升级dubbo3的一些配置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> loadMigrationRule<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务迁移配置加载 JVM &gt; env &gt; 代码路径dubbo-migration.yaml |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadMigrationRule</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件路径配置的key dubbo.migration.file |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// JVM参数中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_MIGRATION_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//env环境变量中获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> System<span style="color:#719e07">.</span>getenv<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO_MIGRATION_KEY<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>path<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//类路径下获取文件dubbo-migration.yaml |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> path <span style="color:#719e07">=</span> CommonConstants<span style="color:#719e07">.</span>DEFAULT_DUBBO_MIGRATION_FILE<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>localMigrationRule <span style="color:#719e07">=</span> ConfigUtils<span style="color:#719e07">.</span>loadMigrationRule<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">.</span>getClassLoaders<span style="color:#719e07">(),</span> path<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>ConfigUtils中读取迁移规则配置文件loadMigrationRule</strong> |
| 这个我们不细说了,贴一下代码感兴趣可以了解下,这个代码主要是读取文件到内存字符串:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> String <span style="color:#268bd2">loadMigrationRule</span><span style="color:#719e07">(</span>Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders<span style="color:#719e07">,</span> String fileName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String rawRule <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;&#34;</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>checkFileNameExist<span style="color:#719e07">(</span>fileName<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>FileInputStream input <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FileInputStream<span style="color:#719e07">(</span>fileName<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> readString<span style="color:#719e07">(</span>input<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoadersToLoad <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>ClassUtils<span style="color:#719e07">.</span>getClassLoader<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> classLoadersToLoad<span style="color:#719e07">.</span>addAll<span style="color:#719e07">(</span>classLoaders<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> urls <span style="color:#719e07">:</span> ClassLoaderResourceLoader<span style="color:#719e07">.</span>loadResources<span style="color:#719e07">(</span>fileName<span style="color:#719e07">,</span> classLoadersToLoad<span style="color:#719e07">).</span>values<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>URL url <span style="color:#719e07">:</span> urls<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> InputStream is <span style="color:#719e07">=</span> url<span style="color:#719e07">.</span>openStream<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>is <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> readString<span style="color:#719e07">(</span>is<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Throwable e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>warn<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Failed to load &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; file from &#34;</span> <span style="color:#719e07">+</span> fileName <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;(ignore this file): &#34;</span> <span style="color:#719e07">+</span> e<span style="color:#719e07">.</span>getMessage<span style="color:#719e07">(),</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> rawRule<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> String <span style="color:#268bd2">readString</span><span style="color:#719e07">(</span>InputStream is<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> StringBuilder stringBuilder <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> StringBuilder<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">char</span><span style="color:#719e07">[]</span> buffer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> <span style="color:#dc322f">char</span><span style="color:#719e07">[</span>10<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">(</span>BufferedReader reader <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> BufferedReader<span style="color:#719e07">(</span><span style="color:#719e07">new</span> InputStreamReader<span style="color:#719e07">(</span>is<span style="color:#719e07">)))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> n<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> <span style="color:#719e07">((</span>n <span style="color:#719e07">=</span> reader<span style="color:#719e07">.</span>read<span style="color:#719e07">(</span>buffer<span style="color:#719e07">))</span> <span style="color:#719e07">!=</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>n <span style="color:#719e07">&lt;</span> 10<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> buffer <span style="color:#719e07">=</span> Arrays<span style="color:#719e07">.</span>copyOf<span style="color:#719e07">(</span>buffer<span style="color:#719e07">,</span> n<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> stringBuilder<span style="color:#719e07">.</span>append<span style="color:#719e07">(</span>String<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>buffer<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> buffer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> <span style="color:#dc322f">char</span><span style="color:#719e07">[</span>10<span style="color:#719e07">];</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>IOException e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Read migration file error.&#34;</span><span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> stringBuilder<span style="color:#719e07">.</span>toString<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * check if the fileName can be found in filesystem |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param fileName |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">checkFileNameExist</span><span style="color:#719e07">(</span>String fileName<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> File file <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File<span style="color:#719e07">(</span>fileName<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> file<span style="color:#719e07">.</span>exists<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p></p> |
| <h2 id="34-创建modulemodel对象">3.4 创建ModuleModel对象</h2> |
| <p>前面ApplicationModel对象初始化的时候创建了ModuleModel如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>internalModule <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这里我们来看下这个它所对应的构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ModuleModel</span><span style="color:#719e07">(</span>ApplicationModel applicationModel<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> isInternal<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用ScopeModel传递3个参数父模型,模型域为模块域,是否为内部模型参数为true |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> ExtensionScope<span style="color:#719e07">.</span>MODULE<span style="color:#719e07">,</span> isInternal<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;ApplicationModel can not be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化成员变量applicationModel |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>applicationModel <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将模块模型添加至应用模型中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationModel<span style="color:#719e07">.</span>addModule<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> isInternal<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>LOGGER<span style="color:#719e07">.</span>isInfoEnabled<span style="color:#719e07">())</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> LOGGER<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span>getDesc<span style="color:#719e07">()</span> <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is created&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>serviceRepository<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;ModuleServiceRepository can not be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>notNull<span style="color:#719e07">(</span>moduleConfigManager<span style="color:#719e07">,</span> <span style="color:#2aa198">&#34;ModuleConfigManager can not be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Assert<span style="color:#719e07">.</span>assertTrue<span style="color:#719e07">(</span>moduleConfigManager<span style="color:#719e07">.</span>isInitialized<span style="color:#719e07">(),</span> <span style="color:#2aa198">&#34;ModuleConfigManager can not be initialized&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// notify application check state |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取应用程序发布对象,通知检查状态 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationDeployer applicationDeployer <span style="color:#719e07">=</span> applicationModel<span style="color:#719e07">.</span>getDeployer<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>applicationDeployer <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer<span style="color:#719e07">.</span>notifyModuleChanged<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> DeployState<span style="color:#719e07">.</span>PENDING<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="341-将模块模型添加至应用模型中">3.4.1 将模块模型添加至应用模型中</h3> |
| <p>如上面代码所示调用如下代码将模块模型添加到应用模型中:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> applicationModel<span style="color:#719e07">.</span>addModule<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">,</span> isInternal<span style="color:#719e07">);</span> |
| </span></span></code></pre></div><p>这里我们来看下添加过程</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#dc322f">void</span> <span style="color:#268bd2">addModule</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">,</span> <span style="color:#dc322f">boolean</span> isInternal<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加锁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">synchronized</span> <span style="color:#719e07">(</span>moduleLock<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不存在则添加 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleModels<span style="color:#719e07">.</span>contains<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查应用模型是否已销毁 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkDestroyed<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//添加至模块模型成员变量中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleModels<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置模块模型内部id,这个内部id生成过程与上面将应用模型添加到框架模型中的方式是一致的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//可以参考 3.3.2 将ApplicationModel添加至FrameworkModel容器中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> moduleModel<span style="color:#719e07">.</span>setInternalId<span style="color:#719e07">(</span>buildInternalId<span style="color:#719e07">(</span>getInternalId<span style="color:#719e07">(),</span> moduleIndex<span style="color:#719e07">.</span>getAndIncrement<span style="color:#719e07">()));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果不是内部模型则添加到公开模块模型中 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>isInternal<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> pubModuleModels<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="342-初始化模块模型">3.4.2 初始化模块模型</h3> |
| <p>前面ModuleModel构造器中通过initialize()方法来进行初始化操作如下代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用域模型ScopeModel的初始化,可以参考 3.2.5 ScopeModel类型的初始化方法initialize()章节 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建模块服务存储库对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建模块配置管理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleConfigManager<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块配置管理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块配置扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> initModuleExt<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化域模型扩展 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializerExtensionLoader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ScopeModelInitializer<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>ScopeModelInitializer<span style="color:#719e07">&gt;</span> initializers <span style="color:#719e07">=</span> initializerExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ScopeModelInitializer initializer <span style="color:#719e07">:</span> initializers<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> initializer<span style="color:#719e07">.</span>initializeModuleModel<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3421-模块服务存储库的创建">3.4.2.1 模块服务存储库的创建</h4> |
| <p>ModuleServiceRepository是模块模型中用来存储服务的通过如下代码调用</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//创建模块服务存储库对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleServiceRepository<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>这里我们来看下模块服务存储库的构造器代码:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ModuleServiceRepository</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块模型 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleModel <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> frameworkServiceRepository <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getFrameworkModel<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">).</span>getServiceRepository<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ModuleServiceRepository存储库中使用框架存储库frameworkServiceRepository来间接存储 |
| 这里我们看下怎么通过模块模型获取框架服务存储库frameworkServiceRepository:通过代码</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ScopeModelUtil<span style="color:#719e07">.</span>getFrameworkModel<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">).</span>getServiceRepository<span style="color:#719e07">()</span> |
| </span></span></code></pre></div><p>ScopeModelUtil工具类获取getFrameworkModel代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> FrameworkModel <span style="color:#268bd2">getFrameworkModel</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> FrameworkModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过成员变量获取(构造器初始化的时候将FrameworkModel赋值给了ApplicationModel的成员变量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">instanceof</span> ApplicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//直接获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">((</span>ApplicationModel<span style="color:#719e07">)</span> scopeModel<span style="color:#719e07">).</span>getFrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">instanceof</span> ModuleModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ModuleModel moduleModel <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>ModuleModel<span style="color:#719e07">)</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//间接通过ApplicationModel获取,不越级获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getFrameworkModel<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">instanceof</span> FrameworkModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">(</span>FrameworkModel<span style="color:#719e07">)</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Unable to get FrameworkModel from &#34;</span> <span style="color:#719e07">+</span> scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3422-模块配置管理器对象的创建与初始化">3.4.2.2 模块配置管理器对象的创建与初始化</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//创建模块配置管理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleConfigManager<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块配置管理对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleConfigManager<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><p>ModuleConfigManager的构造器代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ModuleConfigManager</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//向抽象的配置管理器AbstractConfigManager传递参数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//模块模型参数,模块支持的配置类型集合 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">,</span> Arrays<span style="color:#719e07">.</span>asList<span style="color:#719e07">(</span>ModuleConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> ServiceConfigBase<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> ReferenceConfigBase<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> ProviderConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">,</span> ConsumerConfig<span style="color:#719e07">.</span>class<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用程序配置管理器 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> applicationConfigManager <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">().</span>getApplicationConfigManager<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span></code></pre></div><p>ModuleConfigManager类型的初始化方法代码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!</span>initialized<span style="color:#719e07">.</span>compareAndSet<span style="color:#719e07">(</span><span style="color:#cb4b16">false</span><span style="color:#719e07">,</span> <span style="color:#cb4b16">true</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取组合配置对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> CompositeConfiguration configuration <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">.</span>getModelEnvironment<span style="color:#719e07">().</span>getConfiguration<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// dubbo.config.mode |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//3.3.4.1提到过这里再重复一次 dubbo.config.mode获取配置模式,配置模式对应枚举类型ConfigMode,目前有这么几个STRICT,OVERRIDE,OVERRIDE_ALL,OVERRIDE_IF_ABSENT,IGNORE,这个配置决定了属性覆盖的顺序,当有同一个配置key多次出现时候,以最新配置为准,还是以最老的那个配置为准,还是配置重复则抛出异常,默认值为严格模式STRICT重复则抛出异常 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String configModeStr <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> configuration<span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>StringUtils<span style="color:#719e07">.</span>hasText<span style="color:#719e07">(</span>configModeStr<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>configMode <span style="color:#719e07">=</span> ConfigMode<span style="color:#719e07">.</span>valueOf<span style="color:#719e07">(</span>configModeStr<span style="color:#719e07">.</span>toUpperCase<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">catch</span> <span style="color:#719e07">(</span>Exception e<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> String msg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Illegal &#39;&#34;</span> <span style="color:#719e07">+</span> ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;&#39; config value [&#34;</span> <span style="color:#719e07">+</span> configModeStr <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;], available values &#34;</span> <span style="color:#719e07">+</span> Arrays<span style="color:#719e07">.</span>toString<span style="color:#719e07">(</span>ConfigMode<span style="color:#719e07">.</span>values<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>error<span style="color:#719e07">(</span>msg<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span>msg<span style="color:#719e07">,</span> e<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// dubbo.config.ignore-duplicated-interface |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//忽略重复的接口(服务/引用)配置。默认值为false |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> String ignoreDuplicatedInterfaceStr <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>String<span style="color:#719e07">)</span> configuration |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>getProperty<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>ignoreDuplicatedInterfaceStr <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>ignoreDuplicatedInterface <span style="color:#719e07">=</span> Boolean<span style="color:#719e07">.</span>parseBoolean<span style="color:#719e07">(</span>ignoreDuplicatedInterfaceStr<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// print 打印配置信息到控制台 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> Object<span style="color:#719e07">&gt;</span> map <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LinkedHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_MODE<span style="color:#719e07">,</span> configMode<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> map<span style="color:#719e07">.</span>put<span style="color:#719e07">(</span>ConfigKeys<span style="color:#719e07">.</span>DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>ignoreDuplicatedInterface<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Config settings: &#34;</span> <span style="color:#719e07">+</span> map<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="3423-模块配置扩展的初始化">3.4.2.3 模块配置扩展的初始化</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>initModuleExt<span style="color:#719e07">();</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initModuleExt</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//目前这里的扩展只支持有一个类型ModuleEnvironment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> Set<span style="color:#719e07">&lt;</span>ModuleExt<span style="color:#719e07">&gt;</span> exts <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ModuleExt<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getSupportedExtensionInstances<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>ModuleExt ext <span style="color:#719e07">:</span> exts<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ext<span style="color:#719e07">.</span>initialize<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>ModuleEnvironment的初始化</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initialize</span><span style="color:#719e07">()</span> <span style="color:#268bd2">throws</span> IllegalStateException <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>orderedPropertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> OrderedPropertiesConfiguration<span style="color:#719e07">(</span>moduleModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>OrderedPropertiesConfiguration对象的创建</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">OrderedPropertiesConfiguration</span><span style="color:#719e07">(</span>ModuleModel moduleModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>moduleModel <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> refresh<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refresh</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//有序的配置提供器扩展获取 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ExtensionLoader<span style="color:#719e07">&lt;</span>OrderedPropertiesProvider<span style="color:#719e07">&gt;</span> propertiesProviderExtensionLoader <span style="color:#719e07">=</span> moduleModel<span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>OrderedPropertiesProvider<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> propertiesProviderNames <span style="color:#719e07">=</span> propertiesProviderExtensionLoader<span style="color:#719e07">.</span>getSupportedExtensions<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isEmpty<span style="color:#719e07">(</span>propertiesProviderNames<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>OrderedPropertiesProvider<span style="color:#719e07">&gt;</span> orderedPropertiesProviders <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>String propertiesProviderName <span style="color:#719e07">:</span> propertiesProviderNames<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> orderedPropertiesProviders<span style="color:#719e07">.</span>add<span style="color:#719e07">(</span>propertiesProviderExtensionLoader<span style="color:#719e07">.</span>getExtension<span style="color:#719e07">(</span>propertiesProviderName<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//order the propertiesProvider according the priority descending |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//根据优先级进行排序,值越小优先级越高 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> orderedPropertiesProviders<span style="color:#719e07">.</span>sort<span style="color:#719e07">((</span>a<span style="color:#719e07">,</span> b<span style="color:#719e07">)</span> <span style="color:#719e07">-&gt;</span> b<span style="color:#719e07">.</span>priority<span style="color:#719e07">()</span> <span style="color:#719e07">-</span> a<span style="color:#719e07">.</span>priority<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//override the properties. 目前没看到有具体的扩展实现 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> <span style="color:#719e07">(</span>OrderedPropertiesProvider orderedPropertiesProvider <span style="color:#719e07">:</span> orderedPropertiesProviders<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> properties<span style="color:#719e07">.</span>putAll<span style="color:#719e07">(</span>orderedPropertiesProvider<span style="color:#719e07">.</span>initProperties<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/3-kuang-jia-ying-yong-cheng-xu-mo-kuai-ling-yu-mo-xing-model-dui-xiang-de-chu-shi-hua/">&laquo;框架,应用程序,模块领域模型Model对象的初始化&raquo;</a></p></description></item><item><title>Blog: 02-启动服务前服务配置ServiceConfig类型是如何初始化的?</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/02/02-%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1%E5%89%8D%E6%9C%8D%E5%8A%A1%E9%85%8D%E7%BD%AEserviceconfig%E7%B1%BB%E5%9E%8B%E6%98%AF%E5%A6%82%E4%BD%95%E5%88%9D%E5%A7%8B%E5%8C%96%E7%9A%84/</link><pubDate>Tue, 02 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/02/02-%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1%E5%89%8D%E6%9C%8D%E5%8A%A1%E9%85%8D%E7%BD%AEserviceconfig%E7%B1%BB%E5%9E%8B%E6%98%AF%E5%A6%82%E4%BD%95%E5%88%9D%E5%A7%8B%E5%8C%96%E7%9A%84/</guid><description> |
| <h1 id="2-启动服务前服务配置serviceconfig类型是如何初始化的">2-启动服务前服务配置ServiceConfig类型是如何初始化的?</h1> |
| <h2 id="21-示例源码回顾">2.1 示例源码回顾:</h2> |
| <p>为了方便我们理解记忆,这里先来回顾下上一章我们说的示例代码,如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">Application</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DemoServiceImpl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>上面这几行代码虽然看似简单,仅仅几行的启动,但是完全掌握也得下一翻大功夫,接下来我们重点看启动代码中的第一行,创建一个服务配置对象:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span></code></pre></div><h2 id="22-了解一下服务配置的建模">2.2 了解一下服务配置的建模</h2> |
| <p>下面是一个简单的UML继承关系图,当然这个图很是简单的,这里仅仅列出了当前服务提供者的相关服务配置继承关系, 服务提供者独有的配置标注颜色为蓝色,一些可能与服务引用配置所共有的父类型我们用红色背景,当然这里为了简便起见不会提起服务引用相关的配置类型,这里列举了如下服务提供者类型,他们各司其职: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/2-ServiceConfig.png" alt="在这里插入图片描述"></p> |
| <center>图2.1 服务引用类继承关系UML</center> |
| <ul> |
| <li>AbstractConfig |
| <ul> |
| <li><strong>抽象的配置类型</strong>,也是最顶层的服务配置类型,封装着解析配置的实用方法和公共方法,比如服务id的设置,服务标签名字的处理,服务参数的添加,属性的提取等等</li> |
| </ul> |
| </li> |
| <li>AbstractMethodConfig |
| <ul> |
| <li><strong>抽象的方法配置</strong>,同样这个类型也是见名知意,服务方法的相关配置处理,这个类型主要用于对服务方法的一些配置信息建模比如服务方法的调用超时时间,重试次数,最大并发调用数,负载均衡策略,是否异步调用,是否确认异步发送等等配置信息.</li> |
| </ul> |
| </li> |
| <li>AbstractInterfaceConfig |
| <ul> |
| <li><strong>抽象的接口配置</strong>,与前面介绍的方法配置类似,这个类型是对服务接口的建模,主要的配置信息有暴漏服务的接口名字,服务接口的版本号,客户/提供方将引用的远程服务分组,<strong>服务元数据</strong>,服务接口的本地impl类名,服务监控配置,对于生成动态代理的策略,可以选择两种策略:jdk和javassist,容错类型等等配置</li> |
| </ul> |
| </li> |
| <li>AbstractServiceConfig |
| - <strong>抽象的服务配置</strong>,这个就与我们的服务提供者有了具体的关系了,主要记录了一些服务提供者的公共配置,如服务版本,服务分组,服务延迟注册毫秒数,是否暴漏服务,服务权重,是否为动态服务,服务协议类型,是否注册等等.</li> |
| <li>ServiceConfigBase |
| - <strong>服务的基础配置类</strong>,这个类型仍旧是个抽象的类型提取了一些基础的配置:导出服务的接口类,服务名称,接口实现的引用类型,提供者配置,是否是通用服务GenericService</li> |
| <li>ServiceConfig |
| - <strong>服务配置实现类</strong>, 上面的类型都是抽象类型不能做为具体存在的事物,这个类型是我们出现的第一个服务配置实现类型,服务配置实现类已经从父类型中继承了这么多的属性,这里主要为实现服务提供了一些配置如服务的协议配置,服务的代理工厂JavassistProxyFactory是将生成导出服务代理的ProxyFactory实现,是其默认实现,服务提供者模型,是否导出服务,导出的服务列表,服务监听器等等.</li> |
| <li>ServiceBean |
| - <strong>服务工厂Bean</strong> ,这个主要是Spring模块来简化配置的一个服务工厂Bean这里就先不详细介绍Spring相关的配置. -</li> |
| </ul> |
| <h2 id="23-serviceconfig构造器的初始化调用链">2.3 ServiceConfig构造器的初始化调用链</h2> |
| <p>有了上面的类型继承关系我们就比较好分析了,接下来我们开始创建服务配置对象如下代码所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span></code></pre></div><p>根据Java基础的构造器知识,在每个构造器的第一行都会有个super方法来调用父类的构造器,当前这个super方法我们可以不写但是Java编译器底层还是会为我们默认加上这么一行super()代码来调用父类构造器的.</p> |
| <p>对于上面我提到的这几个构造器<strong>根据代码被调用的先后顺序</strong>,这里重点说几个重要的,这里我仍旧按代码执行的先后顺序来说:</p> |
| <h3 id="231-父类型abstractmethodconfig构造器的初始化">2.3.1 父类型AbstractMethodConfig构造器的初始化</h3> |
| <p>根据super调用链这里先来看AbstractMethodConfig抽象方法配置</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractMethodConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">(</span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">().</span>getDefaultModule<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>在这个构造器中只有个super方法用来调用父类型的构造器,但是在调用之前会先使用代码 <strong>ApplicationModel.defaultModel().getDefaultModule()</strong> 创建一个模块模型对象<strong>ModuleModel</strong> |
| 关于模型对象的细节我们会在下个章节来说,这里我们继续来看调用链</p> |
| <h3 id="232-最顶层类型abstractconfig构造器的初始化">2.3.2 最顶层类型AbstractConfig构造器的初始化</h3> |
| <p><strong>AbstractConfig</strong>的构造器初始化一共有两个,第一个步骤就是创建一个应用程序模型对象<strong>ApplicationModel</strong>,刚刚我们在<strong>AbstractMethodConfig</strong>的构造器中了解到使用这个代码<strong>ApplicationModel.defaultModel().getDefaultModule()<strong>创建了个模块模型对象</strong>ModuleModel</strong>,具体他们细节我们下一章细说,了解了子类型<strong>AbstractMethodConfig</strong>的构造器是带参数的那我们就直接看第二个构造器</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractConfig</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">(</span>ApplicationModel<span style="color:#719e07">.</span>defaultModel<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>将会调用第二个构造器初始化域模型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AbstractConfig</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>当前类型设置ScopeModel类型对象</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setScopeModel</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次初始化的当前成员变量是空的可以设置变量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel <span style="color:#719e07">!=</span> scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查参数是否合法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> checkScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ScopeModel oldScopeModel <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel <span style="color:#719e07">=</span> scopeModel<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// reinitialize spi extension and change referenced config&#39;s scope model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//被子类重写的方法,根据多态会调用具体子类型的这个方法我们下面来看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//子类应该重写此方法以初始化其SPI扩展并更改引用的配置的范围模型。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>postProcessAfterScopeModelChanged<span style="color:#719e07">(</span>oldScopeModel<span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>检查ScopeModel参数是否合法,合法的参数是不能为空并且必须是ApplicationModel类型或者子类型</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">checkScopeModel</span><span style="color:#719e07">(</span>ScopeModel scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>scopeModel <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;scopeModel cannot be null&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(!(</span>scopeModel <span style="color:#719e07">instanceof</span> ApplicationModel<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Invalid scope model, expect to be a ApplicationModel but got: &#34;</span> <span style="color:#719e07">+</span> scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h4 id="2321-重写的postprocessafterscopemodelchanged调用逻辑">2.3.2.1 重写的postProcessAfterScopeModelChanged调用逻辑</h4> |
| <p>当ScopeModel模型对象发生了改变,上面调用了postProcessAfterScopeModelChanged方法来通知模型对象改变的时候要执行的操作,根据多态重写的逻辑我们从实现类的postProcessAfterScopeModelChanged来看,在下面的调用链路中部分父类型并未实现postProcessAfterScopeModelChanged方法我们就直接忽略了</p> |
| <p>第一个被调用到的是<strong>ServiceConfig</strong>类型的postProcessAfterScopeModelChanged方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">postProcessAfterScopeModelChanged</span><span style="color:#719e07">(</span>ScopeModel oldScopeModel<span style="color:#719e07">,</span> ScopeModel newScopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>postProcessAfterScopeModelChanged<span style="color:#719e07">(</span>oldScopeModel<span style="color:#719e07">,</span> newScopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化当前协议对象,通过扩展机制获取协议Protocol类型的对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> protocolSPI <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>Protocol<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化当前代理工厂对象,通过扩展机制获取ProxyFactory类型的对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> proxyFactory <span style="color:#719e07">=</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>getExtensionLoader<span style="color:#719e07">(</span>ProxyFactory<span style="color:#719e07">.</span>class<span style="color:#719e07">).</span>getAdaptiveExtension<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>第二个被调用到的方法为<strong>ServiceConfigBase</strong>的postProcessAfterScopeModelChanged方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">postProcessAfterScopeModelChanged</span><span style="color:#719e07">(</span>ScopeModel oldScopeModel<span style="color:#719e07">,</span> ScopeModel newScopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>postProcessAfterScopeModelChanged<span style="color:#719e07">(</span>oldScopeModel<span style="color:#719e07">,</span> newScopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当服务提供者配置对象不为空时候为服务提供者对象设置域模型,这里服务提供者对象仍旧为空,这个一般用在兼容Dubbo低版本 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>provider <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>provider<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> scopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>provider<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>第三个被调用到的是<strong>AbstractInterfaceConfig</strong>类型的postProcessAfterScopeModelChanged方法</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">postProcessAfterScopeModelChanged</span><span style="color:#719e07">(</span>ScopeModel oldScopeModel<span style="color:#719e07">,</span> ScopeModel newScopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span><span style="color:#719e07">.</span>postProcessAfterScopeModelChanged<span style="color:#719e07">(</span>oldScopeModel<span style="color:#719e07">,</span> newScopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// remove this config from old ConfigManager |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// if (oldScopeModel != null &amp;&amp; oldScopeModel instanceof ModuleModel) { |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// ((ModuleModel)oldScopeModel).getConfigManager().removeConfig(this); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// } |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// change referenced config&#39;s scope model |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//获取应用程序模型对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ApplicationModel applicationModel <span style="color:#719e07">=</span> ScopeModelUtil<span style="color:#719e07">.</span>getApplicationModel<span style="color:#719e07">(</span>scopeModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为配置中心对象设置ApplicationModel类型对象(当前阶段配置中心配置对象为空) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>configCenter <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>configCenter<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>configCenter<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为元数据配置对象设置ApplicationModel类型对象(当前阶段数据配置配置对象为空) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为MonitorConfig服务监控配置对象设置ApplicationModel类型对象(当前阶段数据配置配置对象为空) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>monitor <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>monitor<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>monitor<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个if判断和上面的上面是重复的估计是写代码人加班加的太久了,没注意看 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>metadataReportConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果注册中心配置列表不为空则为每个注册中心配置设置一个ApplicationModel类型对象(当前注册中心对象都为空) |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>CollectionUtils<span style="color:#719e07">.</span>isNotEmpty<span style="color:#719e07">(</span><span style="color:#719e07">this</span><span style="color:#719e07">.</span>registries<span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>registries<span style="color:#719e07">.</span>forEach<span style="color:#719e07">(</span>registryConfig <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>registryConfig<span style="color:#719e07">.</span>getScopeModel<span style="color:#719e07">()</span> <span style="color:#719e07">!=</span> applicationModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> registryConfig<span style="color:#719e07">.</span>setScopeModel<span style="color:#719e07">(</span>applicationModel<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">});</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>最后被调用到的是最顶层父类型<strong>AbstractConfig</strong>的postProcessAfterScopeModelChanged方法 |
| 这个方法什么也没干只是在父类型创建的模版方法让子类型来重写用的</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">postProcessAfterScopeModelChanged</span><span style="color:#719e07">(</span>ScopeModel oldScopeModel<span style="color:#719e07">,</span> ScopeModel newScopeModel<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// remove this config from old ConfigManager |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// if (oldScopeModel != null &amp;&amp; oldScopeModel instanceof ApplicationModel) { |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// ((ApplicationModel)oldScopeModel).getApplicationConfigManager().removeConfig(this); |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// } |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h3 id="233-serviceconfigbase构造器的初始化">2.3.3 ServiceConfigBase构造器的初始化</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ServiceConfigBase</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务元数据对象创建 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceMetadata<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为服务元数据对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> serviceMetadata<span style="color:#719e07">.</span>addAttribute<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;ORIGIN_CONFIG&#34;</span><span style="color:#719e07">,</span> <span style="color:#719e07">this</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p><strong>注意,</strong> <strong>ServiceMetadata</strong>这个类目前在Dubbo中没有用法。与服务级别相关的数据,例如名称、版本、业务服务的类加载器、安全信息等,还带有用于扩展的AttributeMap。</p> |
| <p><strong>服务配置对象的创建过程就这样结束了</strong>,当然有一些细节会<strong>放到后面来写</strong> |
| 上面主要顺序是按照代码执行的顺序来写的部分地方可能稍微做了调整,如果有条件的同学一定要<strong>自己进行DEBUG</strong>了解下细节.</p> |
| <p>关于服务配置官网提供了xml的配置信息这里我拷贝过来,可以做为参考: |
| 当然这个配置不是最新的比如服务配置的<strong>标签配置tag</strong>, |
| <strong>warmup 预热时间</strong>单位毫秒,暂时还没有说明</p> |
| <table> |
| <thead> |
| <tr> |
| <th>属性</th> |
| <th>对应URL参数</th> |
| <th>类型</th> |
| <th>是否必填</th> |
| <th>缺省值</th> |
| <th>作用</th> |
| <th>描述</th> |
| <th>兼容性</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>interface</td> |
| <td></td> |
| <td>class</td> |
| <td>必填</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务接口名</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>ref</td> |
| <td></td> |
| <td>object</td> |
| <td>必填</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务对象实现引用</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>version</td> |
| <td>version</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>0.0.0</td> |
| <td>服务发现</td> |
| <td>服务版本,建议使用两位数字版本,如:1.0,通常在接口不兼容时版本号才需要升级</td> |
| <td>1.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>group</td> |
| <td>group</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务发现</td> |
| <td>服务分组,当一个接口有多个实现,可以用分组区分</td> |
| <td>1.0.7以上版本</td> |
| </tr> |
| <tr> |
| <td>path</td> |
| <td><code>&lt;path&gt;</code></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省为接口名</td> |
| <td>服务发现</td> |
| <td>服务路径 (注意:1.0不支持自定义路径,总是使用接口名,如果有1.0调2.0,配置服务路径可能不兼容)</td> |
| <td>1.0.12以上版本</td> |
| </tr> |
| <tr> |
| <td>delay</td> |
| <td>delay</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>延迟注册服务时间(毫秒) ,设为-1时,表示延迟到Spring容器初始化完成时暴露服务</td> |
| <td>1.0.14以上版本</td> |
| </tr> |
| <tr> |
| <td>timeout</td> |
| <td>timeout</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>1000</td> |
| <td>性能调优</td> |
| <td>远程服务调用超时时间(毫秒)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>retries</td> |
| <td>retries</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>2</td> |
| <td>性能调优</td> |
| <td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>connections</td> |
| <td>connections</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>100</td> |
| <td>性能调优</td> |
| <td>对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>loadbalance</td> |
| <td>loadbalance</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>random</td> |
| <td>性能调优</td> |
| <td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>async</td> |
| <td>async</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>性能调优</td> |
| <td>是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>local</td> |
| <td>local</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>设为true,表示使用缺省代理类名,即:接口名 + Local后缀,已废弃,请使用stub</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>stub</td> |
| <td>stub</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>设为true,表示使用缺省代理类名,即:接口名 + Stub后缀,服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceStub(XxxService xxxService)</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>mock</td> |
| <td>mock</td> |
| <td>class/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>设为true,表示使用缺省Mock类名,即:接口名 + Mock后缀,服务接口调用失败Mock实现类,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>token</td> |
| <td>token</td> |
| <td>string/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>令牌验证,为空表示不开启,如果为true,表示随机生成动态令牌,否则使用静态令牌,令牌的作用是防止消费者绕过注册中心直接访问,保证注册中心的授权功能有效,如果使用点对点调用,需关闭令牌功能</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>registry</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省向所有registry注册</td> |
| <td>配置关联</td> |
| <td>向指定注册中心注册,在多个注册中心时使用,值为<a href="dubbo:registry">dubbo:registry</a>的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>provider</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td>缺省使用第一个provider配置</td> |
| <td>配置关联</td> |
| <td>指定provider,值为<a href="dubbo:provider">dubbo:provider</a>的id属性</td> |
| <td>2.0.0以上版本</td> |
| </tr> |
| <tr> |
| <td>deprecated</td> |
| <td>deprecated</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>服务是否过时,如果设为true,消费方引用时将打印服务过时警告error日志</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>dynamic</td> |
| <td>dynamic</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。 2.0.5以上版本</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>accesslog</td> |
| <td>accesslog</td> |
| <td>string/boolean</td> |
| <td>可选</td> |
| <td>false</td> |
| <td>服务治理</td> |
| <td>设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>owner</td> |
| <td>owner</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务负责人,用于服务治理,请填写负责人公司邮箱前缀</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>document</td> |
| <td>document</td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>服务治理</td> |
| <td>服务文档URL</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>weight</td> |
| <td>weight</td> |
| <td>int</td> |
| <td>可选</td> |
| <td></td> |
| <td>性能调优</td> |
| <td>服务权重</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>executes</td> |
| <td>executes</td> |
| <td>int</td> |
| <td>可选</td> |
| <td>0</td> |
| <td>性能调优</td> |
| <td>服务提供者每服务每方法最大可并行执行请求数</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>proxy</td> |
| <td>proxy</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>javassist</td> |
| <td>性能调优</td> |
| <td>生成动态代理方式,可选:jdk/javassist</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>cluster</td> |
| <td>cluster</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>failover</td> |
| <td>性能调优</td> |
| <td>集群方式,可选:failover/failfast/failsafe/failback/forking</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>filter</td> |
| <td>service.filter</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>default</td> |
| <td>性能调优</td> |
| <td>服务提供方远程调用过程拦截器名称,多个名称用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>listener</td> |
| <td>exporter.listener</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>default</td> |
| <td>性能调优</td> |
| <td>服务提供方导出服务监听器名称,多个名称用逗号分隔</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>protocol</td> |
| <td></td> |
| <td>string</td> |
| <td>可选</td> |
| <td></td> |
| <td>配置关联</td> |
| <td>使用指定的协议暴露服务,在多协议时使用,值为<a href="dubbo:protocol">dubbo:protocol</a>的id属性,多个协议ID用逗号分隔</td> |
| <td>2.0.5以上版本</td> |
| </tr> |
| <tr> |
| <td>layer</td> |
| <td>layer</td> |
| <td>string</td> |
| <td>可选</td> |
| <td>服务治理</td> |
| <td>服务提供者所在的分层。如:biz、dao、intl:web、china:acton。</td> |
| <td>2.0.7以上版本</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>register</td> |
| <td>register</td> |
| <td>boolean</td> |
| <td>可选</td> |
| <td>true</td> |
| <td>服务治理</td> |
| <td>该协议的服务是否注册到注册中心</td> |
| <td>2.0.8以上版本</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/2-qi-dong-fu-wu-qian-fu-wu-pei-zhi-serviceconfig-lei-xing-shi-ru-he-chu-shi-hua-de/">&laquo;ServiceConfig对象的建模&raquo;</a></p></description></item><item><title>Blog: 01 从一个服务提供者的Demo说起</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/01/01-%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</link><pubDate>Mon, 01 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/01/01-%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</guid><description> |
| <h1 id="1-从一个服务提供者的demo说起">1 从一个服务提供者的Demo说起</h1> |
| <p>为了更方便了解原理,我们先来编写一个Demo,从例子中来看源码实现:</p> |
| <h2 id="11-启动zookeeper">1.1 启动Zookeeper</h2> |
| <p>为了Demo可以正常启动,需要我们先在本地启动一个Zookeeper如下图所示: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/1-zookeeper.png" alt="在这里插入图片描述"></p> |
| <h2 id="12-服务提供者">1.2 服务提供者</h2> |
| <p>接下来给大家贴一下示例源码,这个源码来源于Dubbo源码目录的 dubbo-demo/dubbo-demo-api 目录下面的dubbo-demo-api-provider子项目,这里我做了删减,方便看核心代码: |
| 首先我们定义一个服务接口如下所示:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">import</span> java.util.concurrent.CompletableFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">DemoService</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 同步处理的服务方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 用于异步处理的服务方法 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param name |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture<span style="color:#719e07">.</span>completedFuture<span style="color:#719e07">(</span>sayHello<span style="color:#719e07">(</span>name<span style="color:#719e07">));</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>服务实现类如下: |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.rpc.RpcContext<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.Logger<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.LoggerFactory<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> java.util.concurrent.CompletableFuture<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceImpl</span> <span style="color:#268bd2">implements</span> DemoService <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> Logger logger <span style="color:#719e07">=</span> LoggerFactory<span style="color:#719e07">.</span>getLogger<span style="color:#719e07">(</span>DemoServiceImpl<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">sayHello</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> logger<span style="color:#719e07">.</span>info<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, request from consumer: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getServiceContext<span style="color:#719e07">().</span>getRemoteAddress<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#2aa198">&#34;Hello &#34;</span> <span style="color:#719e07">+</span> name <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, response from provider: &#34;</span> <span style="color:#719e07">+</span> RpcContext<span style="color:#719e07">.</span>getServiceContext<span style="color:#719e07">().</span>getLocalAddress<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span><span style="color:#719e07">(</span>String name<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="13-启用服务">1.3 启用服务</h2> |
| <p>有了服务接口之后我们来启用服务,启用服务的源码如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.common.constants.CommonConstants<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ApplicationConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.MetadataReportConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ProtocolConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.RegistryConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ServiceConfig<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.bootstrap.DubboBootstrap<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.demo.DemoService<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">Application</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">main</span><span style="color:#719e07">(</span>String<span style="color:#719e07">[]</span> args<span style="color:#719e07">)</span> <span style="color:#268bd2">throws</span> Exception <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> startWithBootstrap<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span><span style="color:#719e07">()</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> ServiceConfig<span style="color:#719e07">&lt;</span>DemoServiceImpl<span style="color:#719e07">&gt;</span> service <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfig<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setInterface<span style="color:#719e07">(</span>DemoService<span style="color:#719e07">.</span>class<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> service<span style="color:#719e07">.</span>setRef<span style="color:#719e07">(</span><span style="color:#719e07">new</span> DemoServiceImpl<span style="color:#719e07">());</span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap<span style="color:#719e07">.</span>getInstance<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> bootstrap<span style="color:#719e07">.</span>application<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ApplicationConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>registry<span style="color:#719e07">(</span><span style="color:#719e07">new</span> RegistryConfig<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>protocol<span style="color:#719e07">(</span><span style="color:#719e07">new</span> ProtocolConfig<span style="color:#719e07">(</span>CommonConstants<span style="color:#719e07">.</span>DUBBO<span style="color:#719e07">,</span> <span style="color:#719e07">-</span>1<span style="color:#719e07">))</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>service<span style="color:#719e07">(</span>service<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>start<span style="color:#719e07">()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">.</span>await<span style="color:#719e07">();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><h2 id="14-启用服务后写入zookeeper的节点数据">1.4 启用服务后写入Zookeeper的节点数据</h2> |
| <p>启动服务,这个时候我们打开Zookeeper图形化客户端来看看这个服务在Zookeeper上面写入来哪些数据,如下图: |
| <img src="https://dubbo.apache.org/imgs/blog/source-blog/1-zookeeper-data.png" alt="在这里插入图片描述"> |
| 写入Zookeper上的节点用于服务在分布式场景下的协调,这些节点是比较重要的。</p> |
| <p>如果了解过Dubbo的同学,应该会知道Dubbo在低版本的时候会向注册中心中写入服务接口,具体路径在上面的 <strong>dubbo目录下</strong> ,然后在 <strong>/dubbo/服务接口/</strong> 路径下写入如下信息:</p> |
| <ul> |
| <li><strong>服务提供者</strong>配置信息URL形式</li> |
| <li><strong>服务消费者</strong>的配置信息URL形式</li> |
| <li>服务<strong>路由信息</strong></li> |
| <li><strong>配置信息</strong></li> |
| </ul> |
| <p>上面这个图就是Dubbo3的注册信息了,后面我们也会围绕细节来说明下,这里可以看下新增了:</p> |
| <ul> |
| <li>/dubbo/metadata <strong>元数据信息</strong></li> |
| <li>/dubbo/mapping 服务和应用的<strong>映射信息</strong></li> |
| <li>/dubbo/config <strong>注册中心配置</strong></li> |
| <li>/services目录<strong>应用信息</strong></li> |
| </ul> |
| <p>在这里可以大致了解下,在后面会有更详细的源码解析这个示例代码.通过透析代码来看透Dubbo3服务注册原理,服务提供原理。</p> |
| <p>原文: <a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">&laquo;从一个服务提供者的Demo说起&raquo;</a></p></description></item><item><title>Blog: 浅析 Dubbo 3.0 中接口级地址推送性能的优化</title><link>https://dubbo.apache.org/zh-cn/blog/2022/06/23/%E6%B5%85%E6%9E%90-dubbo-3.0-%E4%B8%AD%E6%8E%A5%E5%8F%A3%E7%BA%A7%E5%9C%B0%E5%9D%80%E6%8E%A8%E9%80%81%E6%80%A7%E8%83%BD%E7%9A%84%E4%BC%98%E5%8C%96/</link><pubDate>Thu, 23 Jun 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/06/23/%E6%B5%85%E6%9E%90-dubbo-3.0-%E4%B8%AD%E6%8E%A5%E5%8F%A3%E7%BA%A7%E5%9C%B0%E5%9D%80%E6%8E%A8%E9%80%81%E6%80%A7%E8%83%BD%E7%9A%84%E4%BC%98%E5%8C%96/</guid><description> |
| <h2 id="url-简介">URL 简介</h2> |
| <p>在阐述地址推送性能的具体优化之前,我们有必要先了解一下与之息息相关的内容 &mdash; URL。</p> |
| <h3 id="定义">定义</h3> |
| <p>在不谈及 dubbo 时,我们大多数人对 URL 这个概念并不会感到陌生。统一资源定位器 (<a href="https://www.ietf.org/rfc/rfc1738.txt">RFC1738</a>――Uniform Resource Locators (URL))应该是最广为人知的一个 RFC 规范,它的定义也非常简单。</p> |
| <blockquote> |
| <p>因特网上的可用资源可以用简单字符串来表示,该文档就是描述了这种字符串的语法和语 义。而这些字符串则被称为:“统一资源定位器”(URL)</p> |
| </blockquote> |
| <p><strong>一个标准的 URL 格式</strong>至多可以包含如下的几个部分</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>protocol://username:password@host:port/path?key=value&amp;key=value |
| </span></span></code></pre></div><p><strong>一些典型 URL</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>http://www.facebook.com/friends?param1=value1&amp;amp;param2=value2 |
| </span></span><span style="display:flex;"><span>https://username:password@10.20.130.230:8080/list?version=1.0.0 |
| </span></span><span style="display:flex;"><span>ftp://username:password@192.168.1.7:21/1/read.txt |
| </span></span></code></pre></div><p>当然,也有一些<strong>不太符合常规的 URL</strong>,也被归类到了 URL 之中</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>192.168.1.3:20880 |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = 192.168.1.3, port = 20880, url path = null |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:///home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = null, url path = home/user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file://home/user1/router.js?type=script&lt;br&gt; |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = home, url path = user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:///D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = null, url path = D:/1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:/D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span>同上 file:///D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>/home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = null, url path = home/user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = home, url path = user1/router.js |
| </span></span></code></pre></div><h3 id="dubbo-中的-url">Dubbo 中的 URL</h3> |
| <p>在 dubbo 中,也使用了类似的 URL,主要用于在各个扩展点之间传递数据,组成此 URL 对象的具体参数如下:</p> |
| <ul> |
| <li> |
| <p>protocol:一般是 dubbo 中的各种协议 如:dubbo thrift http zk</p> |
| </li> |
| <li> |
| <p>username/password:用户名/密码</p> |
| </li> |
| <li> |
| <p>host/port:主机/端口</p> |
| </li> |
| <li> |
| <p>path:接口名称</p> |
| </li> |
| <li> |
| <p>parameters:参数键值对</p> |
| <p><strong>一些典型的 Dubbo URL</strong></p> |
| </li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000 |
| </span></span><span style="display:flex;"><span>描述一个 dubbo 协议的服务 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.RegistryService&amp;pid=1214&amp;qos.port=33333&amp;timestamp=1545721981946 |
| </span></span><span style="display:flex;"><span>描述一个 zookeeper 注册中心 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>consumer://30.5.120.217/org.apache.dubbo.demo.DemoService?application=demo-consumer&amp;category=consumers&amp;check=false&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=1209&amp;qos.port=33333&amp;side=consumer&amp;timestamp=1545721827784 |
| </span></span><span style="display:flex;"><span>描述一个消费者 |
| </span></span></code></pre></div><p>可以说,任意的一个领域中的一个实现都可以认为是一类 URL,dubbo 使用 URL 来统一描述了元数据,配置信息,贯穿在整个框架之中。</p> |
| <h2 id="dubbo-27">Dubbo 2.7</h2> |
| <h3 id="url-结构">URL 结构</h3> |
| <p>在 Dubbo 2.7 中,URL 的结构非常简单,一个类就涵盖了所有内容,如下图所示。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-1.png" alt="Dubbo2 URL类图.png"></p> |
| <h3 id="地址推送模型">地址推送模型</h3> |
| <p>接下来我们再来看看 Dubbo 2.7 中的地址推送模型方案,主要性能问题由下列过程引起。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-2.png" alt="Dubbo2 地址推送模型.png"></p> |
| <p>上图中主要的流程为 |
| 1、用户新增/删除DemoService的某个具体Provider实例(常见于扩容缩容、网络波动等原因) |
| 2、ZooKeeper将DemoService下所有实例推送给Consumer端 |
| 3、Consumer端根据Zookeeper推送的数据重新全量生成URL |
| 根据该方案可以看出在Provider实例数量较小时,Consumer端的影响比较小,但当某个接口有大量Provider实例时,便会有大量不必要的URL创建过程。 |
| 而Dubbo 3.0中则主要针对上述推送流程进行了一系列的优化,接下来我们便对其进行具体的讲解。</p> |
| <h2 id="dubbo-30">Dubbo 3.0</h2> |
| <h3 id="url-结构-1">URL 结构</h3> |
| <p>当然,地址推送模型的优化依然离不开 URL 的优化,下图是Dubbo 3.0中优化地址推送模型的过程中使用的新的URL结构。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-3.png" alt="Dubbo3 URL类图.png"></p> |
| <p>根据上图我们可以看出,在 Dubbo 2.7 的 URL 中的几个重要属性在 Dubbo 3.0 中已经不存在了,取而代之的是 URLAddress 和 URLParam 两个类。原来的 parameters 属性被移动到了 URLParam 中的 params,其他的属性则移动到了 URLAddress 及其子类中。 |
| 再来介绍 URL 新增的 3 个子类,其中 InstanceAddressURL 属于应用级接口地址,本篇章中不做介绍。 |
| 而 ServiceConfigURL 及 ServiceAddressURL 主要的差别就是,ServiceConfigURL 是程序读取配置文件时生成的 URL。而 ServiceAddressURL 则是注册中心推送一些信息(如 providers)过来时生成的 URL。 |
| 在这里我们顺便提一下为什么会有 DubboServiceAddressURL 这个子类,按照目前的结构来看,ServiceAddressURL 只有这一个子类,所以完全可以将他们两个的属性全都放到 ServiceAddressURL 中,那么为什么还要有这个子类呢?其实是 Dubbo 3.0 为了兼容 HSF 框架所设计的,抽象出了一个 ServiceAddressURL,而 HSF 框架则可以继承这个类,使用 HSFServiceAddressURL,当然,这个类目前没有体现出来,所以此处我们简单一提,不过多讲解。 |
| 那么,我们接下来就讨论一下 Dubbo 3.0 为什么要改为此种数据结构,并且该结构和地址推送模型的优化有何关联性吧!</p> |
| <h3 id="地址推送模型的优化">地址推送模型的优化</h3> |
| <h4 id="url-结构上的优化">URL 结构上的优化</h4> |
| <p>我们在上小节中的类图里看到虽然原来的属性都被移到了 URLAddress 和 URLParam 里,但是 URL 的子类依然多了几个属性,这几个属性自然也是为了优化而新增的,那么这里就讲讲这几个属性的作用。 |
| <strong>ServiceConfigURL</strong>:这个子类中新增了 attribute 这个属性,这个属性主要是针对 URLParam 的 params 做了冗余,仅仅只是将 value 的类型从 String 改为了 Object,减少了代码中每次获取 parameters 的格式转换消耗。 |
| <strong>ServiceAddressURL</strong>:这个子类及其对应的其他子类中则新增了 overrideURL 和 consumerURL 属性。其中 consumerURL 是针对 consumer 端的配置信息,overrideURL 则是在 Dubbo Admin 上进行动态配置时写入的值,当我们调用 URL 的 getParameter() 方法时,优先级为 <code>overrideURL &gt; consumerURL &gt; urlParam</code>。在 Dubbo 2.7 时,动态配置属性会替换 URL 中的属性,及当你有大量 URL 时消耗也是不可忽视的,而此处的 overrideURL 则避免了这种消耗,因为所有 URL 都会共同使用同一个对象。</p> |
| <h4 id="多级缓存">多级缓存</h4> |
| <p>缓存是 Dubbo 3.0 在 URL 上做的优化的重点,同时这部分也是直接针对地址推送模型所做的优化,那么接下来我们就开始来介绍一下多级缓存的具体实现。 |
| 首先,多级缓存主要体现在 CacheableFailbackRegistry 这个类之中,它直接继承于 FailbackRegistry,以 Zookeeper 为例,我们看看 Dubbo 2.7 和 Dubbo 3.0 继承结构的区别。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-4.png" alt="Dubbo3 CacheableFailbackRegistry缓存.png"></p> |
| <p>可以看到在 CacheableFailbackRegistry 缓存中,我们新增了 3 个缓存属性 <code>stringAddress</code>,<code>stringParam</code> 和 <code>stringUrls</code>。我们通过下图来描述这 3 个缓存的具体使用场景。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-5.png" alt="多级缓存.png"></p> |
| <p>在该方案下,我们使用了 3 个纬度的缓存数据(URL 字符串缓存、URL 地址缓存、URL 参数缓存),这样一来,在大部分情况下都能有效利用到缓存中的数据,减少了 Zookeeper 重复通知的消耗。</p> |
| <h4 id="延迟通知">延迟通知</h4> |
| <p>除了上面提到的优化之外,其实另外还有两个小小的优化。 |
| 第一个是解析 URL 时可以直接使用编码后的 URL 字符串字节进行解析,而在 Dubbo 2.7 中,所有编码后的 URL 字符串都需要经过解码才可以正常解析为 URL 对象。该方式也直接减少了 URL 解码过程的开销。 |
| 第二个则是 URL 变更后的通知机制增加了延迟,下图以Zookeeper为例讲解了实现细节。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-6.png" alt="延迟通知.png"></p> |
| <p>在该方案中,当 Consumer 接收 Zookeeper 的变更通知后会主动休眠一段时间,而这段时间内的变更在休眠结束后只会保留最后一次变更,Consumer 便会使用最后一次变更来进行监听实例的更新,以此方法来减少大量 URL 的创建开销。</p> |
| <h4 id="字符串重用">字符串重用</h4> |
| <p>在旧版本实现中,不同的 URL 中属性相同的字符串会存储在堆内不同的地址中,如 protocol、path 等,当有大量 provider 的情况下,Consumer 端的堆内会存在大量的重复字符串,导致内存利用率低下,所以此处提供了另一个优化方式,即字符串重用。 |
| 而它的实现方式也非常的简单,让我们来看看对应的代码片段。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">URLItemCache</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> PATH_CACHE <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> LRUCache<span style="color:#719e07">&lt;&gt;(</span>10000<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> PROTOCOL_CACHE <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;();</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 省略无关代码片段 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> String <span style="color:#268bd2">checkProtocol</span><span style="color:#719e07">(</span>String _protocol<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>_protocol <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> _protocol<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> String cachedProtocol <span style="color:#719e07">=</span> PROTOCOL_CACHE<span style="color:#719e07">.</span>putIfAbsent<span style="color:#719e07">(</span>_protocol<span style="color:#719e07">,</span> _protocol<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedProtocol <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cachedProtocol<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> _protocol<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> String <span style="color:#268bd2">checkPath</span><span style="color:#719e07">(</span>String _path<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>_path <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> _path<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> String cachedPath <span style="color:#719e07">=</span> PATH_CACHE<span style="color:#719e07">.</span>putIfAbsent<span style="color:#719e07">(</span>_path<span style="color:#719e07">,</span> _path<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>cachedPath <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cachedPath<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> _path<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>由如上代码片段可以得知,字符串重用即为简单地使用了 Map 来存储对应的缓存值,当你使用了相同的字符串时,便会从 Map 中获取早已存在的对象返回给调用方,由此便可以减少堆内存中重复的字符串数以达到优化的效果。</p> |
| <h3 id="优化结果">优化结果</h3> |
| <p>这里优化结果我引用了<a href="https://zhuanlan.zhihu.com/p/345626851">《Dubbo 3.0 前瞻:服务发现支持百万集群,带来可伸缩微服务架构》</a>这篇文章中的两副图来说明,下图模拟了在<strong>220万</strong>个 Provider 接口的情况下,接口数据不断变更导致的 Consumer 端的消耗,我们看到整个 Consumer 端几乎被 Full GC 占满了,严重影响了性能。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-7.png" alt="Dubbo2 接口级地址模型.png"></p> |
| <p>那么我们再来看看 Dubbo 3.0 中对 URL 进行优化后同一个环境下的压测结果,如下图所示。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/url-perf-tuning-8.png" alt="Dubbo3 接口级地址模型.png"></p> |
| <p>我们明显可以看到 Full GC 的频率减少到了只有 3 次,大大提升了性能。当然,该文章中还有其他方面的对比,此处便不一一引用了,感兴趣的读者可以自行去阅读该文章。</p></description></item><item><title>Blog: Dubbo3 应用级服务发现</title><link>https://dubbo.apache.org/zh-cn/blog/2021/06/02/dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</link><pubDate>Wed, 02 Jun 2021 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2021/06/02/dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</guid><description> |
| <h2 id="1-服务发现service-discovery-概述">1 服务发现(Service Discovery) 概述</h2> |
| <p>从 Internet 刚开始兴起,如何动态感知后端服务的地址变化就是一个必须要面对的问题,为此人们定义了 DNS 协议,基于此协议,调用方只需要记住由固定字符串组成的域名,就能轻松完成对后端服务的访问,而不用担心流量最终会访问到哪些机器 IP,因为有代理组件会基于 DNS 地址解析后的地址列表,将流量透明的、均匀的分发到不同的后端机器上。</p> |
| <p>在使用微服务构建复杂的分布式系统时,如何感知 backend 服务实例的动态上下线,也是微服务框架最需要关心并解决的问题之一。业界将这个问题称之为 - 微服务的地址发现(Service Discovery),业界比较有代表性的微服务框架如 SpringCloud、Dubbo 等都抽象了强大的动态地址发现能力,并且为了满足微服务业务场景的需求,绝大多数框架的地址发现都是基于自己设计的一套机制来实现,因此在能力、灵活性上都要比传统 DNS 丰富得多。如 SpringCloud 中常用的 Eureka, Dubbo 中常用的 Zookeeper、Nacos 等,这些注册中心实现不止能够传递地址(IP + Port),还包括一些微服务的 Metadata 信息,如实例序列化类型、实例方法列表、各个方法级的定制化配置等。</p> |
| <p>下图是微服务中 Service Discovery 的基本工作原理图,微服务体系中的实例大概可分为三种角色:服务提供者(Provider)、服务消费者(Consumer)和注册中心(Registry)。而不同框架实现间最主要的区别就体现在注册中心数据的组织:地址如何组织、以什么粒度组织、除地址外还同步哪些数据?</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-1.png" alt="img1"></p> |
| <p>我们今天这篇文章就是围绕这三个角色展开,重点看下 Dubbo 中对于服务发现方案的设计,包括之前老的服务发现方案的优势和缺点,以及 Dubbo 3.0 中正在设计、开发中的全新的<strong>面向应用粒度的地址发现方案</strong>,我们期待这个新的方案能做到:</p> |
| <ul> |
| <li><strong>支持几十万/上百万级集群实例的地址发现</strong></li> |
| <li><strong>与不同的微服务体系(如 Spring Cloud)实现在地址发现层面的互通</strong></li> |
| </ul> |
| <h2 id="2-dubbo-地址发现机制解析">2 Dubbo 地址发现机制解析</h2> |
| <p>我们先以一个 DEMO 应用为例,来快速的看一下 Dubbo “接口粒度”服务发现与“应用粒度”服务发现体现出来的区别。这里我们重点关注 Provider 实例是如何向注册中心注册的,并且,为了体现注册中心数据量变化,我们观察的是两个 Provider 实例的场景。</p> |
| <p><strong>应用 DEMO 提供的服务列表如下:</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.DemoService&#34;</span> ref=<span style="color:#2aa198">&#34;demoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.basic.api.GreetingService&#34;</span> ref=<span style="color:#2aa198">&#34;greetingService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>我们示例注册中心实现采用的是 Zookeeper ,启动 192.168.0.103 和 192.168.0.104 两个实例后,以下是两种模式下注册中心的实际数据</p> |
| <p><strong>1. “接口粒度” 服务发现</strong></p> |
| <p>192.168.0.103 实例注册的数据</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>dubbo://192.168.0.103:20880/org.apache.dubbo.samples.basic.api.DemoService?anyhost=true&amp;application=demo-provider&amp;default=true&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.samples.basic.api.DemoService&amp;methods=testVoid,sayHello&amp;pid=995&amp;release=2.7.7&amp;side=provider&amp;timestamp=1596988171266 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>dubbo://192.168.0.103:20880/org.apache.dubbo.samples.basic.api.GreetingService?anyhost=true&amp;application=demo-provider&amp;default=true&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.samples.basic.api.GreetingService&amp;methods=greeting&amp;pid=995&amp;release=2.7.7&amp;side=provider&amp;timestamp=1596988170816 |
| </span></span></code></pre></div><p>192.168.0.104 实例注册的数据</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>dubbo://192.168.0.104:20880/org.apache.dubbo.samples.basic.api.DemoService?anyhost=true&amp;application=demo-provider&amp;default=true&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.samples.basic.api.DemoService&amp;methods=testVoid,sayHello&amp;pid=995&amp;release=2.7.7&amp;side=provider&amp;timestamp=1596988171266 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>dubbo://192.168.0.104:20880/org.apache.dubbo.samples.basic.api.GreetingService?anyhost=true&amp;application=demo-provider&amp;default=true&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.samples.basic.api.GreetingService&amp;methods=greeting&amp;pid=995&amp;release=2.7.7&amp;side=provider&amp;timestamp=1596988170816 |
| </span></span></code></pre></div><p><strong>2. “应用粒度” 服务发现</strong></p> |
| <p>192.168.0.103 与 192.168.0.104 两个实例共享一份注册中心数据,如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;demo-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;id&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.103:20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;address&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.103&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;port&#34;</span>: <span style="color:#2aa198">20880</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;metadata&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.endpoints&#34;</span>: <span style="color:#2aa198">&#34;[{\&#34;port\&#34;:20880,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}]&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.metadata.storage-type&#34;</span>: <span style="color:#2aa198">&#34;local&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.revision&#34;</span>: <span style="color:#2aa198">&#34;6785535733750099598&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;time&#34;</span>: <span style="color:#2aa198">1583461240877</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;demo-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;id&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.104:20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;address&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.104&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;port&#34;</span>: <span style="color:#2aa198">20880</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;metadata&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.endpoints&#34;</span>: <span style="color:#2aa198">&#34;[{\&#34;port\&#34;:20880,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}]&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.metadata.storage-type&#34;</span>: <span style="color:#2aa198">&#34;local&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;dubbo.revision&#34;</span>: <span style="color:#2aa198">&#34;7829635812370099387&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;time&#34;</span>: <span style="color:#2aa198">1583461240877</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>对比以上两种不同粒度的服务发现模式,从 “接口粒度” 升级到 “应用粒度” 后我们可以总结出最大的区别是:注册中心数据量不再与接口数成正比,不论应用提供有多少接口,注册中心只有一条实例数据。</p> |
| <p>那么接下来我们详细看下这个变化给 Dubbo 带来了哪些好处。</p> |
| <h2 id="3-dubbo-应用级服务发现的意义">3 Dubbo 应用级服务发现的意义</h2> |
| <p>我们先说结论,应用级服务发现给 Dubbo 带来以下优势:</p> |
| <ol> |
| <li>与业界主流微服务模型对齐,比如 SpringCloud、Kubernetes Native Service等。</li> |
| <li>提升性能与可伸缩性。注册中心数据的重新组织(减少),能最大幅度的减轻注册中心的存储、推送压力,进而减少 Dubbo Consumer 侧的地址计算压力;集群规模也开始变得可预测、可评估(与 RPC 接口数量无关,只与实例部署规模相关)。</li> |
| </ol> |
| <h3 id="31-对齐主流微服务模型">3.1 对齐主流微服务模型</h3> |
| <p>自动、透明的实例地址发现(负载均衡)是所有微服务框架需要解决的事情,这能让后端的部署结构对上游微服务透明,上游服务只需要从收到的地址列表中选取一个,发起调用就可以了。要实现以上目标,涉及两个关键点的自动同步:</p> |
| <ul> |
| <li>实例地址,服务消费方需要知道地址以建立连接</li> |
| <li>RPC 方法定义,服务消费方需要知道 RPC 服务的具体定义,不论服务类型是 rest 或 rmi 等。</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-2.png" alt="img2"></p> |
| <p>对于 RPC 实例间借助注册中心的数据同步,REST 定义了一套非常有意思的成熟度模型,感兴趣的朋友可以参考这里的链接 <a href="https://www.martinfowler.com/articles/richardsonMaturityModel.html">https://www.martinfowler.com/articles/richardsonMaturityModel.html</a>, 按照文章中的 4 级成熟度定义,Dubbo 当前基于接口粒度的模型可以对应到 L4 级别。</p> |
| <p>接下来,我们看看 Dubbo、SpringCloud 以及 Kubernetes 分别是怎么围绕自动化的实例地址发现这个目标设计的。</p> |
| <p><strong>1. Spring Cloud</strong></p> |
| <p>Spring Cloud 通过注册中心只同步了应用与实例地址,消费方可以基于实例地址与服务提供方建立连接,但是消费方对于如何发起 http 调用(SpringCloud 基于 rest 通信)一无所知,比如对方有哪些 http endpoint,需要传入哪些参数等。</p> |
| <p>RPC 服务这部分信息目前都是通过线下约定或离线的管理系统来协商的。这种架构的优缺点总结如下。 |
| 优势:部署结构清晰、地址推送量小; |
| 缺点:地址订阅需要指定应用名, provider 应用变更(拆分)需消费端感知;RPC 调用无法全自动同步。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-3.png" alt="img3"></p> |
| <p><strong>2. Dubbo</strong></p> |
| <p>Dubbo 通过注册中心同时同步了实例地址和 RPC 方法,因此其能实现 RPC 过程的自动同步,面向 RPC 编程、面向 RPC 治理,对后端应用的拆分消费端无感知,其缺点则是地址推送数量变大,和 RPC 方法成正比。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-4.png" alt="img4"></p> |
| <p><strong>3. Dubbo + Kubernetes</strong></p> |
| <p>Dubbo 要支持 Kubernetes native service,相比之前自建注册中心的服务发现体系来说,在工作机制上主要有两点变化:</p> |
| <ul> |
| <li>服务注册由平台接管,provider 不再需要关心服务注册</li> |
| <li>consumer 端服务发现将是 Dubbo 关注的重点,通过对接平台层的 API-Server、DNS 等,Dubbo client 可以通过一个 <a href="https://kubernetes.io/docs/concepts/services-networking/service/">Service Name</a>(通常对应到 Application Name)查询到一组 Endpoints(一组运行 provider 的 pod),通过将 Endpoints 映射到 Dubbo 内部地址列表,以驱动 Dubbo 内置的负载均衡机制工作。</li> |
| </ul> |
| <blockquote> |
| <p>Kubernetes Service 作为一个抽象概念,怎么映射到 Dubbo 是一个值得讨论的点</p> |
| <ul> |
| <li>Service Name - &gt; Application Name,Dubbo 应用和 Kubernetes 服务一一对应,对于微服务运维和建设环节透明,与开发阶段解耦。</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Service |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: provider-app-name |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: provider-app-name |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">protocol</span>: TCP |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">port</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">targetPort</span>: <span style="color:#2aa198">9376</span> |
| </span></span></code></pre></div><ul> |
| <li>Service Name - &gt; Dubbo RPC Service,Kubernetes 要维护调度的服务与应用内建 RPC 服务绑定,维护的服务数量变多。</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>--- |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Service |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: rpc-service-1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: provider-app-name |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: <span style="color:#586e75">##</span> |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span>--- |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Service |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: rpc-service-2 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: provider-app-name |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: <span style="color:#586e75">##</span> |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span>--- |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">apiVersion</span>: v1 |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">kind</span>: Service |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">metadata</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: rpc-service-N |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">spec</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">selector</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">app</span>: provider-app-name |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">ports</span>: <span style="color:#586e75">##</span> |
| </span></span><span style="display:flex;"><span>... |
| </span></span></code></pre></div></blockquote> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-5.png" alt="img5"></p> |
| <p>结合以上几种不同微服务框架模型的分析,我们可以发现,Dubbo 与 SpringCloud、Kubernetes 等不同产品在微服务的抽象定义上还是存在很大不同的。SpringCloud 和 Kubernetes 在微服务的模型抽象上还是比较接近的,两者基本都只关心实例地址的同步,如果我们去关心其他的一些服务框架产品,会发现它们绝大多数也是这么设计的;</p> |
| <blockquote> |
| <p>即 REST 成熟度模型中的 L3 级别。</p> |
| </blockquote> |
| <p>对比起来 Dubbo 则相对是比较特殊的存在,更多的是从 RPC 服务的粒度去设计的。</p> |
| <blockquote> |
| <p>对应 REST 成熟度模型中的 L4 级别。</p> |
| </blockquote> |
| <p>如我们上面针对每种模型做了详细的分析,每种模型都有其优势和不足。而我们最初决定 Dubbo 要做出改变,往其他的微服务发现模型上的对齐,是我们最早在确定 Dubbo 的云原生方案时,我们发现要让 Dubbo 去支持 Kubernetes Native Service,模型对齐是一个基础条件;另一点是来自用户侧对 Dubbo 场景化的一些工程实践的需求,得益于 Dubbo 对多注册、多协议能力的支持,使得 Dubbo 联通不同的微服务体系成为可能,而服务发现模型的不一致成为其中的一个障碍,这部分的场景描述请参见以下文章:<a href="https://developer.aliyun.com/article/740260">Dubbo 如何成为连接异构微服务体系的最佳服务开发框架</a></p> |
| <h3 id="32-更大规模的微服务集群---解决性能瓶颈">3.2 更大规模的微服务集群 - 解决性能瓶颈</h3> |
| <p>这部分涉及到和注册中心、配置中心的交互,关于不同模型下注册中心数据的变化,之前原理部分我们简单分析过。为更直观的对比服务模型变更带来的推送效率提升,我们来通过一个示例看一下不同模型注册中心的对比:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-6.png" alt="img6"></p> |
| <p>图中左边是微服务框架的一个典型工作流程,Provider 和 Consumer 通过注册中心实现自动化的地址通知。其中,Provider 实例的信息如图中表格所示: |
| 应用 DEMO 包含三个接口 DemoService 1 2 3,当前实例的 ip 地址为 10.210.134.30。</p> |
| <ul> |
| <li>对于 Spring Cloud 和 Kubernetes 模型,注册中心只会存储一条 <code>DEMO - 10.210.134.30+metadata</code> 的数据;</li> |
| <li>对于老的 Dubbo 模型,注册中心存储了三条接口粒度的数据,分别对应三个接口 DemoService 1 2 3,并且很多的址数据都是重复的;</li> |
| </ul> |
| <p>可以总结出,基于应用粒度的模型所存储和推送的数据量是和应用、实例数成正比的,只有当我们的应用数增多或应用的实例数增长时,地址推送压力才会上涨。 |
| 而对于基于接口粒度的模型,数据量是和接口数量正相关的,鉴于一个应用通常发布多个接口的现状,这个数量级本身比应用粒度是要乘以倍数的;另外一个关键点在于,接口粒度导致的集群规模评估的不透明,相对于实例、应用增长都通常是在运维侧的规划之中,接口的定义更多的是业务侧的内部行为,往往可以绕过评估给集群带来压力。</p> |
| <p>以 Consumer 端服务订阅举例,根据我对社区部分 Dubbo 中大规模头部用户的粗略统计,根据受统计公司的实际场景,一个 Consumer 应用要消费(订阅)的 Provier 应用数量往往要超过 10 个,而具体到其要消费(订阅)的的接口数量则通常要达到 30 个,平均情况下 Consumer 订阅的 3 个接口来自同一个 Provider 应用,如此计算下来,如果以应用粒度为地址通知和选址基本单位,则平均地址推送和计算量将下降 60% 还要多, |
| 而在极端情况下,也就是当 Consumer 端消费的接口更多的来自同一个应用时,这个地址推送与内存消耗的占用将会进一步得到降低,甚至可以超过 80% 以上。</p> |
| <p>一个典型的极端场景即是 Dubbo 体系中的网关型应用,有些网关应用消费(订阅)达 100+ 应用,而消费(订阅)的服务有 1000+ ,平均有 10 个接口来自同一个应用,如果我们把地址推送和计算的粒度改为应用,则地址推送量从原来的 n * 1000 变为 n * 100,地址数量降低可达近 90%。</p> |
| <h2 id="4-应用级服务发现工作原理">4 应用级服务发现工作原理</h2> |
| <h3 id="41-设计原则">4.1 设计原则</h3> |
| <p>上面一节我们从<strong>服务模型</strong>及<strong>支撑大规模集群</strong>的角度分别给出了 Dubbo 往应用级服务发现靠拢的好处和原因,但这么做的同时接口粒度的服务治理能力还是要继续保留,这是 Dubbo 框架编程模型易用性、服务治理能力优势的基础。 |
| 以下是我认为我们做服务模型迁移仍要坚持的设计原则</p> |
| <ul> |
| <li>新的服务发现模型要实现对原有 Dubbo 消费端开发者的无感知迁移,即 Dubbo 继续面向 RPC 服务编程、面向 RPC 服务治理,做到对用户侧完全无感知。</li> |
| <li>建立 Consumer 与 Provider 间的自动化 RPC 服务元数据协调机制,解决传统微服务模型无法同步 RPC 级接口配置的缺点。</li> |
| </ul> |
| <h3 id="42-基本原理详解">4.2 基本原理详解</h3> |
| <p>应用级服务发现作为一种新的服务发现机制,和以前 Dubbo 基于 RPC 服务粒度的服务发现在核心流程上基本上是一致的:即服务提供者往注册中心注册地址信息,服务消费者从注册中心拉取&amp;订阅地址信息。</p> |
| <p>这里主要的不同有以下两点:</p> |
| <h4 id="421-注册中心数据以应用---实例列表格式组织不再包含-rpc-服务信息">4.2.1 注册中心数据以“应用 - 实例列表”格式组织,不再包含 RPC 服务信息</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-7.png" alt="img7"></p> |
| <p>以下是每个 Instance metadata 的示例数据,总的原则是 metadata 只包含当前 instance 节点相关的信息,不涉及 RPC 服务粒度的信息。</p> |
| <p>总体信息概括如下:实例地址、实例各种环境标、metadata service 元数据、其他少量必要属性。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;provider-app-name&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;id&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.102:20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;address&#34;</span>: <span style="color:#2aa198">&#34;192.168.0.102&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;port&#34;</span>: <span style="color:#2aa198">20880</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;sslPort&#34;</span>: <span style="color:#cb4b16">null</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;payload&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;id&#34;</span>: <span style="color:#cb4b16">null</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;name&#34;</span>: <span style="color:#2aa198">&#34;provider-app-name&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;metadata&#34;</span>: { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;metadataService&#34;</span>: <span style="color:#2aa198">&#34;{\&#34;dubbo\&#34;:{\&#34;version\&#34;:\&#34;1.0.0\&#34;,\&#34;dubbo\&#34;:\&#34;2.0.2\&#34;,\&#34;release\&#34;:\&#34;2.7.5\&#34;,\&#34;port\&#34;:\&#34;20881\&#34;}}&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;endpoints&#34;</span>: <span style="color:#2aa198">&#34;[{\&#34;port\&#34;:20880,\&#34;protocol\&#34;:\&#34;dubbo\&#34;}]&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;storage-type&#34;</span>: <span style="color:#2aa198">&#34;local&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;revision&#34;</span>: <span style="color:#2aa198">&#34;6785535733750099598&#34;</span>, |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;registrationTimeUTC&#34;</span>: <span style="color:#2aa198">1583461240877</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;serviceType&#34;</span>: <span style="color:#2aa198">&#34;DYNAMIC&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&#34;uriSpec&#34;</span>: <span style="color:#cb4b16">null</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h4 id="422-client--server-自行协商-rpc-方法信息">4.2.2 Client – Server 自行协商 RPC 方法信息</h4> |
| <p>在注册中心不再同步 RPC 服务信息后,服务自省在服务消费端和提供端之间建立了一条内置的 RPC 服务信息协商机制,这也是“服务自省”这个名字的由来。服务端实例会暴露一个预定义的 MetadataService RPC 服务,消费端通过调用 MetadataService 获取每个实例 RPC 方法相关的配置信息。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-8.png" alt="img8"></p> |
| <p>当前 MetadataService 返回的数据格式如下,</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[ |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo://192.168.0.102:20880/org.apache.dubbo.demo.DemoService?anyhost=true&amp;application=demo-provider&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=9585&amp;release=2.7.5&amp;side=provider&amp;timestamp=1583469714314&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo://192.168.0.102:20880/org.apache.dubbo.demo.HelloService?anyhost=true&amp;application=demo-provider&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=9585&amp;release=2.7.5&amp;side=provider&amp;timestamp=1583469714314&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo://192.168.0.102:20880/org.apache.dubbo.demo.WorldService?anyhost=true&amp;application=demo-provider&amp;deprecated=false&amp;dubbo=2.0.2&amp;dynamic=true&amp;generic=false&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=9585&amp;release=2.7.5&amp;side=provider&amp;timestamp=1583469714314&#34;</span> |
| </span></span><span style="display:flex;"><span>] |
| </span></span></code></pre></div><blockquote> |
| <p>熟悉 Dubbo 基于 RPC 服务粒度的服务发现模型的开发者应该能看出来,服务自省机制机制将以前注册中心传递的 URL 一拆为二:</p> |
| <ul> |
| <li>一部分和实例相关的数据继续保留在注册中心,如 ip、port、机器标识等。</li> |
| <li>另一部分和 RPC 方法相关的数据从注册中心移除,转而通过 MetadataService 暴露给消费端。</li> |
| </ul> |
| <p><strong>理想情况下是能达到数据按照实例、RPC 服务严格区分开来,但明显可以看到以上实现版本还存在一些数据冗余,有些数据也还未合理划分。尤其是 MetadataService 部分,其返回的数据还只是简单的 URL 列表组装,这些 URL其实是包含了全量的数据。</strong></p> |
| </blockquote> |
| <p>以下是服务自省的一个完整工作流程图,详细描述了服务注册、服务发现、MetadataService、RPC 调用间的协作流程。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-9.png" alt="img9"></p> |
| <ul> |
| <li>服务提供者启动,首先解析应用定义的“普通服务”并依次注册为 RPC 服务,紧接着注册内建的 MetadataService 服务,最后打开 TCP 监听端口。</li> |
| <li>启动完成后,将实例信息注册到注册中心(仅限 ip、port 等实例相关数据),提供者启动完成。</li> |
| <li>服务消费者启动,首先依据其要“消费的 provider 应用名”到注册中心查询地址列表,并完成订阅(以实现后续地址变更自动通知)。</li> |
| <li>消费端拿到地址列表后,紧接着对 MetadataService 发起调用,返回结果中包含了所有应用定义的“普通服务”及其相关配置信息。</li> |
| <li>至此,消费者可以接收外部流量,并对提供者发起 Dubbo RPC 调用</li> |
| </ul> |
| <blockquote> |
| <p>在以上流程中,我们只考虑了一切顺利的情况,但在更详细的设计或编码实现中,我们还需要严格约定一些异常场景下的框架行为。比如,如果消费者 MetadataService 调用失败,则在重试直到成功之前,消费者将不可以接收外部流量。</p> |
| </blockquote> |
| <h3 id="43-服务自省中的关键机制">4.3 服务自省中的关键机制</h3> |
| <h4 id="431-元数据同步机制">4.3.1 元数据同步机制</h4> |
| <p>Client 与 Server 间在收到地址推送后的配置同步是服务自省的关键环节,目前针对元数据同步有两种具体的可选方案,分别是:</p> |
| <ul> |
| <li>内建 MetadataService。</li> |
| <li>独立的元数据中心,通过中心化的元数据集群协调数据。</li> |
| </ul> |
| <p><strong>1. 内建 MetadataService</strong> |
| MetadataService 通过标准的 Dubbo 协议暴露,根据查询条件,会将内存中符合条件的“普通服务”配置返回给消费者。这一步发生在消费端选址和调用前。</p> |
| <p><strong>2. 元数据中心</strong> |
| 复用 2.7 版本中引入的元数据中心,provider 实例启动后,会尝试将内部的 RPC 服务组织成元数据的格式同步到元数据中心,而 consumer 则在每次收到注册中心推送更新后,主动查询元数据中心。</p> |
| <blockquote> |
| <p>注意 consumer 端查询元数据中心的时机,是等到注册中心的地址更新通知之后。也就是通过注册中心下发的数据,我们能明确的知道何时某个实例的元数据被更新了,此时才需要去查元数据中心。</p> |
| </blockquote> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-10.png" alt="img10"></p> |
| <h4 id="432-rpc-服务-----应用映射关系">4.3.2 RPC 服务 &lt; - &gt; 应用映射关系</h4> |
| <p>回顾上文讲到的注册中心关于“应用 - 实例列表”结构的数据组织形式,这个变动目前对开发者并不是完全透明的,业务开发侧会感知到查询/订阅地址列表的机制的变化。具体来说,相比以往我们基于 RPC 服务来检索地址,现在 consumer 需要通过指定 provider 应用名才能实现地址查询或订阅。</p> |
| <p>老的 Consumer 开发与配置示例:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 框架直接通过 RPC Service 1/2/N 去注册中心查询或订阅地址列表 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service 1&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service 2&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service N&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>新的 Consumer 开发与配置示例:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- 框架需要通过额外的 provided-by=&#34;provider-app-x&#34; 才能在注册中心查询或订阅到地址列表 --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:registry</span> address=<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181?registry-type=service&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service 1&#34;</span> provided-by=<span style="color:#2aa198">&#34;provider-app-x&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service 2&#34;</span> provided-by=<span style="color:#2aa198">&#34;provider-app-x&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:reference</span> interface=<span style="color:#2aa198">&#34;RPC Service N&#34;</span> provided-by=<span style="color:#2aa198">&#34;provider-app-y&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>以上指定 provider 应用名的方式是 Spring Cloud 当前的做法,需要 consumer 端的开发者显示指定其要消费的 provider 应用。</p> |
| <p>以上问题的根源在于注册中心不知道任何 RPC 服务相关的信息,因此只能通过应用名来查询。</p> |
| <p>为了使整个开发流程对老的 Dubbo 用户更透明,同时避免指定 provider 对可扩展性带来的影响(参见下方说明),我们设计了一套 <code>RPC 服务到应用名</code>的映射关系,以尝试在 consumer 端自动完成 RPC 服务到 provider 应用名的转换。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/service-discovery-11.png" alt="img11"></p> |
| <blockquote> |
| <p>Dubbo 之所以选择建立一套“接口-应用”的映射关系,主要是考虑到 service - app 映射关系的不确定性。一个典型的场景即是应用/服务拆分,如上面提到的配置<code>&lt;dubbo:reference interface=&quot;RPC Service 2&quot; provided-by=&quot;provider-app-x&quot; /&gt;</code>,PC Service 2 是定义于 provider-app-x 中的一个服务,未来它随时可能会被开发者分拆到另外一个新的应用如 provider-app-x-1 中,这个拆分要被所有的 PC Service 2 消费方感知到,并对应用进行修改升级,如改为<code>&lt;dubbo:reference interface=&quot;RPC Service 2&quot; provided-by=&quot;provider-app-x-1&quot; /&gt;</code>,这样的升级成本不可否认还是挺高的。 |
| 到底是 Dubbo 框架帮助开发者透明的解决这个问题,还是交由开发者自己去解决,当然这只是个策略选择问题,并且 Dubbo 2.7.5+ 版本目前是都提供了的。其实我个人更倾向于交由业务开发者通过组织上的约束来做,这样也可进一步降低 Dubbo 框架的复杂度,提升运行态的稳定性。</p> |
| </blockquote> |
| <h2 id="5-总结与展望">5 总结与展望</h2> |
| <p>应用级服务发现机制是 Dubbo 面向云原生走出的重要一步,它帮 Dubbo 打通了与其他微服务体系之间在地址发现层面的鸿沟,也成为 Dubbo 适配 Kubernetes Native Service 等基础设施的基础。我们期望 Dubbo 在新模型基础上,能继续保留在编程易用性、服务治理能力等方面强大的优势。但是我们也应该看到应用粒度的模型一方面带来了新的复杂性,需要我们继续去优化与增强;另一方面,除了地址存储与推送之外,应用粒度在帮助 Dubbo 选址层面也有进一步挖掘的潜力。</p></description></item><item><title>Blog: dubbo-go源码笔记(二)客户端调用过程</title><link>https://dubbo.apache.org/zh-cn/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://dubbo.apache.org/zh-cn/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> |
| <p>随着微服务架构的流行,许多高性能 rpc 框架应运而生,由阿里开源的 dubbo 框架 go 语言版本的 dubbo-go 也成为了众多开发者不错的选择。本文将介绍 dubbo-go 框架的基本使用方法,以及从 export 调用链的角度进行 server 端源码导读,希望能引导读者进一步认识这款框架。</p> |
| <h2 id="前言">前言</h2> |
| <p>有了上一篇文章<a href="https://dubbo.apache.org/zh-cn/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服务暴露过程详解》</a> 的铺垫,可以大致上类比客户端服务类似于服务端启动过程。其中最大的区别是服务端通过zk注册服务,发布自己的ivkURL并订阅事件开启监听;而服务端应该是通过zk注册组件,<strong>拿到需要调用的serviceURL</strong>,<strong>更新invoker</strong>并<strong>重写用户的RPCService</strong>,从而实现对远程过程调用细节的封装。</p> |
| <h2 id="1-配置文件和客户端源码">1. 配置文件和客户端源码</h2> |
| <h4 id="11-client配置文件">1.1 client配置文件</h4> |
| <p>helloworld提供的demo:profiles/client.yaml</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">registries </span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;demoZk&#34;</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protocol</span>: <span style="color:#2aa198">&#34;zookeeper&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">timeout </span>: <span style="color:#2aa198">&#34;3s&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">address</span>: <span style="color:#2aa198">&#34;127.0.0.1:2181&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">username</span>: <span style="color:#2aa198">&#34;&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">password</span>: <span style="color:#2aa198">&#34;&#34;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">references</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;UserProvider&#34;</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75"># 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">registry</span>: <span style="color:#2aa198">&#34;demoZk&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protocol </span>: <span style="color:#2aa198">&#34;dubbo&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">interface </span>: <span style="color:#2aa198">&#34;com.ikurento.user.UserProvider&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">cluster</span>: <span style="color:#2aa198">&#34;failover&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">methods </span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">name</span>: <span style="color:#2aa198">&#34;GetUser&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">retries</span>: <span style="color:#2aa198">3</span> |
| </span></span></code></pre></div><p>可看到配置文件与之前讨论过的server端非常类似,其refrences部分字段就是对当前服务要主调的服务的配置,其中详细说明了调用协议、注册协议、接口id、调用方法、集群策略等,这些配置都会在之后与注册组件交互,重写ivk、调用的过程中使用到。</p> |
| <h4 id="12-客户端使用框架源码">1.2 客户端使用框架源码</h4> |
| <p>user.go</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">init</span>() { |
| </span></span><span style="display:flex;"><span> config.<span style="color:#268bd2">SetConsumerService</span>(userProvider) |
| </span></span><span style="display:flex;"><span> hessian.<span style="color:#268bd2">RegisterPOJO</span>(<span style="color:#719e07">&amp;</span>User{}) |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>main.go</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">main</span>() { |
| </span></span><span style="display:flex;"><span> hessian.<span style="color:#268bd2">RegisterPOJO</span>(<span style="color:#719e07">&amp;</span>User{}) |
| </span></span><span style="display:flex;"><span> config.<span style="color:#268bd2">Load</span>() |
| </span></span><span style="display:flex;"><span> time.<span style="color:#268bd2">Sleep</span>(<span style="color:#2aa198">3e9</span>) |
| </span></span><span style="display:flex;"><span> <span style="color:#b58900">println</span>(<span style="color:#2aa198">&#34;\n\n\nstart to test dubbo&#34;</span>) |
| </span></span><span style="display:flex;"><span> user <span style="color:#719e07">:=</span> <span style="color:#719e07">&amp;</span>User{} |
| </span></span><span style="display:flex;"><span> err <span style="color:#719e07">:=</span> userProvider.<span style="color:#268bd2">GetUser</span>(context.<span style="color:#268bd2">TODO</span>(), []<span style="color:#268bd2">interface</span>{}{<span style="color:#2aa198">&#34;A001&#34;</span>}, user) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#b58900">panic</span>(err) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#b58900">println</span>(<span style="color:#2aa198">&#34;response result: %v\n&#34;</span>, user) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">initSignal</span>() |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>官网提供的helloworld demo的源码。可看到与服务端类似,在user.go内注册了rpc-service,以及需要rpc传输的结构体user。</p> |
| <p>在main函数中,同样调用了config.Load()函数,之后就可以直接通过实现好的rpc-service:userProvider 直接调用对应的功能函数,即可实现rpc调用。</p> |
| <p>可以猜到,从hessian注册结构、SetConsumerService,到调用函数.GetUser()期间,用户定义的rpc-service也就是userProvider对应的函数被重写,重写后的GetUser函数已经包含了实现了远程调用逻辑的invoker。</p> |
| <p>接下来,就要通过阅读源码,看看dubbo-go是如何做到的。</p> |
| <h2 id="2-实现远程过程调用">2. 实现远程过程调用</h2> |
| <h4 id="21-加载配置文件">2.1 加载配置文件</h4> |
| <p>config/config_loader.go :Load()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// Load Dubbo Init |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> <span style="color:#268bd2">Load</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init router |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">initRouter</span>() |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init the global event dispatcher |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extension.<span style="color:#268bd2">SetAndInitGlobalDispatcher</span>(<span style="color:#268bd2">GetBaseConfig</span>().EventDispatcherType) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// start the metadata report if config set |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> err <span style="color:#719e07">:=</span> <span style="color:#268bd2">startMetadataReport</span>(<span style="color:#268bd2">GetApplicationConfig</span>().MetadataType, <span style="color:#268bd2">GetBaseConfig</span>().MetadataReportConfig); err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> logger.<span style="color:#268bd2">Errorf</span>(<span style="color:#2aa198">&#34;Provider starts metadata report error, and the error is {%#v}&#34;</span>, err) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// reference config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">loadConsumerConfig</span>() |
| </span></span></code></pre></div><p>在main函数中调用的config.Load()函数,进而调用了loadConsumerConfig,类似于之前讲到的server端配置读入函数。</p> |
| <p>在loadConsumerConfig函数中,进行了三步操作:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code2/p1.png" alt="img"></p> |
| <ol> |
| <li>检查配置文件并将配置写入内存</li> |
| <li><strong>在for循环内部</strong>,依次引用(refer)并且实例化(implement)每个被调reference。</li> |
| <li>等待三秒钟所有invoker就绪</li> |
| </ol> |
| <p>其中重要的就是for循环里面的引用和实例化,两步操作,会在接下来展开讨论。</p> |
| <p>至此,配置已经被写入了框架。</p> |
| <h4 id="22-获取远程service-url实现可供调用的invoker">2.2 获取远程Service URL,实现可供调用的invoker</h4> |
| <p>上述的ref.Refer完成的就是这部分的操作。</p> |
| <p>图(一)</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code2/p2.png" alt="img"></p> |
| <h5 id="221-构造注册url">2.2.1 构造注册url</h5> |
| <p>和server端类似,存在注册url和服务url,dubbo习惯将服务url作为注册url的sub。</p> |
| <p>config/reference_config.go: Refer()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#719e07">/</span> Refer <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">func</span> (c <span style="color:#719e07">*</span>ReferenceConfig) <span style="color:#268bd2">Refer</span>(_ <span style="color:#268bd2">interface</span>{}) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//(一)配置url参数(serviceUrl),将会作为sub |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cfgURL <span style="color:#719e07">:=</span> common.<span style="color:#268bd2">NewURLWithOptions</span>( |
| </span></span><span style="display:flex;"><span> common.<span style="color:#268bd2">WithPath</span>(c.id), |
| </span></span><span style="display:flex;"><span> common.<span style="color:#268bd2">WithProtocol</span>(c.Protocol), |
| </span></span><span style="display:flex;"><span> common.<span style="color:#268bd2">WithParams</span>(c.<span style="color:#268bd2">getUrlMap</span>()), |
| </span></span><span style="display:flex;"><span> common.<span style="color:#268bd2">WithParamsValue</span>(constant.BEAN_NAME_KEY, c.id), |
| </span></span><span style="display:flex;"><span> ) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// (二)注册地址可以通过url格式给定,也可以通过配置格式给定 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 这一步的意义就是配置-&gt;提取信息生成URL |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> c.Url <span style="color:#719e07">!=</span> <span style="color:#2aa198">&#34;&#34;</span> {<span style="color:#586e75">// 用户给定url信息,可以是点对点的地址,也可以是注册中心的地址 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 1. user specified URL, could be peer-to-peer address, or register center&#39;s address. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> urlStrings <span style="color:#719e07">:=</span> gxstrings.<span style="color:#268bd2">RegSplit</span>(c.Url, <span style="color:#2aa198">&#34;\\s*[;]+\\s*&#34;</span>) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> _, urlStr <span style="color:#719e07">:=</span> <span style="color:#719e07">range</span> urlStrings { |
| </span></span><span style="display:flex;"><span> serviceUrl, err <span style="color:#719e07">:=</span> common.<span style="color:#268bd2">NewURL</span>(urlStr) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> {<span style="color:#586e75">// 配置读入注册中心的信息 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// assemble SubURL from register center&#39;s configuration mode |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 这是注册url,protocol = registry,包含了zk的用户名、密码、ip等等 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> c.urls = <span style="color:#268bd2">loadRegistries</span>(c.Registry, consumerConfig.Registries, common.CONSUMER) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// set url to regUrls |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">for</span> _, regUrl <span style="color:#719e07">:=</span> <span style="color:#719e07">range</span> c.urls { |
| </span></span><span style="display:flex;"><span> regUrl.SubURL = cfgURL<span style="color:#586e75">// regUrl的subURl存当前配置url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//至此,无论通过什么形式,已经拿到了全部的regURL |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// (三)获取registryProtocol实例,调用其Refer方法,传入新构建好的regURL |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#b58900">len</span>(c.urls) <span style="color:#719e07">==</span> <span style="color:#2aa198">1</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 这一步访问到registry/protocol/protocol.go registryProtocol.Refer |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 这里是registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> c.invoker = extension.<span style="color:#268bd2">GetProtocol</span>(c.urls[<span style="color:#2aa198">0</span>].Protocol).<span style="color:#268bd2">Refer</span>(<span style="color:#719e07">*</span>c.urls[<span style="color:#2aa198">0</span>]) |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 如果有多个注册中心,即有多个invoker,则采取集群策略 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invokers <span style="color:#719e07">:=</span> <span style="color:#b58900">make</span>([]protocol.Invoker, <span style="color:#2aa198">0</span>, <span style="color:#b58900">len</span>(c.urls)) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>这个函数中,已经处理完从Register配置到RegisterURL的转换,即图(一)中部分:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code2/p3.png" alt="img"></p> |
| <p>接下来,已经拿到的url将被传递给RegistryProtocol,进一步refer。</p> |
| <h5 id="222-registryprotocol获取到zkregistry实例进一步refer">2.2.2 registryProtocol获取到zkRegistry实例,进一步Refer</h5> |
| <p>registry/protocol/protocol.go: Refer</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// Refer provider service from registry center |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// 拿到的是配置文件registries的url,他能够生成一个invoker = 指向目的addr,以供客户端直接调用。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> (proto <span style="color:#719e07">*</span>registryProtocol) <span style="color:#268bd2">Refer</span>(url common.URL) protocol.Invoker { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">var</span> registryUrl = url |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 这里拿到的是referenceConfig,serviceUrl里面包含了Reference的所有信息,包含interfaceName、method等等 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">var</span> serviceUrl = registryUrl.SubURL |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> registryUrl.Protocol <span style="color:#719e07">==</span> constant.REGISTRY_PROTOCOL {<span style="color:#586e75">// registryUrl.Proto = &#34;registry&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> protocol <span style="color:#719e07">:=</span> registryUrl.<span style="color:#268bd2">GetParam</span>(constant.REGISTRY_KEY, <span style="color:#2aa198">&#34;&#34;</span>) |
| </span></span><span style="display:flex;"><span> registryUrl.Protocol = protocol<span style="color:#586e75">//替换成了具体的值,比如&#34;zookeeper&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 接口对象 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">var</span> reg registry.Registry |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// (一)实例化接口对象,缓存策略 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> regI, loaded <span style="color:#719e07">:=</span> proto.registries.<span style="color:#268bd2">Load</span>(registryUrl.<span style="color:#268bd2">Key</span>()); !loaded { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 缓存中不存在当前registry,新建一个reg |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> reg = <span style="color:#268bd2">getRegistry</span>(<span style="color:#719e07">&amp;</span>registryUrl) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 缓存起来 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> proto.registries.<span style="color:#268bd2">Store</span>(registryUrl.<span style="color:#268bd2">Key</span>(), reg) |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> reg = regI.(registry.Registry) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 到这里,获取到了reg实例 zookeeper的registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//(二)根据Register的实例zkRegistry和传入的regURL新建一个directory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 这一步存在复杂的异步逻辑,从注册中心拿到了目的service的真实addr,获取了invoker并放入directory, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 这一步将在下面详细给出步骤 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// new registry directory for store service url from registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> directory, err <span style="color:#719e07">:=</span> extension.<span style="color:#268bd2">GetDefaultRegistryDirectory</span>(<span style="color:#719e07">&amp;</span>registryUrl, reg) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> logger.<span style="color:#268bd2">Errorf</span>(<span style="color:#2aa198">&#34;consumer service %v create registry directory error, error message is %s, and will return nil invoker!&#34;</span>, |
| </span></span><span style="display:flex;"><span> serviceUrl.<span style="color:#268bd2">String</span>(), err.<span style="color:#268bd2">Error</span>()) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">nil</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// (三)DoRegister 在zk上注册当前client service |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> err = reg.<span style="color:#268bd2">Register</span>(<span style="color:#719e07">*</span>serviceUrl) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> logger.<span style="color:#268bd2">Errorf</span>(<span style="color:#2aa198">&#34;consumer service %v register registry %v error, error message is %s&#34;</span>, |
| </span></span><span style="display:flex;"><span> serviceUrl.<span style="color:#268bd2">String</span>(), registryUrl.<span style="color:#268bd2">String</span>(), err.<span style="color:#268bd2">Error</span>()) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// (四)new cluster invoker,将directory写入集群,获得具有集群策略的invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cluster <span style="color:#719e07">:=</span> extension.<span style="color:#268bd2">GetCluster</span>(serviceUrl.<span style="color:#268bd2">GetParam</span>(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER)) |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">:=</span> cluster.<span style="color:#268bd2">Join</span>(directory) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// invoker保存 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> proto.invokers = <span style="color:#b58900">append</span>(proto.invokers, invoker) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>可详细阅读上述注释,这个函数完成了从url到invoker的全部过程</p> |
| <p>(一)首先获得Registry对象,默认是之前实例化的zkRegistry,和之前server获取Registry的处理很类似。 |
| (二)通过构造一个新的directory,异步拿到之前在zk上注册的server端信息,生成invoker |
| (三)在zk上注册当前service |
| (四)集群策略,获得最终invoker</p> |
| <p>这一步完成了图(一)中所有余下的绝大多数操作,接下来就需要详细的查看directory的构造过程:</p> |
| <h5 id="223-构造directory包含较复杂的异步操作">2.2.3 构造directory(包含较复杂的异步操作)</h5> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code2/p4.png" alt="img"></p> |
| <p>图(二)</p> |
| <p>上述的 <code>extension.GetDefaultRegistryDirectory(&amp;registryUrl, reg)</code>函数,本质上调用了已经注册好的<code>NewRegistryDirectory</code>函数:</p> |
| <p>registry/directory/directory.go: NewRegistryDirectory()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// NewRegistryDirectory will create a new RegistryDirectory |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// 这个函数作为default注册在extension上面 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// url为注册url,reg为zookeeper registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> <span style="color:#268bd2">NewRegistryDirectory</span>(url <span style="color:#719e07">*</span>common.URL, registry registry.Registry) (cluster.Directory, <span style="color:#dc322f">error</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> url.SubURL <span style="color:#719e07">==</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">nil</span>, perrors.<span style="color:#268bd2">Errorf</span>(<span style="color:#2aa198">&#34;url is invalid, suburl can not be nil&#34;</span>) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> dir <span style="color:#719e07">:=</span> <span style="color:#719e07">&amp;</span>RegistryDirectory{ |
| </span></span><span style="display:flex;"><span> BaseDirectory: directory.<span style="color:#268bd2">NewBaseDirectory</span>(url), |
| </span></span><span style="display:flex;"><span> cacheInvokers: []protocol.Invoker{}, |
| </span></span><span style="display:flex;"><span> cacheInvokersMap: <span style="color:#719e07">&amp;</span>sync.Map{}, |
| </span></span><span style="display:flex;"><span> serviceType: url.SubURL.<span style="color:#268bd2">Service</span>(), |
| </span></span><span style="display:flex;"><span> registry: registry, |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> dir.consumerConfigurationListener = <span style="color:#268bd2">newConsumerConfigurationListener</span>(dir) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">go</span> dir.<span style="color:#268bd2">subscribe</span>(url.SubURL) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> dir, <span style="color:#cb4b16">nil</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>首先构造了一个注册directory,开启携程调用其subscribe函数,传入serviceURL。</p> |
| <p>这个directory目前包含了对应的zkRegistry,以及传入的URL,他cacheInvokers的部分是空的。</p> |
| <p>进入dir.subscribe(url.SubURL)这个异步函数:</p> |
| <p>registry/directory/directory.go: subscribe()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// subscribe from registry |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> (dir <span style="color:#719e07">*</span>RegistryDirectory) <span style="color:#268bd2">subscribe</span>(url <span style="color:#719e07">*</span>common.URL) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 增加两个监听, |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> dir.consumerConfigurationListener.<span style="color:#268bd2">addNotifyListener</span>(dir) |
| </span></span><span style="display:flex;"><span> dir.referenceConfigurationListener = <span style="color:#268bd2">newReferenceConfigurationListener</span>(dir, url) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// subscribe调用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> dir.registry.<span style="color:#268bd2">Subscribe</span>(url, dir) |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>重点来了,他调用了zkRegistry的Subscribe方法,与此同时将自己作为ConfigListener传入</p> |
| <blockquote> |
| <p>我认为这种传入listener的设计模式非常值得学习,而且很有java的味道。</p> |
| <p>针对等待zk返回订阅信息这样的异步操作,需要传入一个Listener,这个Listener需要实现Notify方法,进而在作为参数传入内部之后,可以被异步地调用Notify,将内部触发的异步事件“传递出来”,再进一步处理加工。</p> |
| <p>层层的Listener事件链,能将传入的原始serviceURL通过zkConn发送给zk服务,获取到服务端注册好的url对应的二进制信息。</p> |
| <p>而Notify回调链,则将这串byte[]一步一步解析、加工;以事件的形式向外传递,最终落到directory上的时候,已经是成型的newInvokers了。</p> |
| <p>具体细节不再以源码形式展示,可参照上图查阅源码。</p> |
| </blockquote> |
| <p>至此已经拿到了server端注册好的真实invoker。</p> |
| <p>完成了图(一)中的部分:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code2/p5.png" alt="img"></p> |
| <h5 id="224-构造带有集群策略的clusterinvoker">2.2.4 构造带有集群策略的clusterinvoker</h5> |
| <p>经过上述操作,已经拿到了server端Invokers,放入了directory的cacheinvokers数组里面缓存。</p> |
| <p>后续的操作对应本文2.2.2的第四步,由directory生成带有特性集群策略的invoker</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// (四)new cluster invoker,将directory写入集群,获得具有集群策略的invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cluster <span style="color:#719e07">:=</span> extension.<span style="color:#268bd2">GetCluster</span>(serviceUrl.<span style="color:#268bd2">GetParam</span>(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER)) |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">:=</span> cluster.<span style="color:#268bd2">Join</span>(directory) |
| </span></span><span style="display:flex;"><span><span style="color:#2aa198">123</span> |
| </span></span></code></pre></div><p>Join函数的实现就是如下函数:</p> |
| <p>cluster/cluster_impl/failover_cluster_invokers.go: newFailoverClusterInvoker()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">newFailoverClusterInvoker</span>(directory cluster.Directory) protocol.Invoker { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">&amp;</span>failoverClusterInvoker{ |
| </span></span><span style="display:flex;"><span> baseClusterInvoker: <span style="color:#268bd2">newBaseClusterInvoker</span>(directory), |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span><span style="color:#2aa198">12345</span> |
| </span></span></code></pre></div><p>dubbo-go框架默认选择failover策略,既然返回了一个invoker,我们查看一下failoverClusterInvoker的Invoker方法,看他是如何将集群策略封装到Invoker函数内部的:</p> |
| <p>cluster/cluster_impl/failover_cluster_invokers.go: Invoker()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// Invoker 函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> (invoker <span style="color:#719e07">*</span>failoverClusterInvoker) <span style="color:#268bd2">Invoke</span>(ctx context.Context, invocation protocol.Invocation) protocol.Result { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用List方法拿到directory缓存的所有invokers |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invokers <span style="color:#719e07">:=</span> invoker.directory.<span style="color:#268bd2">List</span>(invocation) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> err <span style="color:#719e07">:=</span> invoker.<span style="color:#268bd2">checkInvokers</span>(invokers, invocation); err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> {<span style="color:#586e75">// 检查是否可以实现调用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> <span style="color:#719e07">&amp;</span>protocol.RPCResult{Err: err} |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 获取来自用户方向传入的 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> methodName <span style="color:#719e07">:=</span> invocation.<span style="color:#268bd2">MethodName</span>() |
| </span></span><span style="display:flex;"><span> retries <span style="color:#719e07">:=</span> <span style="color:#268bd2">getRetries</span>(invokers, methodName) |
| </span></span><span style="display:flex;"><span> loadBalance <span style="color:#719e07">:=</span> <span style="color:#268bd2">getLoadBalance</span>(invokers[<span style="color:#2aa198">0</span>], invocation) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> i <span style="color:#719e07">:=</span> <span style="color:#2aa198">0</span>; i <span style="color:#719e07">&lt;=</span> retries; i<span style="color:#719e07">++</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 重要!这里是集群策略的体现,失败后重试! |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//Reselect before retry to avoid a change of candidate `invokers`. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">//NOTE: if `invokers` changed, then `invoked` also lose accuracy. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> i &gt; <span style="color:#2aa198">0</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> err <span style="color:#719e07">:=</span> invoker.<span style="color:#268bd2">checkWhetherDestroyed</span>(); err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">&amp;</span>protocol.RPCResult{Err: err} |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> invokers = invoker.directory.<span style="color:#268bd2">List</span>(invocation) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> err <span style="color:#719e07">:=</span> invoker.<span style="color:#268bd2">checkInvokers</span>(invokers, invocation); err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">&amp;</span>protocol.RPCResult{Err: err} |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 这里是负载均衡策略的体现!选择特定ivk进行调用。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> ivk <span style="color:#719e07">:=</span> invoker.<span style="color:#268bd2">doSelect</span>(loadBalance, invocation, invokers, invoked) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> ivk <span style="color:#719e07">==</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> invoked = <span style="color:#b58900">append</span>(invoked, ivk) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//DO INVOKE |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> result = ivk.<span style="color:#268bd2">Invoke</span>(ctx, invocation) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> result.<span style="color:#268bd2">Error</span>() <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> providers = <span style="color:#b58900">append</span>(providers, ivk.<span style="color:#268bd2">GetUrl</span>().<span style="color:#268bd2">Key</span>()) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> result |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><blockquote> |
| <p>看了很多Invoke函数的实现,所有类似的Invoker函数都包含两个方向,一个是用户方向的invcation,一个是函数方向的底层invokers。</p> |
| <p>而集群策略的invoke函数本身作为接线员,把invocation一步步解析,根据调用需求和集群策略,选择特定的invoker来执行</p> |
| <p>proxy函数也是这样,一个是用户方向的ins[] reflect.Type, 一个是函数方向的invoker。</p> |
| <p>proxy函数负责将ins转换为invocation,调用对应invoker的invoker函数,实现连通。</p> |
| <p>而出于这样的设计,可以在一步步Invoker封装的过程中,每个Invoker只关心自己负责操作的部分,从而使整个调用栈解耦。</p> |
| <p>妙啊!!!</p> |
| </blockquote> |
| <p>至此,我们理解了failoverClusterInvoker 的Invoke函数实现,也正是和这个集群策略Invoker被返回,接受来自上方的调用。</p> |
| <p>已完成图(一)中的:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code2/p6.png" alt="img"></p> |
| <h5 id="225-在zookeeper上注册当前client">2.2.5 在zookeeper上注册当前client</h5> |
| <p>拿到invokers后,可以回到:</p> |
| <p>config/refrence_config.go: Refer()函数了。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#b58900">len</span>(c.urls) <span style="color:#719e07">==</span> <span style="color:#2aa198">1</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 这一步访问到registry/protocol/protocol.go registryProtocol.Refer |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> c.invoker = extension.<span style="color:#268bd2">GetProtocol</span>(c.urls[<span style="color:#2aa198">0</span>].Protocol).<span style="color:#268bd2">Refer</span>(<span style="color:#719e07">*</span>c.urls[<span style="color:#2aa198">0</span>]) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// (一)拿到了真实的invokers |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 如果有多个注册中心,即有多个invoker,则采取集群策略 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> invokers <span style="color:#719e07">:=</span> <span style="color:#b58900">make</span>([]protocol.Invoker, <span style="color:#2aa198">0</span>, <span style="color:#b58900">len</span>(c.urls)) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> cluster <span style="color:#719e07">:=</span> extension.<span style="color:#268bd2">GetCluster</span>(hitClu) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// If &#39;zone-aware&#39; policy select, the invoker wrap sequence would be: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// ZoneAwareClusterInvoker(StaticDirectory) -&gt; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// FailoverClusterInvoker(RegistryDirectory, routing happens here) -&gt; Invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> c.invoker = cluster.<span style="color:#268bd2">Join</span>(directory.<span style="color:#268bd2">NewStaticDirectory</span>(invokers)) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// (二)create proxy,为函数配置代理 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> c.Async { |
| </span></span><span style="display:flex;"><span> callback <span style="color:#719e07">:=</span> <span style="color:#268bd2">GetCallback</span>(c.id) |
| </span></span><span style="display:flex;"><span> c.pxy = extension.<span style="color:#268bd2">GetProxyFactory</span>(consumerConfig.ProxyFactory).<span style="color:#268bd2">GetAsyncProxy</span>(c.invoker, callback, cfgURL) |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 这里c.invoker已经是目的addr了 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> c.pxy = extension.<span style="color:#268bd2">GetProxyFactory</span>(consumerConfig.ProxyFactory).<span style="color:#268bd2">GetProxy</span>(c.invoker, cfgURL) |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>我们有了可以打通的invokers,但还不能直接调用,因为invoker的入参是invocation,而调用函数使用的是具体的参数列表。需要通过一层proxy来规范入参和出参。</p> |
| <p>接下来新建一个默认proxy,放置在c.proxy内,以供后续使用</p> |
| <p>至此,完成了图(一)中最后的操作</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code2/p7.png" alt="img"></p> |
| <h3 id="23-将调用逻辑以代理函数的形式写入rpc-service">2.3 将调用逻辑以代理函数的形式写入rpc-service</h3> |
| <p>上面完成了config.Refer操作</p> |
| <p>回到config/config_loader.go: loadConsumerConfig()</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code2/p8.png" alt="img"></p> |
| <p>下一个重要的函数是Implement,他完的操作较为简单:旨在使用上面生成的c.proxy代理,链接用户自己定义的rpcService到clusterInvoker的信息传输。</p> |
| <p>函数较长,只选取了重要的部分:</p> |
| <p>common/proxy/proxy.go: Implement()</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// Implement |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// proxy implement |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// In consumer, RPCService like: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// type XxxProvider struct { |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// Yyy func(ctx context.Context, args []interface{}, rsp *Zzz) error |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// } |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// Implement 实现的过程,就是proxy根据函数名和返回值,通过调用invoker 构造出拥有远程调用逻辑的代理函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// 将当前rpc所有可供调用的函数注册到proxy.rpc内 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> (p <span style="color:#719e07">*</span>Proxy) <span style="color:#268bd2">Implement</span>(v common.RPCService) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// makeDubboCallProxy 这是一个构造代理函数,这个函数的返回值是func(in []reflect.Value) []reflect.Value 这样一个函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 这个被返回的函数是请求实现的载体,由他来发起调用获取结果 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> makeDubboCallProxy <span style="color:#719e07">:=</span> <span style="color:#268bd2">func</span>(methodName <span style="color:#dc322f">string</span>, outs []reflect.Type) <span style="color:#268bd2">func</span>(in []reflect.Value) []reflect.Value { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#268bd2">func</span>(in []reflect.Value) []reflect.Value { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 根据methodName和outs的类型,构造这样一个函数,这个函数能将in 输入的value转换为输出的value |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 这个函数具体的实现如下: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 目前拿到了 methodName、所有入参的interface和value,出参数reply |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// (一)根据这些生成一个 rpcinvocation |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> inv = invocation_impl.<span style="color:#268bd2">NewRPCInvocationWithOptions</span>( |
| </span></span><span style="display:flex;"><span> invocation_impl.<span style="color:#268bd2">WithMethodName</span>(methodName), |
| </span></span><span style="display:flex;"><span> invocation_impl.<span style="color:#268bd2">WithArguments</span>(inIArr), |
| </span></span><span style="display:flex;"><span> invocation_impl.<span style="color:#268bd2">WithReply</span>(reply.<span style="color:#268bd2">Interface</span>()), |
| </span></span><span style="display:flex;"><span> invocation_impl.<span style="color:#268bd2">WithCallBack</span>(p.callBack), |
| </span></span><span style="display:flex;"><span> invocation_impl.<span style="color:#268bd2">WithParameterValues</span>(inVArr)) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> k, value <span style="color:#719e07">:=</span> <span style="color:#719e07">range</span> p.attachments { |
| </span></span><span style="display:flex;"><span> inv.<span style="color:#268bd2">SetAttachments</span>(k, value) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// add user setAttachment |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> atm <span style="color:#719e07">:=</span> invCtx.<span style="color:#268bd2">Value</span>(constant.AttachmentKey) <span style="color:#586e75">// 如果传入的ctx里面有attachment,也要写入inv |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> m, ok <span style="color:#719e07">:=</span> atm.(<span style="color:#268bd2">map</span>[<span style="color:#dc322f">string</span>]<span style="color:#dc322f">string</span>); ok { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> k, value <span style="color:#719e07">:=</span> <span style="color:#719e07">range</span> m { |
| </span></span><span style="display:flex;"><span> inv.<span style="color:#268bd2">SetAttachments</span>(k, value) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 至此构造inv完毕 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// (二)触发Invoker 之前已经将cluster_invoker放入proxy,使用Invoke方法,通过getty远程过程调用 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> result <span style="color:#719e07">:=</span> p.invoke.<span style="color:#268bd2">Invoke</span>(invCtx, inv) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 如果有attachment,则加入 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> <span style="color:#b58900">len</span>(result.<span style="color:#268bd2">Attachments</span>()) &gt; <span style="color:#2aa198">0</span> { |
| </span></span><span style="display:flex;"><span> invCtx = context.<span style="color:#268bd2">WithValue</span>(invCtx, constant.AttachmentKey, result.<span style="color:#268bd2">Attachments</span>()) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> numField <span style="color:#719e07">:=</span> valueOfElem.<span style="color:#268bd2">NumField</span>() |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> i <span style="color:#719e07">:=</span> <span style="color:#2aa198">0</span>; i &lt; numField; i<span style="color:#719e07">++</span> { |
| </span></span><span style="display:flex;"><span> t <span style="color:#719e07">:=</span> typeOf.<span style="color:#268bd2">Field</span>(i) |
| </span></span><span style="display:flex;"><span> methodName <span style="color:#719e07">:=</span> t.Tag.<span style="color:#268bd2">Get</span>(<span style="color:#2aa198">&#34;dubbo&#34;</span>) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> methodName <span style="color:#719e07">==</span> <span style="color:#2aa198">&#34;&#34;</span> { |
| </span></span><span style="display:flex;"><span> methodName = t.Name |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> f <span style="color:#719e07">:=</span> valueOfElem.<span style="color:#268bd2">Field</span>(i) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> f.<span style="color:#268bd2">Kind</span>() <span style="color:#719e07">==</span> reflect.Func <span style="color:#719e07">&amp;&amp;</span> f.<span style="color:#268bd2">IsValid</span>() <span style="color:#719e07">&amp;&amp;</span> f.<span style="color:#268bd2">CanSet</span>() { <span style="color:#586e75">// 针对于每个函数 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> outNum <span style="color:#719e07">:=</span> t.Type.<span style="color:#268bd2">NumOut</span>() |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 规定函数输出只能有1/2个 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> outNum <span style="color:#719e07">!=</span> <span style="color:#2aa198">1</span> <span style="color:#719e07">&amp;&amp;</span> outNum <span style="color:#719e07">!=</span> <span style="color:#2aa198">2</span> { |
| </span></span><span style="display:flex;"><span> logger.<span style="color:#268bd2">Warnf</span>(<span style="color:#2aa198">&#34;method %s of mtype %v has wrong number of in out parameters %d; needs exactly 1/2&#34;</span>, |
| </span></span><span style="display:flex;"><span> t.Name, t.Type.<span style="color:#268bd2">String</span>(), outNum) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// The latest return type of the method must be error. |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// 规定最后一个返回值一定是error |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> returnType <span style="color:#719e07">:=</span> t.Type.<span style="color:#268bd2">Out</span>(outNum <span style="color:#719e07">-</span> <span style="color:#2aa198">1</span>); returnType <span style="color:#719e07">!=</span> typError { |
| </span></span><span style="display:flex;"><span> logger.<span style="color:#268bd2">Warnf</span>(<span style="color:#2aa198">&#34;the latest return type %s of method %q is not error&#34;</span>, returnType, t.Name) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">continue</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 获取到所有的出参类型,放到数组里 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">var</span> funcOuts = <span style="color:#b58900">make</span>([]reflect.Type, outNum) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> i <span style="color:#719e07">:=</span> <span style="color:#2aa198">0</span>; i &lt; outNum; i<span style="color:#719e07">++</span> { |
| </span></span><span style="display:flex;"><span> funcOuts[i] = t.Type.<span style="color:#268bd2">Out</span>(i) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// do method proxy here: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// (三)调用make函数,传入函数名和返回值,获得能调用远程的proxy,将这个proxy替换掉原来的函数位置 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> f.<span style="color:#268bd2">Set</span>(reflect.<span style="color:#268bd2">MakeFunc</span>(f.<span style="color:#268bd2">Type</span>(), <span style="color:#268bd2">makeDubboCallProxy</span>(methodName, funcOuts))) |
| </span></span><span style="display:flex;"><span> logger.<span style="color:#268bd2">Debugf</span>(<span style="color:#2aa198">&#34;set method [%s]&#34;</span>, methodName) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>正如之前所说,proxy的作用是将用户定义的函数参数列表,转化为抽象的invocation传入Invoker,进行调用。</p> |
| <p>其中已标明有三处较为重要的地方:</p> |
| <ol> |
| <li>在代理函数中实现由参数列表生成Invocation的逻辑</li> |
| <li>在代理函数实现调用Invoker的逻辑</li> |
| <li>将代理函数替换为原始rpc-service对应函数 |
| 至此,也就解决了一开始的问题: |
| client.go: main()</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span> config.<span style="color:#268bd2">Load</span>() |
| </span></span><span style="display:flex;"><span> user <span style="color:#719e07">:=</span> <span style="color:#719e07">&amp;</span>User{} |
| </span></span><span style="display:flex;"><span> err <span style="color:#719e07">:=</span> userProvider.<span style="color:#268bd2">GetUser</span>(context.<span style="color:#268bd2">TODO</span>(), []<span style="color:#268bd2">interface</span>{}{<span style="color:#2aa198">&#34;A001&#34;</span>}, user) |
| </span></span></code></pre></div><p>这里直接调用用户定义的rpcService的函数GetUser,这里实际调用的是经过重写入的函数代理,所以就能实现远程调用了。</p> |
| <h3 id="3-从client到server的invoker嵌套链--小结">3. 从client到server的invoker嵌套链- 小结</h3> |
| <p>在阅读dubbo-go源码的过程中,我能发现一条清晰的invoker-proxy嵌套链,我希望通过图的形式来展现:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code2/p9.png" alt="img"></p> |
| <blockquote> |
| <p>作者简介 李志信 (GitHubID LaurenceLiZhixin),中山大学软件工程专业在校学生,擅长使用 Java/Go 语言,专注于云原生和微服务等技术方向。</p> |
| </blockquote></description></item><item><title>Blog: Dubbo-go 源码笔记(一)Server 端开启服务过程</title><link>https://dubbo.apache.org/zh-cn/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://dubbo.apache.org/zh-cn/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> |
| <p>随着微服务架构的流行,许多高性能 rpc 框架应运而生,由阿里开源的 dubbo 框架 go 语言版本的 dubbo-go 也成为了众多开发者不错的选择。本文将介绍 dubbo-go 框架的基本使用方法,以及从 export 调用链的角度进行 server 端源码导读,希望能引导读者进一步认识这款框架。</p> |
| <p>当拿到一款框架之后,一种不错的源码阅读方式大致如下:从运行最基础的 helloworld demo 源码开始 —&gt; 再查看配置文件 —&gt; 开启各种依赖服务(比如zk、consul) —&gt; 开启服务端 —&gt; 再到通过 client 调用服务端 —&gt; 打印完整请求日志和回包。调用成功之后,再根据框架的设计模型,从配置文件解析开始,自顶向下递阅读整个框架的调用栈。</p> |
| <p>对于 C/S 模式的 rpc 请求来说,整个调用栈被拆成了 client 和 server 两部分,所以可以分别从 server 端的配置文件解析阅读到 server 端的监听启动,从 client 端的配置文件解析阅读到一次 invoker Call 调用。这样一次完整请求就明晰了起来。</p> |
| <h2 id="运行官网提供的-helloworld-demo">运行官网提供的 helloworld-demo</h2> |
| <p><strong>官方 demo 相关链接</strong>:https://github.com/dubbogo/dubbo-samples/tree/master/golang/helloworld/dubbo</p> |
| <h3 id="1-dubbo-go-27-版本-quickstart">1. dubbo-go 2.7 版本 QuickStart</h3> |
| <h4 id="1开启一个-go-server-服务">1)开启一个 go-server 服务</h4> |
| <ul> |
| <li>将仓库 clone 到本地</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ git clone https://github.com/dubbogo/dubbo-samples.git |
| </span></span></code></pre></div><ul> |
| <li>进入 dubbo 目录</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#b58900">cd</span> dubbo-samples/golang/helloworld/dubbo |
| </span></span></code></pre></div><p>进入目录后可看到四个文件夹,分别支持 go 和 java 的 client 以及 server,我们尝试运行一个 go 的 server。进入 app 子文件夹内,可以看到里面保存了 go 文件。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#b58900">cd</span> go-server/app |
| </span></span></code></pre></div><ul> |
| <li>sample 文件结构</li> |
| </ul> |
| <p>可以在 go-server 里面看到三个文件夹:app、assembly、profiles。</p> |
| <p>其中 app 文件夹下保存 go 源码,assembly 文件夹下保存可选的针对特定环境的 build 脚本,profiles 下保存配置文件。对于 dubbo-go 框架,配置文件非常重要,没有文件将导致服务无法启动。</p> |
| <ul> |
| <li>设置指向配置文件的环境变量</li> |
| </ul> |
| <p>由于 dubbo-go 框架依赖配置文件启动,让框架定位到配置文件的方式就是通过环境变量来找。对于 server 端需要两个必须配置的环境变量:CONF_PROVIDER_FILE_PATH、APP_LOG_CONF_FILE,分别应该指向服务端配置文件、日志配置文件。</p> |
| <p>在 sample 里面,我们可以使用 dev 环境,即 profiles/dev/log.yml 和 profiles/dev/server.yml 两个文件。在 app/ 下,通过命令行中指定好这两个文件:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#b58900">export</span> <span style="color:#268bd2">CONF_PROVIDER_FILE_PATH</span><span style="color:#719e07">=</span><span style="color:#2aa198">&#34;../profiles/dev/server.yml&#34;</span> |
| </span></span><span style="display:flex;"><span>$ <span style="color:#b58900">export</span> <span style="color:#268bd2">APP_LOG_CONF_FILE</span><span style="color:#719e07">=</span><span style="color:#2aa198">&#34;../profiles/dev/log.yml&#34;</span> |
| </span></span></code></pre></div><ul> |
| <li>设置 go 代理并运行服务</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ go run . |
| </span></span></code></pre></div><p>如果提示 timeout,则需要设置 goproxy 代理。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#b58900">export</span> <span style="color:#268bd2">GOPROXY</span><span style="color:#719e07">=</span><span style="color:#2aa198">&#34;http://goproxy.io&#34;</span> |
| </span></span></code></pre></div><p>再运行 go run 即可开启服务。</p> |
| <h4 id="2运行-zookeeper">2)运行 zookeeper</h4> |
| <p>安装 zookeeper,并运行 zkServer, 默认为 2181 端口。</p> |
| <h4 id="3运行-go-client-调用-server-服务">3)运行 go-client 调用 server 服务</h4> |
| <ul> |
| <li>进入 go-client 的源码目录</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#b58900">cd</span> go-client/app |
| </span></span></code></pre></div><ul> |
| <li>同理,在 /app 下配置环境变量</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#b58900">export</span> <span style="color:#268bd2">CONF_CONSUMER_FILE_PATH</span><span style="color:#719e07">=</span><span style="color:#2aa198">&#34;../profiles/dev/client.yml&#34;</span> |
| </span></span><span style="display:flex;"><span>$ <span style="color:#b58900">export</span> <span style="color:#268bd2">APP_LOG_CONF_FILE</span><span style="color:#719e07">=</span><span style="color:#2aa198">&#34;../profiles/dev/log.yml&#34;</span> |
| </span></span></code></pre></div><p>配置 go 代理:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#b58900">export</span> <span style="color:#268bd2">GOPROXY</span><span style="color:#719e07">=</span><span style="color:#2aa198">&#34;http://goproxy.io&#34;</span> |
| </span></span></code></pre></div><ul> |
| <li>运行程序</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ go run . |
| </span></span></code></pre></div><p>即可在日志中找到打印出的请求结果:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>response result: &amp;<span style="color:#719e07">{</span>A001 Alex Stocks <span style="color:#2aa198">18</span> 2020-10-28 14:52:49.131 +0800 CST<span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>同样,在运行的 server 中,也可以在日志中找到打印出的请求:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>req:<span style="color:#719e07">[]</span>interface <span style="color:#719e07">{}{</span><span style="color:#2aa198">&#34;A001&#34;</span><span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span>rsp:main.User<span style="color:#719e07">{</span>Id:<span style="color:#2aa198">&#34;A001&#34;</span>, Name:<span style="color:#2aa198">&#34;Alex Stocks&#34;</span>, Age:18, Time:time.Time<span style="color:#719e07">{</span>...<span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>恭喜!一次基于 dubbo-go 的 rpc 调用成功。</p> |
| <h4 id="4常见问题">4)常见问题</h4> |
| <ul> |
| <li>当日志开始部分出现 profiderInit 和 ConsumerInit 均失败的日志,检查环境变量中配置路径是否正确,配置文件是否正确。</li> |
| <li>当日志中出现 register 失败的情况,一般为向注册中心注册失败,检查注册中心是否开启,检查配置文件中关于 register 的端口是否正确。</li> |
| <li>sample 的默认开启端口为 20000,确保启动前无占用。</li> |
| </ul> |
| <h3 id="2-配置环境变量">2. 配置环境变量</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#b58900">export</span> <span style="color:#268bd2">APP_LOG_CONF_FILE</span><span style="color:#719e07">=</span><span style="color:#2aa198">&#34;../profiles/dev/log.yml&#34;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#b58900">export</span> <span style="color:#268bd2">CONF_CONSUMER_FILE_PATH</span><span style="color:#719e07">=</span><span style="color:#2aa198">&#34;../profiles/dev/client.yml&#34;</span> |
| </span></span></code></pre></div><h3 id="3-服务端源码">3. 服务端源码</h3> |
| <h4 id="1目录结构">1)目录结构</h4> |
| <p>dubbo-go 框架的 example 提供的目录如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p1.png" alt="img"></p> |
| <ul> |
| <li>app/ 文件夹下存放源码,可以自己编写环境变量配置脚本 buliddev.sh</li> |
| <li>assembly/ 文件夹下存放不同平台的构建脚本</li> |
| <li>profiles/ 文件夹下存放不同环境的配置文件</li> |
| <li>target/ 文件夹下存放可执行文件</li> |
| </ul> |
| <h3 id="2关键源码">2)关键源码</h3> |
| <p>源码放置在 app/ 文件夹下,主要包含 server.go 和 user.go 两个文件,顾名思义,server.go 用于使用框架开启服务以及注册传输协议;user.go 则定义了 rpc-service 结构体,以及传输协议的结构。</p> |
| <ul> |
| <li><strong>user.go</strong></li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">init</span>() { |
| </span></span><span style="display:flex;"><span> config.<span style="color:#268bd2">SetProviderService</span>(<span style="color:#b58900">new</span>(UserProvider)) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ------for hessian2------ |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> hessian.<span style="color:#268bd2">RegisterPOJO</span>(<span style="color:#719e07">&amp;</span>User{}) |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">type</span> User <span style="color:#268bd2">struct</span> { |
| </span></span><span style="display:flex;"><span> Id <span style="color:#dc322f">string</span> |
| </span></span><span style="display:flex;"><span> Name <span style="color:#dc322f">string</span> |
| </span></span><span style="display:flex;"><span> Age <span style="color:#dc322f">int32</span> |
| </span></span><span style="display:flex;"><span> Time time.Time |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">type</span> UserProvider <span style="color:#268bd2">struct</span> { |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">func</span> (u <span style="color:#719e07">*</span>UserProvider) <span style="color:#268bd2">GetUser</span>(ctx context.Context, req []<span style="color:#268bd2">interface</span>{}) (<span style="color:#719e07">*</span>User, <span style="color:#dc322f">error</span>) { |
| </span></span></code></pre></div><p>可以看到,user.go 中存在 init 函数,是服务端代码中最先被执行的部分。User 为用户自定义的传输结构体,UserProvider 为用户自定义的 rpc_service;包含一个 rpc 函数,GetUser。当然,用户可以自定义其他的 rpc 功能函数。</p> |
| <p>在 init 函数中,调用 config 的 SetProviderService 函数,将当前 rpc_service 注册在框架 config 上。</p> |
| <p><strong>可以查看 dubbo 官方文档提供的设计图:</strong></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p2.png" alt="img"></p> |
| <p>service 层下面就是 config 层,用户服务会逐层向下注册,最终实现服务端的暴露。</p> |
| <p>rpc-service 注册完毕之后,调用 hessian 接口注册传输结构体 User。</p> |
| <p>至此,init 函数执行完毕。</p> |
| <ul> |
| <li><strong>server.go</strong></li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// they are necessary: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// export CONF_PROVIDER_FILE_PATH=&#34;xxx&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// export APP_LOG_CONF_FILE=&#34;xxx&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> <span style="color:#268bd2">main</span>() { |
| </span></span><span style="display:flex;"><span> hessian.<span style="color:#268bd2">RegisterPOJO</span>(<span style="color:#719e07">&amp;</span>User{}) |
| </span></span><span style="display:flex;"><span> config.<span style="color:#268bd2">Load</span>() |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">initSignal</span>() |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">initSignal</span>() { |
| </span></span><span style="display:flex;"><span> signals <span style="color:#719e07">:=</span> <span style="color:#b58900">make</span>(<span style="color:#268bd2">chan</span> os.Signal, <span style="color:#2aa198">1</span>) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">...</span> |
| </span></span></code></pre></div><p>之后执行 main 函数。</p> |
| <p>main 函数中只进行了两个操作,首先使用 hessian 注册组件将 User 结构体注册(与之前略有重复),从而可以在接下来使用 getty 打解包。</p> |
| <p>之后调用 config.Load 函数,该函数位于框架 config/config_loader.go 内,这个函数是整个框架服务的启动点,<strong>下面会详细讲这个函数内重要的配置处理过程</strong>。执行完 Load() 函数之后,配置文件会读入框架,之后根据配置文件的内容,将注册的 service 实现到配置结构里,再调用 Export 暴露给特定的 registry,进而开启特定的 service 进行对应端口的 tcp 监听,成功启动并且暴露服务。</p> |
| <p>最终开启信号监听 initSignal() 优雅地结束一个服务的启动过程。</p> |
| <h3 id="4-客户端源码">4. 客户端源码</h3> |
| <p>客户端包含 client.go 和 user.go 两个文件,其中 user.go 与服务端完全一致,不再赘述。</p> |
| <ul> |
| <li><strong>client.go</strong></li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// they are necessary: |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// export CONF_CONSUMER_FILE_PATH=&#34;xxx&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// export APP_LOG_CONF_FILE=&#34;xxx&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> <span style="color:#268bd2">main</span>() { |
| </span></span><span style="display:flex;"><span> hessian.<span style="color:#268bd2">RegisterPOJO</span>(<span style="color:#719e07">&amp;</span>User{}) |
| </span></span><span style="display:flex;"><span> config.<span style="color:#268bd2">Load</span>() |
| </span></span><span style="display:flex;"><span> time.<span style="color:#268bd2">Sleep</span>(<span style="color:#2aa198">3e9</span>) |
| </span></span><span style="display:flex;"><span> <span style="color:#b58900">println</span>(<span style="color:#2aa198">&#34;\n\n\nstart to test dubbo&#34;</span>) |
| </span></span><span style="display:flex;"><span> user <span style="color:#719e07">:=</span> <span style="color:#719e07">&amp;</span>User{} |
| </span></span><span style="display:flex;"><span> err <span style="color:#719e07">:=</span> userProvider.<span style="color:#268bd2">GetUser</span>(context.<span style="color:#268bd2">TODO</span>(), []<span style="color:#268bd2">interface</span>{}{<span style="color:#2aa198">&#34;A001&#34;</span>}, user) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#b58900">panic</span>(err) |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#b58900">println</span>(<span style="color:#2aa198">&#34;response result: %v\n&#34;</span>, user) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">initSignal</span>() |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>main 函数和服务端也类似,首先将传输结构注册到 hessian 上,再调用 config.Load() 函数。在下文会介绍,客户端和服务端会根据配置类型执行 config.Load() 中特定的函数 loadConsumerConfig() 和 loadProviderConfig(),从而达到“开启服务”、“调用服务”的目的。</p> |
| <p>加载完配置之后,还是通过实现服务、增加函数 proxy、申请 registry 和 reloadInvoker 指向服务端 ip 等操作,重写了客户端实例 userProvider 的对应函数,这时再通过调用 GetUser 函数,可以直接通过 invoker,调用到已经开启的服务端,实现 rpc 过程。</p> |
| <p>下面会从 server 端和 client 端两个角度,详细讲解服务启动、registry 注册和调用过程。</p> |
| <h3 id="5-自定义配置文件非环境变量方法">5. 自定义配置文件(非环境变量)方法</h3> |
| <h4 id="1服务端自定义配置文件">1)服务端自定义配置文件</h4> |
| <ul> |
| <li>var providerConfigStr = <code>xxxxx</code>// 配置文件内容,可以参考 log 和 client。在这里你可以定义配置文件的获取方式,比如配置中心,本地文件读取。</li> |
| </ul> |
| <blockquote> |
| <p><strong>log 地址</strong>:https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/log.yml</p> |
| <p><strong>client 地址</strong>:https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/client.yml</p> |
| </blockquote> |
| <ul> |
| <li>在 <code>config.Load()</code> 之前设置配置,例如:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">main</span>() { |
| </span></span><span style="display:flex;"><span> hessian.<span style="color:#268bd2">RegisterPOJO</span>(<span style="color:#719e07">&amp;</span>User{}) |
| </span></span><span style="display:flex;"><span> providerConfig <span style="color:#719e07">:=</span> config.ProviderConfig{} |
| </span></span><span style="display:flex;"><span> yaml.<span style="color:#268bd2">Unmarshal</span>([]<span style="color:#b58900">byte</span>(providerConfigStr), <span style="color:#719e07">&amp;</span>providerConfig) |
| </span></span><span style="display:flex;"><span> config.<span style="color:#268bd2">SetProviderConfig</span>(providerConfig) |
| </span></span><span style="display:flex;"><span> defaultServerConfig <span style="color:#719e07">:=</span> dubbo.<span style="color:#268bd2">GetDefaultServerConfig</span>() |
| </span></span><span style="display:flex;"><span> dubbo.<span style="color:#268bd2">SetServerConfig</span>(defaultServerConfig) |
| </span></span><span style="display:flex;"><span> logger.<span style="color:#268bd2">SetLoggerLevel</span>(<span style="color:#2aa198">&#34;warn&#34;</span>) <span style="color:#586e75">// info,warn |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> config.<span style="color:#268bd2">Load</span>() |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">select</span> { |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h4 id="2客户端自定义配置文件">2)客户端自定义配置文件</h4> |
| <ul> |
| <li>var consumerConfigStr = <code>xxxxx</code>// 配置文件内容,可以参考 log 和 clien。在这里你可以定义配置文件的获取方式,比如配置中心,本地文件读取。</li> |
| <li>在 <code>config.Load()</code> 之前设置配置,例如:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">main</span>() { |
| </span></span><span style="display:flex;"><span> p <span style="color:#719e07">:=</span> config.ConsumerConfig{} |
| </span></span><span style="display:flex;"><span> yaml.<span style="color:#268bd2">Unmarshal</span>([]<span style="color:#b58900">byte</span>(consumerConfigStr), <span style="color:#719e07">&amp;</span>p) |
| </span></span><span style="display:flex;"><span> config.<span style="color:#268bd2">SetConsumerConfig</span>(p) |
| </span></span><span style="display:flex;"><span> defaultClientConfig <span style="color:#719e07">:=</span> dubbo.<span style="color:#268bd2">GetDefaultClientConfig</span>() |
| </span></span><span style="display:flex;"><span> dubbo.<span style="color:#268bd2">SetClientConf</span>(defaultClientConfig) |
| </span></span><span style="display:flex;"><span> logger.<span style="color:#268bd2">SetLoggerLevel</span>(<span style="color:#2aa198">&#34;warn&#34;</span>) <span style="color:#586e75">// info,warn |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> config.<span style="color:#268bd2">Load</span>() |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> user <span style="color:#719e07">:=</span> <span style="color:#719e07">&amp;</span>User{} |
| </span></span><span style="display:flex;"><span> err <span style="color:#719e07">:=</span> userProvider.<span style="color:#268bd2">GetUser</span>(context.<span style="color:#268bd2">TODO</span>(), []<span style="color:#268bd2">interface</span>{}{<span style="color:#2aa198">&#34;A001&#34;</span>}, user) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> log.<span style="color:#268bd2">Print</span>(err) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> log.<span style="color:#268bd2">Print</span>(user) |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h2 id="server-端">Server 端</h2> |
| <p>服务暴露过程涉及到多次原始 rpcService 的封装、暴露,网上其他文章的图感觉太过笼统,在此,简要地绘制了一个用户定义服务的数据流图:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p3.png" alt="img"></p> |
| <h3 id="1-加载配置">1. 加载配置</h3> |
| <h4 id="1框架初始化">1)框架初始化</h4> |
| <p>在加载配置之前,框架提供了很多已定义好的协议、工厂等组件,都会在对应模块 init 函数内注册到 extension 模块上,以供接下来配置文件中进行选用。</p> |
| <p>其中重要的有:</p> |
| <ul> |
| <li><strong>默认函数代理工厂</strong>:common/proxy/proxy_factory/default.go</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">init</span>() { |
| </span></span><span style="display:flex;"><span> extension.<span style="color:#268bd2">SetProxyFactory</span>(<span style="color:#2aa198">&#34;default&#34;</span>, NewDefaultProxyFactory) |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>它的作用是将原始 rpc-service 进行封装,形成 proxy_invoker,更易于实现远程 call 调用,详情可见其 invoke 函数。</p> |
| <ul> |
| <li><strong>注册中心注册协议</strong>: |
| registry/protocol/protocol.go</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">init</span>() { |
| </span></span><span style="display:flex;"><span> extension.<span style="color:#268bd2">SetProtocol</span>(<span style="color:#2aa198">&#34;registry&#34;</span>, GetProtocol) |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>它负责将 invoker 暴露给对应注册中心,比如 zk 注册中心。</p> |
| <ul> |
| <li><strong>zookeeper 注册协议</strong>:registry/zookeeper/zookeeper.go</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">init</span>() { |
| </span></span><span style="display:flex;"><span> extension.<span style="color:#268bd2">SetRegistry</span>(<span style="color:#2aa198">&#34;zookeeper&#34;</span>, newZkRegistry) |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>它合并了 base_resiger,负责在服务暴露过程中,将服务注册在 zookeeper 注册器上,从而为调用者提供调用方法。</p> |
| <ul> |
| <li><strong>dubbo 传输协议</strong>:protocol/dubbo/dubbo.go</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">init</span>() { |
| </span></span><span style="display:flex;"><span> extension.<span style="color:#268bd2">SetProtocol</span>(DUBBO, GetProtocol) |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>它负责监听对应端口,将具体的服务暴露,并启动对应的事件 handler,将远程调用的 event 事件传递到 invoker 内部,调用本地 invoker 并获得执行结果返回。</p> |
| <ul> |
| <li><strong>filter 包装调用链协议</strong>:protocol/protocolwrapper/protocol_filter_wrapper.go</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#268bd2">func</span> <span style="color:#268bd2">init</span>() { |
| </span></span><span style="display:flex;"><span> extension.<span style="color:#268bd2">SetProtocol</span>(FILTER, GetProtocol) |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>它负责在服务暴露过程中,将代理 invoker 打包,通过配置好的 filter 形成调用链,并交付给 dubbo 协议进行暴露。</p> |
| <p>上述提前注册好的框架已实现的组件,在整个服务暴露调用链中都会用到,会根据配置取其所需。</p> |
| <h4 id="2配置文件">2)配置文件</h4> |
| <p>服务端需要的重要配置有三个字段:services、protocols、registries。</p> |
| <p>profiles/dev/server.yml:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#268bd2">registries </span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;demoZk&#34;</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protocol</span>: <span style="color:#2aa198">&#34;zookeeper&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">timeout </span>: <span style="color:#2aa198">&#34;3s&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">address</span>: <span style="color:#2aa198">&#34;127.0.0.1:2181&#34;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">services</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;UserProvider&#34;</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75"># 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">registry</span>: <span style="color:#2aa198">&#34;demoZk&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protocol </span>: <span style="color:#2aa198">&#34;dubbo&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75"># 相当于dubbo.xml中的interface</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">interface </span>: <span style="color:#2aa198">&#34;com.ikurento.user.UserProvider&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">loadbalance</span>: <span style="color:#2aa198">&#34;random&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">warmup</span>: <span style="color:#2aa198">&#34;100&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">cluster</span>: <span style="color:#2aa198">&#34;failover&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">methods</span>: |
| </span></span><span style="display:flex;"><span> - <span style="color:#268bd2">name</span>: <span style="color:#2aa198">&#34;GetUser&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">retries</span>: <span style="color:#2aa198">1</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">loadbalance</span>: <span style="color:#2aa198">&#34;random&#34;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">protocols</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo&#34;</span>: |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">name</span>: <span style="color:#2aa198">&#34;dubbo&#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">port</span>: <span style="color:#2aa198">20000</span> |
| </span></span></code></pre></div><p>其中 service 指定了要暴露的 rpc-service 名(&ldquo;UserProvider)、暴露的协议名(&ldquo;dubbo&rdquo;)、注册的协议名(&ldquo;demoZk&rdquo;)、暴露的服务所处的 interface、负载均衡策略、集群失败策略及调用的方法等等。</p> |
| <p>其中,中间服务的协议名需要和 registries 下的 mapkey 对应,暴露的协议名需要和 protocols 下的 mapkey 对应。</p> |
| <p>可以看到上述例子中,使用了 dubbo 作为暴露协议,使用了 zookeeper 作为中间注册协议,并且给定了端口。如果 zk 需要设置用户名和密码,也可以在配置中写好。</p> |
| <h4 id="3配置文件的读入和检查">3)配置文件的读入和检查</h4> |
| <blockquote> |
| <p>config/config_loader.go:: Load()</p> |
| </blockquote> |
| <p>在上述 example 的 main 函数中,有 config.Load() 函数的直接调用,该函数执行细节如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// Load Dubbo Init |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> <span style="color:#268bd2">Load</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init router |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">initRouter</span>() |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init the global event dispatcher |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> extension.<span style="color:#268bd2">SetAndInitGlobalDispatcher</span>(<span style="color:#268bd2">GetBaseConfig</span>().EventDispatcherType) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// start the metadata report if config set |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">if</span> err <span style="color:#719e07">:=</span> <span style="color:#268bd2">startMetadataReport</span>(<span style="color:#268bd2">GetApplicationConfig</span>().MetadataType, <span style="color:#268bd2">GetBaseConfig</span>().MetadataReportConfig); err <span style="color:#719e07">!=</span> <span style="color:#cb4b16">nil</span> { |
| </span></span><span style="display:flex;"><span> logger.<span style="color:#268bd2">Errorf</span>(<span style="color:#2aa198">&#34;Provider starts metadata report error, and the error is {%#v}&#34;</span>, err) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// reference config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">loadConsumerConfig</span>() |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// service config |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">loadProviderConfig</span>() |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// init the shutdown callback |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#268bd2">GracefulShutdownInit</span>() |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>在本文中,我们重点关心 loadConsumerConfig() 和 loadProviderConfig() 两个函数。</p> |
| <p>对于 provider 端,可以看到 loadProviderConfig() 函数代码如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p4.png" alt="img"></p> |
| <p>前半部分是配置的读入和检查,进入 for 循环后,是单个 service 的暴露起始点。</p> |
| <p>前面提到,在配置文件中已经写好了要暴露的 service 的种种信息,比如服务名、interface 名、method 名等等。在图中 for 循环内,会将所有 service 的服务依次实现。</p> |
| <p>for 循环的第一行,根据 key 调用 GetProviderService 函数,拿到注册的 rpcService 实例,这里对应上述提到的 init 函数中,用户手动注册的自己实现的 rpc-service 实例:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p5.png" alt="img"></p> |
| <p>这个对象也就成为了 for 循环中的 rpcService 变量,将这个对象注册通过 Implement 函数写到 sys(ServiceConfig 类型)上,设置好 sys 的 key 和协议组,最终调用了 sys 的 Export 方法。</p> |
| <p>此处对应流程图的部分:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p6.png" alt="img"></p> |
| <p>至此,框架配置结构体已经拿到了所有 service 有关的配置,以及用户定义好的 rpc-service 实例,它触发了 Export 方法,旨在将自己的实例暴露出去。这是 Export 调用链的起始点。</p> |
| <h3 id="2-原始-service-封装入-proxy_invoker">2. 原始 service 封装入 proxy_invoker</h3> |
| <blockquote> |
| <p>config/service_config.go :: Export()</p> |
| </blockquote> |
| <p>接下来进入 ServiceConfig.Export() 函数.</p> |
| <p>这个函数进行了一些细碎的操作,比如为不同的协议分配随机端口,如果指定了多个中心注册协议,则会将服务通过多个中心注册协议的 registryProtocol 暴露出去,我们只关心对于一个注册协议是如何操作的。还有一些操作比如生成调用 url 和注册 url,用于为暴露做准备。</p> |
| <h4 id="1首先通过配置生成对应-registryurl-和-serviceurl">1)首先通过配置生成对应 registryUrl 和 serviceUrl</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p7.png" alt="img"></p> |
| <p>registryUrl 是用来向中心注册组件发起注册请求的,对于 zookeeper 的话,会传入其 ip 和端口号,以及附加的用户名密码等信息。</p> |
| <p>这个 regUrl 目前只存有注册(zk)相关信息,后续会补写入 ServiceIvk,即服务调用相关信息,里面包含了方法名,参数等&hellip;</p> |
| <h4 id="2对于一个注册协议将传入的-rpc-service-实例注册在-commonservicemap">2)对于一个注册协议,将传入的 rpc-service 实例注册在 common.ServiceMap</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p8.png" alt="img"></p> |
| <p>这个 Register 函数将服务实例注册了两次,一次是以 Interface 为 key 写入接口服务组内,一次是以 interface 和 proto 为 key 写入特定的一个唯一的服务。</p> |
| <p>后续会从 common.Map 里面取出来这个实例。</p> |
| <h4 id="3获取默认代理工厂将实例封装入代理-invoker">3)获取默认代理工厂,将实例封装入代理 invoker</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// 拿到一个proxyInvoker,这个invoker的url是传入的regUrl,这个地方将上面注册的service实例封装成了invoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// 这个GetProxyFactory返回的默认是common/proxy/proxy_factory/default.go |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// 这个默认工厂调用GetInvoker获得默认的proxyInvoker,保存了当前注册url |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>invoker <span style="color:#719e07">:=</span> extension.<span style="color:#268bd2">GetProxyFactory</span>(providerConfig.ProxyFactory).<span style="color:#268bd2">GetInvoker</span>(<span style="color:#719e07">*</span>regUrl) |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">// 暴露出来 生成exporter,开启tcp监听 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75">// 这里就该跳到registry/protocol/protocol.go registryProtocol 调用的Export,将当前proxyInvoker导出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span>exporter = c.cacheProtocol.<span style="color:#268bd2">Export</span>(invoker) |
| </span></span></code></pre></div><p>这一步的 GetProxyFactory(&ldquo;default&rdquo;) 方法获取默认代理工厂,通过传入上述构造的 regUrl,将 url 封装入代理 invoker。</p> |
| <p>可以进入 common/proxy/proxy_factory/default.go::ProxyInvoker.Invoke() 函数里,看到对于 common.Map 取用为 svc 的部分,以及关于 svc 对应 Method 的实际调用 Call 的函数如下:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p9.png" alt="img"></p> |
| <p>到这里,上面 GetInvoker(*regUrl) 返回的 invoker 即为 proxy_invoker,它封装好了用户定义的 rpc_service,并将具体的调用逻辑封装入了 Invoke 函数内。</p> |
| <blockquote> |
| <p>为什么使用 Proxy_invoker 来调用?</p> |
| <p>通过这个 proxy_invoke 调用用户的功能函数,调用方式将更加抽象化,可以在代码中看到,通过 ins 和 outs 来定义入参和出参,将整个调用逻辑抽象化为 invocation 结构体,而将具体的函数名的选择、参数向下传递和 reflect 反射过程封装在 invoke 函数内,这样的设计更有利于之后远程调用。个人认为这是 dubbo Invoke 调用链的设计思想。</p> |
| <p>至此,实现了图中对应的部分:</p> |
| </blockquote> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p10.png" alt="img"></p> |
| <h3 id="3-registry-协议在-zkregistry-上暴露上面的-proxy_invoker">3. registry 协议在 zkRegistry 上暴露上面的 proxy_invoker</h3> |
| <p>上面,我们执行到了 exporter = c.cacheProtocol.Export(invoker)。</p> |
| <p>这里的 cacheProtocol 为一层缓存设计,对应到原始的 demo 上,这里是默认实现好的 registryProtocol。</p> |
| <blockquote> |
| <p>registry/protocol/protocol.go:: Export()</p> |
| </blockquote> |
| <p>这个函数内构造了多个 EventListener,非常有 java 的设计感。</p> |
| <p>我们只关心服务暴露的过程,先忽略这些监听器。</p> |
| <h4 id="1获取注册-url-和服务-url">1)获取注册 url 和服务 url</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p11.png" alt="img"></p> |
| <h4 id="2获取注册中心实例-zkregistry">2)获取注册中心实例 zkRegistry</h4> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p12.png" alt="img"></p> |
| <p>一层缓存操作,如果 cache 没有需要从 common 里面重新拿 zkRegistry。</p> |
| <h4 id="3zkregistry-调用-registry-方法在-zookeeper-上注册-dubbopath">3)zkRegistry 调用 Registry 方法,在 zookeeper 上注册 dubboPath</h4> |
| <p>上述拿到了具体的 zkRegistry 实例,该实例的定义在:registry/zookeeper/registry.go。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p13.png" alt="img"></p> |
| <p>该结构体组合了 registry.BaseRegistry 结构,base 结构定义了注册器基础的功能函数,比如 Registry、Subscribe 等,但在这些默认定义的函数内部,还是会调用 facade 层(zkRegistry 层)的具体实现函数,这一设计模型能在保证已有功能函数不需要重复定义的同时,引入外层函数的实现,类似于结构体继承却又复用了代码。这一设计模式值得学习。</p> |
| <p>我们查看上述 registry/protocol/protocol.go:: Export() 函数,直接调用了:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// 1. 通过zk注册器,调用Register()函数,将已有@root@rawurl注册到zk上 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> err <span style="color:#719e07">:=</span> reg.<span style="color:#268bd2">Register</span>(<span style="color:#719e07">*</span>registeredProviderUrl) |
| </span></span></code></pre></div><p>将已有 RegistryUrl 注册到了 zkRegistry 上。</p> |
| <p>这一步调用了 baseRegistry 的 Register 函数,进而调用 zkRegister 的 DoRegister 函数,进而调用:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p14.png" alt="img"></p> |
| <p>在这个函数里,将对应 root 创造一个新的节点。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p15.png" alt="img"></p> |
| <p>并且写入具体 node 信息,node 为 url 经过 encode 的结果,<strong>包含了服务端的调用方式。</strong></p> |
| <p>这部分的代码较为复杂,具体可以看 baseRegistry 的 processURL() 函数:http://t.tb.cn/6Xje4bijnsIDNaSmyPc4Ot。</p> |
| <p>至此,将服务端调用 url 注册到了 zookeeper 上,而客户端如果想获取到这个 url,只需要传入特定的 dubboPath,向 zk 请求即可。目前 client 是可以获取到访问方式了,但服务端的特定服务还没有启动,还没有开启特定协议端口的监听,这也是 registry/protocol/protocol.go:: Export() 函数接下来要做的事情。</p> |
| <h4 id="4proxy_invoker-封装入-wrapped_invoker得到-filter-调用链">4)proxy_invoker 封装入 wrapped_invoker,得到 filter 调用链</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span> <span style="color:#586e75">// invoker封装入warppedInvoker |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> wrappedInvoker <span style="color:#719e07">:=</span> <span style="color:#268bd2">newWrappedInvoker</span>(invoker, providerUrl) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 经过为invoker增加filter调用链,再使用dubbo协议Export,开启service并且返回了Exporter 。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#586e75">// export_1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> cachedExporter = extension.<span style="color:#268bd2">GetProtocol</span>(protocolwrapper.FILTER).<span style="color:#268bd2">Export</span>(wrappedInvoker) |
| </span></span></code></pre></div><p>新建一个 WrappedInvoker,用于之后链式调用。</p> |
| <p>拿到提前实现并注册好的 ProtocolFilterWrapper,调用 Export 方法,进一步暴露。</p> |
| <blockquote> |
| <p>protocol/protocolwrapped/protocol_filter_wrapper.go:Export()</p> |
| </blockquote> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p16.png" alt="img"></p> |
| <blockquote> |
| <p>protocol/protocolwrapped/protocol_filter_wrapper.go:buildInvokerChain</p> |
| </blockquote> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p17.png" alt="img"></p> |
| <p>可见,根据配置的内容,通过链式调用的构造,将 proxy_invoker 层层包裹在调用链的最底部,最终返回一个调用链 invoker。</p> |
| <p>对应图中部分:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p18.png" alt="img"></p> |
| <p>至此,我们已经拿到 filter 调用链,期待将这个 chain 暴露到特定端口,用于相应请求事件。</p> |
| <h4 id="5通过-dubbo-协议暴露-wrapped_invoker">5)通过 dubbo 协议暴露 wrapped_invoker</h4> |
| <blockquote> |
| <p>protocol/protocolwrapped/protocol_filter_wrapper.go:Export()</p> |
| </blockquote> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// 通过dubbo协议Export dubbo_protocol调用的 export_2 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">return</span> pfw.protocol.<span style="color:#268bd2">Export</span>(invoker) |
| </span></span></code></pre></div><p>回到上述 Export 函数的最后一行,调用了 dubboProtocol 的 Export 方法,将上述 chain 真正暴露。</p> |
| <p>该 Export 方法的具体实现在:protocol/dubbo/dubbo_protocol.go: Export()。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p19.png" alt="img"></p> |
| <p>这一函数做了两个事情:构造触发器、启动服务。</p> |
| <ul> |
| <li>将传入的 Invoker 调用 chain 进一步封装,封装成一个 exporter,再将这个 export 放入 map 保存。<strong>注意!这里把 exporter 放入了 SetExporterMap中,在下面服务启动的时候,会以注册事件监听器的形式将这个 exporter 取出!</strong></li> |
| <li>调用 dubboProtocol 的 openServer 方法,开启一个针对特定端口的监听。</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p20.png" alt="img"></p> |
| <p>如上图所示,一个 Session 被传入,开启对应端口的事件监听。</p> |
| <p>至此构造出了 exporter,完成图中部分:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p21.png" alt="img"></p> |
| <h3 id="4-注册触发动作">4. 注册触发动作</h3> |
| <p>上述只是启动了服务,但还没有看到触发事件的细节,点进上面的 s.newSession 可以看到,dubbo 协议为一个 getty 的 session 默认使用了如下配置:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p22.png" alt="img"></p> |
| <p>其中很重要的一个配置是 EventListener,传入的是 dubboServer 的默认 rpcHandler。</p> |
| <blockquote> |
| <p>protocol/dubbo/listener.go:OnMessage()</p> |
| </blockquote> |
| <p>rpcHandler 有一个实现好的 OnMessage 函数,根据 getty 的 API,当 client 调用该端口时,会触发 OnMessage。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#586e75">// OnMessage notified when RPC server session got any message in connection |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#268bd2">func</span> (h <span style="color:#719e07">*</span>RpcServerHandler) <span style="color:#268bd2">OnMessage</span>(session getty.Session, pkg <span style="color:#268bd2">interface</span>{}) { |
| </span></span></code></pre></div><p>这一函数实现了在 getty session 接收到 rpc 调用后的一系列处理:</p> |
| <ul> |
| <li>传入包的解析</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p23.png" alt="img"></p> |
| <ul> |
| <li>根据请求包构造请求 url</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p24.png" alt="img"></p> |
| <ul> |
| <li>拿到对应请求 key,找到要被调用的 exporter</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p25.png" alt="img"></p> |
| <ul> |
| <li>拿到对应的 Invoker</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p26.png" alt="img"></p> |
| <ul> |
| <li>构造 invocation</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p27.png" alt="img"></p> |
| <ul> |
| <li>调用</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p28.png" alt="img"></p> |
| <ul> |
| <li>返回</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/blog/dubbo-go/code1/p29.png" alt="img"></p> |
| <p>整个被调过程一气呵成。实现了从 getty.Session 的调用事件,到经过层层封装的 invoker 的调用。</p> |
| <p>至此,一次 rpc 调用得以正确返回。</p> |
| <h2 id="小结">小结</h2> |
| <ul> |
| <li><strong>关于 Invoker 的层层封装</strong></li> |
| </ul> |
| <p>能把一次调用抽象成一次 invoke;能把一个协议抽象成针对 invoke 的封装;能把针对一次 invoke 所做出的特定改变封装到 invoke 函数内部,可以降低模块之间的耦合性。层层封装逻辑更加清晰。</p> |
| <ul> |
| <li><strong>关于 URL 的抽象</strong></li> |
| </ul> |
| <p>关于 dubbo 的统一化请求对象 URL 的极度抽象是之前没有见过的&hellip; 个人认为这样封装能保证请求参数列表的简化和一致。但在开发的过程中,滥用极度抽象的接口可能造成&hellip; debug 的困难?以及不知道哪些字段是当前已经封装好的,哪些字段是无用的。</p> |
| <ul> |
| <li><strong>关于协议的理解</strong></li> |
| </ul> |
| <p>之前理解的协议还是太过具体化了,而关于 dubbo-go 对于 dubboProtocol 的协议,我认为是基于 getty 的进一步封装,它定义了客户端和服务端,对于 getty 的 session 应该有哪些特定的操作,从而保证主调和被调的协议一致性,而这种保证也是一种协议的体现,是由 dubbo 协议来规范的。</p> |
| <p>如果你有任何疑问,欢迎钉钉扫码加入交流群:钉钉群号 23331795!</p> |
| <blockquote> |
| <p>作者简介 <strong>李志信</strong> (GitHubID LaurenceLiZhixin),中山大学软件工程专业在校学生,擅长使用 Java/Go 语言,专注于云原生和微服务等技术方向</p> |
| </blockquote></description></item><item><title>Blog: Dubbo 中的 URL 统一模型</title><link>https://dubbo.apache.org/zh-cn/blog/2019/10/17/dubbo-%E4%B8%AD%E7%9A%84-url-%E7%BB%9F%E4%B8%80%E6%A8%A1%E5%9E%8B/</link><pubDate>Thu, 17 Oct 2019 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2019/10/17/dubbo-%E4%B8%AD%E7%9A%84-url-%E7%BB%9F%E4%B8%80%E6%A8%A1%E5%9E%8B/</guid><description> |
| <h3 id="定义">定义</h3> |
| <p>在不谈及 dubbo 时,我们大多数人对 URL 这个概念并不会感到陌生。统一资源定位器 (<a href="https://www.ietf.org/rfc/rfc1738.txt">RFC1738</a>――Uniform Resource Locators (URL))应该是最广为人知的一个 RFC 规范,它的定义也非常简单</p> |
| <blockquote> |
| <p>因特网上的可用资源可以用简单字符串来表示,该文档就是描述了这种字符串的语法和语 |
| 义。而这些字符串则被称为:“统一资源定位器”(URL)</p> |
| </blockquote> |
| <p><strong>一个标准的 URL 格式</strong>至多可以包含如下的几个部分</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>protocol://username:password@host:port/path?key=value&amp;key=value |
| </span></span></code></pre></div><p><strong>一些典型 URL</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>http://www.facebook.com/friends?param1=value1&amp;amp;param2=value2 |
| </span></span><span style="display:flex;"><span>https://username:password@10.20.130.230:8080/list?version=1.0.0 |
| </span></span><span style="display:flex;"><span>ftp://username:password@192.168.1.7:21/1/read.txt |
| </span></span></code></pre></div><p>当然,也有一些<strong>不太符合常规的 URL</strong>,也被归类到了 URL 之中</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>192.168.1.3:20880 |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = 192.168.1.3, port = 20880, url path = null |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:///home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = null, url path = home/user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file://home/user1/router.js?type=script&lt;br&gt; |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = home, url path = user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:///D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = file, url host = null, url path = D:/1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>file:/D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span>同上 file:///D:/1/router.js?type=script |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>/home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = null, url path = home/user1/router.js |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>home/user1/router.js?type=script |
| </span></span><span style="display:flex;"><span>url protocol = null, url host = home, url path = user1/router.js |
| </span></span></code></pre></div><h3 id="dubbo-中的-url">Dubbo 中的 URL</h3> |
| <p>在 dubbo 中,也使用了类似的 URL,主要用于在各个扩展点之间传递数据,组成此 URL 对象的具体参数如下:</p> |
| <ul> |
| <li>protocol:一般是 dubbo 中的各种协议 如:dubbo thrift http zk</li> |
| <li>username/password:用户名/密码</li> |
| <li>host/port:主机/端口</li> |
| <li>path:接口名称</li> |
| <li>parameters:参数键值对</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">URL</span><span style="color:#719e07">(</span>String protocol<span style="color:#719e07">,</span> String username<span style="color:#719e07">,</span> String password<span style="color:#719e07">,</span> String host<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> port<span style="color:#719e07">,</span> String path<span style="color:#719e07">,</span> Map<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;</span> parameters<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">((</span>username <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> username<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">==</span> 0<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> password <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> password<span style="color:#719e07">.</span>length<span style="color:#719e07">()</span> <span style="color:#719e07">&gt;</span> 0<span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;Invalid url, password without username!&#34;</span><span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>protocol <span style="color:#719e07">=</span> protocol<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>username <span style="color:#719e07">=</span> username<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>password <span style="color:#719e07">=</span> password<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>host <span style="color:#719e07">=</span> host<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>port <span style="color:#719e07">=</span> <span style="color:#719e07">(</span>port <span style="color:#719e07">&lt;</span> 0 <span style="color:#719e07">?</span> 0 <span style="color:#719e07">:</span> port<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>path <span style="color:#719e07">=</span> path<span style="color:#719e07">;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// trim the beginning &#34;/&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span> <span style="color:#719e07">while</span><span style="color:#719e07">(</span>path <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> path<span style="color:#719e07">.</span>startsWith<span style="color:#719e07">(</span><span style="color:#2aa198">&#34;/&#34;</span><span style="color:#719e07">))</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> path<span style="color:#719e07">.</span>substring<span style="color:#719e07">(</span>1<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> <span style="color:#719e07">(</span>parameters <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span><span style="color:#719e07">)</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> parameters <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> <span style="color:#719e07">else</span> <span style="color:#719e07">{</span> |
| </span></span><span style="display:flex;"><span> parameters <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">,</span> String<span style="color:#719e07">&gt;(</span>parameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span><span style="color:#719e07">.</span>parameters <span style="color:#719e07">=</span> Collections<span style="color:#719e07">.</span>unmodifiableMap<span style="color:#719e07">(</span>parameters<span style="color:#719e07">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">}</span> |
| </span></span></code></pre></div><p>可以看出,dubbo 认为 protocol,username,passwored,host,port,path 是主要的 URL 参数,其他键值对存放在 parameters 之中。</p> |
| <p><strong>一些典型的 Dubbo URL</strong></p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000 |
| </span></span><span style="display:flex;"><span>描述一个 dubbo 协议的服务 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.registry.RegistryService&amp;pid=1214&amp;qos.port=33333&amp;timestamp=1545721981946 |
| </span></span><span style="display:flex;"><span>描述一个 zookeeper 注册中心 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>consumer://30.5.120.217/org.apache.dubbo.demo.DemoService?application=demo-consumer&amp;category=consumers&amp;check=false&amp;dubbo=2.0.2&amp;interface=org.apache.dubbo.demo.DemoService&amp;methods=sayHello&amp;pid=1209&amp;qos.port=33333&amp;side=consumer&amp;timestamp=1545721827784 |
| </span></span><span style="display:flex;"><span>描述一个消费者 |
| </span></span></code></pre></div><p>可以说,任意的一个领域中的一个实现都可以认为是一类 URL,dubbo 使用 URL 来统一描述了元数据,配置信息,贯穿在整个框架之中。</p> |
| <h3 id="url-相关的生命周期">URL 相关的生命周期</h3> |
| <h4 id="rpc调用">RPC调用</h4> |
| <p>从地址发现的视角,URL 代表了一条可用的 provider 实例地址,除了地址信息之外还有相关的配置信息,这些配置信息是层次化的,有 provider 侧指定的配置值、consumer 侧指定的配置值、接口级别的配置值、方法级别的配置值等。这些 URL 配置值将直接影响消费端的 RPC 调用行为。</p> |
| <p>以 timeout 为例,下图显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似:</p> |
| <ul> |
| <li>方法级优先,接口级次之,全局配置再次之。</li> |
| <li>如果级别一样,则消费方优先,提供方次之。</li> |
| </ul> |
| <p>其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。</p> |
| <p><img src="https://dubbo.apache.org/imgs/user/dubbo-config-override.jpg" alt="dubbo-config-override"></p> |
| <p>(建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置)。</p> |
| <p>理论上 ReferenceConfig 中除了<code>interface</code>这一项,其他所有配置项都可以缺省不配置,框架会自动使用ConsumerConfig,ServiceConfig, ProviderConfig等提供的缺省配置。</p> |
| <h4 id="解析服务">解析服务</h4> |
| <p>基于 dubbo.jar 内的 <code>META-INF/spring.handlers</code> 配置,Spring 在遇到 dubbo 名称空间时,会回调 <code>DubboNamespaceHandler</code>。</p> |
| <p>所有 dubbo 的标签,都统一用 <code>DubboBeanDefinitionParser</code> 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。</p> |
| <p>在 <code>ServiceConfig.export()</code> 或 <code>ReferenceConfig.get()</code> 初始化时,将 Bean 对象转换 URL 格式,所有 Bean 属性转成 URL 的参数。</p> |
| <p>然后将 URL 传给协议扩展点,基于扩展点自适应机制,根据 URL 的协议头,进行不同协议的服务暴露或引用。</p> |
| <h4 id="暴露服务">暴露服务</h4> |
| <p><strong>1. 只暴露服务端口:</strong></p> |
| <p>在没有注册中心,直接暴露提供者的情况下,<code>ServiceConfig</code> 解析出的 URL 的格式为:<code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>。</p> |
| <p>基于扩展点自适应机制,通过 URL 的 <code>dubbo://</code> 协议头识别,直接调用 <code>DubboProtocol</code>的 <code>export()</code> 方法,打开服务端口。</p> |
| <p><strong>2. 向注册中心暴露服务:</strong></p> |
| <p>在有注册中心,需要注册提供者地址的情况下,<code>ServiceConfig</code> 解析出的 URL 的格式为: <code>registry://registry-host/org.apache.dubbo.registry.RegistryService?export=URL.encode(&quot;dubbo://service-host/com.foo.FooService?version=1.0.0&quot;)</code>,</p> |
| <p>基于扩展点自适应机制,通过 URL 的 <code>registry://</code> 协议头识别,就会调用 <code>RegistryProtocol</code> 的 <code>export()</code> 方法,将 <code>export</code> 参数中的提供者 URL,先注册到注册中心。</p> |
| <p>再重新传给 <code>Protocol</code> 扩展点进行暴露: <code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>,然后基于扩展点自适应机制,通过提供者 URL 的 <code>dubbo://</code> 协议头识别,就会调用 <code>DubboProtocol</code> 的 <code>export()</code> 方法,打开服务端口。</p> |
| <h4 id="引用服务">引用服务</h4> |
| <p><strong>1. 直连引用服务:</strong></p> |
| <p>在没有注册中心,直连提供者的情况下,<code>ReferenceConfig</code> 解析出的 URL 的格式为:<code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>。</p> |
| <p>基于扩展点自适应机制,通过 URL 的 <code>dubbo://</code> 协议头识别,直接调用 <code>DubboProtocol</code> 的 <code>refer()</code> 方法,返回提供者引用。</p> |
| <p><strong>2. 从注册中心发现引用服务:</strong></p> |
| <p>在有注册中心,通过注册中心发现提供者地址的情况下,<code>ReferenceConfig</code> 解析出的 URL 的格式为:<code>registry://registry-host/org.apache.dubbo.registry.RegistryService?refer=URL.encode(&quot;consumer://consumer-host/com.foo.FooService?version=1.0.0&quot;)</code>。</p> |
| <p>基于扩展点自适应机制,通过 URL 的 <code>registry://</code> 协议头识别,就会调用 <code>RegistryProtocol</code> 的 <code>refer()</code> 方法,基于 <code>refer</code> 参数中的条件,查询提供者 URL,如: <code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>。</p> |
| <p>基于扩展点自适应机制,通过提供者 URL 的 <code>dubbo://</code> 协议头识别,就会调用 <code>DubboProtocol</code> 的 <code>refer()</code> 方法,得到提供者引用。</p> |
| <p>然后 <code>RegistryProtocol</code> 将多个提供者引用,通过 <code>Cluster</code> 扩展点,伪装成单个提供者引用返回。</p> |
| <h3 id="url-统一模型的意义">URL 统一模型的意义</h3> |
| <p>对于 dubbo 中的 URL,有人理解为配置总线,有人理解为统一配置模型,说法虽然不同,但都是在表达一个意思,这样的 URL 在 dubbo 中被当做是<a href="https://dubbo.apache.org/zh-cn/docsv2.7/dev/contract/">公共契约</a>,所有扩展点参数都包含 URL 参数,URL 作为上下文信息贯穿整个扩展点设计体系。</p> |
| <p>在没有 URL 之前,只能以字符串传递参数,不停的解析和拼装,导致相同类型的接口,参数时而 Map, 时而 Parameters 类包装:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>export<span style="color:#719e07">(</span>String url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span>createExporter<span style="color:#719e07">(</span>String host<span style="color:#719e07">,</span> <span style="color:#dc322f">int</span> port<span style="color:#719e07">,</span> Parameters params<span style="color:#719e07">)</span> |
| </span></span></code></pre></div><p>使用 URL 一致性模型:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>export<span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> |
| </span></span><span style="display:flex;"><span>createExporter<span style="color:#719e07">(</span>URL url<span style="color:#719e07">)</span> |
| </span></span></code></pre></div><p>在最新的 dubbo 代码中,我们可以看到大量使用 URL 来进行上下文之间信息的传递,这样的好处是显而易见的:</p> |
| <ol> |
| <li>使得代码编写者和阅读者能够将一系列的参数联系起来,进而形成规范,使得代码易写,易读。</li> |
| <li>可扩展性强,URL 相当于参数的集合(相当于一个 Map),他所表达的含义比单个参数更丰富,当我们在扩展代码时,可以将新的参数追加到 URL 之中,而不需要改变入参,返参的结构。</li> |
| <li>统一模型,它位于 org.apache.dubbo.common 包中,各个扩展模块都可以使用它作为参数的表达形式,简化了概念,降低了代码的理解成本。</li> |
| </ol> |
| <p>如果你能够理解 final 契约和 restful 契约,那我相信你会很好地理解 URL 契约。契约的好处我还是啰嗦一句:大家都这么做,就形成了默契,沟通是一件很麻烦的事,统一 URL 模型可以省去很多沟通成本,这边是 URL 统一模型存在的意义。</p></description></item></channel></rss> |