| <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – Dubbo 3.0.8 源码解析</title><link>https://dubbo.apache.org/zh-cn/blog/java/codeanalysis/3.0.8/</link><description>Recent content in Dubbo 3.0.8 源码解析 on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><atom:link href="https://dubbo.apache.org/zh-cn/blog/java/codeanalysis/3.0.8/index.xml" rel="self" type="application/rss+xml"/><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>(ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker, URL url, URL consumerUrl) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目前存在的扩展类型为RegistryProtocolListener监听器的实现类型MigrationRuleListener </span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>RegistryProtocolListener<span style="color:#719e07">&gt;</span> listeners <span style="color:#719e07">=</span> findRegistryProtocolListeners(url); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isEmpty(listeners)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker; |
| </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">for</span> (RegistryProtocolListener listener : listeners) { |
| </span></span><span style="display:flex;"><span> listener.onRefer(<span style="color:#719e07">this</span>, invoker, consumerUrl, url); |
| </span></span><span style="display:flex;"><span> } |
| </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>该方法尝试加载所有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>(RegistryProtocol registryProtocol, ClusterInvoker<span style="color:#719e07">&lt;?&gt;</span> invoker, URL consumerUrl, URL registryURL) { |
| </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> MigrationRuleHandler<span style="color:#719e07">&lt;?&gt;</span> migrationRuleHandler <span style="color:#719e07">=</span> handlers.computeIfAbsent((MigrationInvoker<span style="color:#719e07">&lt;?&gt;</span>) invoker, _key <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> ((MigrationInvoker<span style="color:#719e07">&lt;?&gt;</span>) invoker).setMigrationRuleListener(<span style="color:#719e07">this</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, consumerUrl); |
| </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">//迁移规则执行 rule是封装了迁移的配置规则的信息对应类型MigrationRule类型,在初始化对象的时候进行了配置初始化</span> |
| </span></span><span style="display:flex;"><span> migrationRuleHandler.doMigrate(rule); |
| </span></span><span style="display:flex;"><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>(MigrationRule rule) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认情况下这个类型是MigrationInvoker</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (migrationInvoker <span style="color:#719e07">instanceof</span> ServiceDiscoveryMigrationInvoker) { |
| </span></span><span style="display:flex;"><span> refreshInvoker(MigrationStep.FORCE_APPLICATION, 1.0f, rule); |
| </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></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">// initial step : APPLICATION_FIRST</span> |
| </span></span><span style="display:flex;"><span> MigrationStep step <span style="color:#719e07">=</span> MigrationStep.APPLICATION_FIRST; |
| </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></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">//获取配置的类型 默认走APPLICATION_FIRST</span> |
| </span></span><span style="display:flex;"><span> step <span style="color:#719e07">=</span> rule.getStep(consumerURL); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//threshold: 决策阈值(默认-1.0)计算与获取</span> |
| </span></span><span style="display:flex;"><span> threshold <span style="color:#719e07">=</span> rule.getThreshold(consumerURL); |
| </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 get step and threshold info from rule: &#34;</span> <span style="color:#719e07">+</span> rule, e); |
| </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:#719e07">if</span> (refreshInvoker(step, threshold, rule)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// refresh success, update rule</span> |
| </span></span><span style="display:flex;"><span> setMigrationRule(rule); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(MigrationStep step, Float threshold, MigrationRule newRule) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException(<span style="color:#2aa198">&#34;Step or threshold of migration rule cannot be null&#34;</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> MigrationStep originStep <span style="color:#719e07">=</span> currentStep; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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>currentThreshold.equals(threshold)) { |
| </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></span><span style="display:flex;"><span> <span style="color:#719e07">switch</span> (step) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> APPLICATION_FIRST: |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认和配置了应用级优先的服务发现则走这里</span> |
| </span></span><span style="display:flex;"><span> migrationInvoker.migrateToApplicationFirstInvoker(newRule); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> FORCE_APPLICATION: |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置了应用级服务发现则走这里</span> |
| </span></span><span style="display:flex;"><span> success <span style="color:#719e07">=</span> migrationInvoker.migrateToForceApplicationInvoker(newRule); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> FORCE_INTERFACE: |
| </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>: |
| </span></span><span style="display:flex;"><span> success <span style="color:#719e07">=</span> migrationInvoker.migrateToForceInterfaceInvoker(newRule); |
| </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">if</span> (success) { |
| </span></span><span style="display:flex;"><span> setCurrentStepAndThreshold(step, threshold); |
| </span></span><span style="display:flex;"><span> logger.info(<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.getDisplayServiceKey()); |
| </span></span><span style="display:flex;"><span> report(step, originStep, <span style="color:#2aa198">&#34;true&#34;</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">// migrate failed, do not save new step and rule</span> |
| </span></span><span style="display:flex;"><span> logger.warn(<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></span><span style="display:flex;"><span> report(step, originStep, <span style="color:#2aa198">&#34;false&#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:#719e07">return</span> success; |
| </span></span><span style="display:flex;"><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:#719e07">return</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><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>(MigrationRule newRule) { |
| </span></span><span style="display:flex;"><span> CountDownLatch latch <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CountDownLatch(0); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新接口级服务发现Invoker </span> |
| </span></span><span style="display:flex;"><span> refreshInterfaceInvoker(latch); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新应用级服务发现Invoker类型对象</span> |
| </span></span><span style="display:flex;"><span> refreshServiceDiscoveryInvoker(latch); |
| </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">// calculation will re-occurred when address notify later</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//计算当前使用应用级还是接口级服务发现的Invoker对象</span> |
| </span></span><span style="display:flex;"><span> calcPreferredInvoker(newRule); |
| </span></span><span style="display:flex;"><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>(CountDownLatch latch) { |
| </span></span><span style="display:flex;"><span> clearListener(invoker); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (needRefresh(invoker)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isDebugEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.debug(<span style="color:#2aa198">&#34;Re-subscribing interface addresses for interface &#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:#719e07">if</span> (invoker <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> invoker.destroy(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> registryProtocol.getInvoker(cluster, registry, type, url); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> setListener(invoker, () <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> latch.countDown(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (reportService.hasReporter()) { |
| </span></span><span style="display:flex;"><span> reportService.reportConsumptionStatus( |
| </span></span><span style="display:flex;"><span> reportService.createConsumptionReport(consumerUrl.getServiceInterface(), consumerUrl.getVersion(), consumerUrl.getGroup(), <span style="color:#2aa198">&#34;interface&#34;</span>)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (step <span style="color:#719e07">==</span> APPLICATION_FIRST) { |
| </span></span><span style="display:flex;"><span> calcPreferredInvoker(rule); |
| </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><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>(CountDownLatch latch) { |
| </span></span><span style="display:flex;"><span> clearListener(serviceDiscoveryInvoker); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (needRefresh(serviceDiscoveryInvoker)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isDebugEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.debug(<span style="color:#2aa198">&#34;Re-subscribing instance addresses, current interface &#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:#719e07">if</span> (serviceDiscoveryInvoker <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> serviceDiscoveryInvoker.destroy(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> serviceDiscoveryInvoker <span style="color:#719e07">=</span> registryProtocol.getServiceDiscoveryInvoker(cluster, registry, type, url); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> setListener(serviceDiscoveryInvoker, () <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> latch.countDown(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (reportService.hasReporter()) { |
| </span></span><span style="display:flex;"><span> reportService.reportConsumptionStatus( |
| </span></span><span style="display:flex;"><span> reportService.createConsumptionReport(consumerUrl.getServiceInterface(), consumerUrl.getVersion(), consumerUrl.getGroup(), <span style="color:#2aa198">&#34;app&#34;</span>)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (step <span style="color:#719e07">==</span> APPLICATION_FIRST) { |
| </span></span><span style="display:flex;"><span> calcPreferredInvoker(rule); |
| </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><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>(MigrationRule migrationRule) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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></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> Set<span style="color:#719e07">&lt;</span>MigrationAddressComparator<span style="color:#719e07">&gt;</span> detectors <span style="color:#719e07">=</span> ScopeModelUtil.getApplicationModel(consumerUrl <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> : consumerUrl.getScopeModel()) |
| </span></span><span style="display:flex;"><span> .getExtensionLoader(MigrationAddressComparator.class).getSupportedExtensionInstances(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(detectors)) { |
| </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">// the real invoker choice in invocation will be affected by promotion</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (detectors.stream().allMatch(comparator <span style="color:#719e07">-&gt;</span> comparator.shouldMigrate(serviceDiscoveryInvoker, invoker, migrationRule))) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.currentAvailableInvoker <span style="color:#719e07">=</span> serviceDiscoveryInvoker; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.currentAvailableInvoker <span style="color:#719e07">=</span> invoker; |
| </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>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></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>(String<span style="color:#719e07">[]</span> args) { |
| </span></span><span style="display:flex;"><span> runWithBootstrap(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">runWithBootstrap</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.setInterface(DemoService.class); |
| </span></span><span style="display:flex;"><span> reference.setGeneric(<span style="color:#2aa198">&#34;true&#34;</span>); |
| </span></span><span style="display:flex;"><span> reference.setProtocol(<span style="color:#2aa198">&#34;&#34;</span>); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap.getInstance(); |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-consumer&#34;</span>); |
| </span></span><span style="display:flex;"><span> applicationConfig.setQosEnable(<span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><span> applicationConfig.setQosPort(<span style="color:#719e07">-</span>1); |
| </span></span><span style="display:flex;"><span> bootstrap.application(applicationConfig) |
| </span></span><span style="display:flex;"><span> .registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://8.131.79.126:2181&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </span></span><span style="display:flex;"><span> .reference(reference) |
| </span></span><span style="display:flex;"><span> .start(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DemoService demoService <span style="color:#719e07">=</span> bootstrap.getCache().get(reference); |
| </span></span><span style="display:flex;"><span> String message <span style="color:#719e07">=</span> demoService.sayHello(<span style="color:#2aa198">&#34;dubbo&#34;</span>); |
| </span></span><span style="display:flex;"><span> System.out.println(message); |
| </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> GenericService genericService <span style="color:#719e07">=</span> (GenericService) demoService; |
| </span></span><span style="display:flex;"><span> Object genericInvokeResult <span style="color:#719e07">=</span> genericService.$invoke(<span style="color:#2aa198">&#34;sayHello&#34;</span>, <span style="color:#719e07">new</span> String<span style="color:#719e07">[]</span>{String.class.getName()}, |
| </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></span><span style="display:flex;"><span> System.out.println(genericInvokeResult); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#268bd2">throws</span> IllegalStateException { |
| </span></span><span style="display:flex;"><span> ...省略掉若干代码 |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> onModuleStarting(); |
| </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> applicationDeployer.initialize(); |
| </span></span><span style="display:flex;"><span> initialize(); |
| </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> exportServices(); |
| </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">// exclude internal module to avoid wait itself</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (moduleModel <span style="color:#719e07">!=</span> moduleModel.getApplicationModel().getInternalModule()) { |
| </span></span><span style="display:flex;"><span> applicationDeployer.prepareInternalModule(); |
| </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">// refer services</span> |
| </span></span><span style="display:flex;"><span> referServices(); |
| </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> startFuture; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//这个是获取配置的所有的ReferenceConfigBase类型对象</span> |
| </span></span><span style="display:flex;"><span> configManager.getReferences().forEach(rc <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> ReferenceConfig<span style="color:#719e07">&lt;?&gt;</span> referenceConfig <span style="color:#719e07">=</span> (ReferenceConfig<span style="color:#719e07">&lt;?&gt;</span>) rc; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>referenceConfig.isRefreshed()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新引用配置</span> |
| </span></span><span style="display:flex;"><span> referenceConfig.refresh(); |
| </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">if</span> (rc.shouldInit()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (referAsync <span style="color:#719e07">||</span> rc.shouldReferAsync()) { |
| </span></span><span style="display:flex;"><span> ExecutorService executor <span style="color:#719e07">=</span> executorRepository.getServiceReferExecutor(); |
| </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.runAsync(() <span style="color:#719e07">-&gt;</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> referenceCache.get(rc); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </span></span><span style="display:flex;"><span> logger.error(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; refer async catch error : &#34;</span> <span style="color:#719e07">+</span> t.getMessage(), t); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, executor); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> asyncReferringFutures.add(future); |
| </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> referenceCache.get(rc); |
| </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> (Throwable t) { |
| </span></span><span style="display:flex;"><span> logger.error(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; refer catch error.&#34;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//出现异常销毁引用配置</span> |
| </span></span><span style="display:flex;"><span> referenceCache.destroy(rc); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> t; |
| </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>在这个代码中我们核心需要关心的就是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:#2aa198">&#34;unchecked&#34;</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>(ReferenceConfigBase<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> rc) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个生成的key规则是这样的 服务分组/服务接口:版本号 详细的代码就不看了</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//例如: group/link.elastic.dubbo.entity.DemoService:1.0</span> |
| </span></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> generator.generateKey(rc); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务类型 如果是泛化调用则这个类型为GenericService</span> |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;</span> type <span style="color:#719e07">=</span> rc.getInterfaceClass(); |
| </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:#dc322f">boolean</span> singleton <span style="color:#719e07">=</span> rc.getSingleton() <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> rc.getSingleton(); |
| </span></span><span style="display:flex;"><span> T proxy <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</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:#719e07">if</span> (singleton) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一般为单例的 这个方法是从缓存中获取</span> |
| </span></span><span style="display:flex;"><span> proxy <span style="color:#719e07">=</span> get(key, (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:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//非单例容易造成内存泄露,无法从缓存中获取</span> |
| </span></span><span style="display:flex;"><span> logger.warn(<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></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:#719e07">if</span> (proxy <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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> List<span style="color:#719e07">&lt;</span>ReferenceConfigBase<span style="color:#719e07">&lt;?&gt;&gt;</span> referencesOfType <span style="color:#719e07">=</span> referenceTypeMap.computeIfAbsent(type, _t <span style="color:#719e07">-&gt;</span> Collections.synchronizedList(<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> referencesOfType.add(rc); |
| </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> List<span style="color:#719e07">&lt;</span>ReferenceConfigBase<span style="color:#719e07">&lt;?&gt;&gt;</span> referenceConfigList <span style="color:#719e07">=</span> referenceKeyMap.computeIfAbsent(key, _k <span style="color:#719e07">-&gt;</span> Collections.synchronizedList(<span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>())); |
| </span></span><span style="display:flex;"><span> referenceConfigList.add(rc); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//开始引用服务</span> |
| </span></span><span style="display:flex;"><span> proxy <span style="color:#719e07">=</span> rc.get(); |
| </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> proxy; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (destroyed) { |
| </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;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></span><span style="display:flex;"><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:#719e07">if</span> (ref <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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">//这个前面已经调用了模块发布器启动过了,这里有这么一行代码是有一定作用的,如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化下面执行逻辑的时候出现问题,这个代码其实就是启动模块进行一些基础配置的初始化操作 比如元数据中心默认配置选择,注册中心默认配置选择这些都是比较重要的</span> |
| </span></span><span style="display:flex;"><span> getScopeModel().getDeployer().start(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (<span style="color:#719e07">this</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ref <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> init(); |
| </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:#719e07">return</span> ref; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化标记变量保证只初始化一次,这里又是加锁🔐又是加标记变量的</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (initialized) { |
| </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> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新配置</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>.isRefreshed()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.refresh(); |
| </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">// init serviceMetadata</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化ServiceMetadata类型对象serviceMetadata 为其设置服务基本属性比如版本号,分组,服务接口名</span> |
| </span></span><span style="display:flex;"><span> initServiceMetadata(consumer); |
| </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> serviceMetadata.setServiceType(getServiceInterfaceClass()); |
| </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> serviceMetadata.setServiceKey(URL.buildKey(interfaceName, group, version)); |
| </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> Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> referenceParameters <span style="color:#719e07">=</span> appendConfig(); |
| </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">//来自本地存储和url参数的初始化映射。 参数转URL配置初始化 Dubbo中喜欢用url作为配置的一种处理方式</span> |
| </span></span><span style="display:flex;"><span> initServiceAppsMapping(referenceParameters); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地内存模块服务存储库</span> |
| </span></span><span style="display:flex;"><span> ModuleServiceRepository repository <span style="color:#719e07">=</span> getScopeModel().getServiceRepository(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ServiceModel和ServiceMetadata在某种程度上是相互重复的。我们将来应该合并它们。</span> |
| </span></span><span style="display:flex;"><span> ServiceDescriptor serviceDescriptor; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CommonConstants.NATIVE_STUB.equals(getProxy())) { |
| </span></span><span style="display:flex;"><span> serviceDescriptor <span style="color:#719e07">=</span> StubSuppliers.getServiceDescriptor(interfaceName); |
| </span></span><span style="display:flex;"><span> repository.registerService(serviceDescriptor); |
| </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> serviceDescriptor <span style="color:#719e07">=</span> repository.registerService(interfaceClass); |
| </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> consumerModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConsumerModel(serviceMetadata.getServiceKey(), proxy, serviceDescriptor, <span style="color:#719e07">this</span>, |
| </span></span><span style="display:flex;"><span> getScopeModel(), serviceMetadata, createAsyncMethodInfo()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地存储库注册消费者模型对象</span> |
| </span></span><span style="display:flex;"><span> repository.registerConsumer(consumerModel); |
| </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> serviceMetadata.getAttachments().putAll(referenceParameters); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务的代理对象 !!!核心代码在这里</span> |
| </span></span><span style="display:flex;"><span> ref <span style="color:#719e07">=</span> createProxy(referenceParameters); |
| </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> serviceMetadata.setTarget(ref); |
| </span></span><span style="display:flex;"><span> serviceMetadata.addAttribute(PROXY_CLASS_REF, ref); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> consumerModel.setProxyObject(ref); |
| </span></span><span style="display:flex;"><span> consumerModel.initMethodModels(); |
| </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> checkInvokerAvailable(); |
| </span></span><span style="display:flex;"><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>(Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> referenceParameters) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//本地引用 这里为false</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (shouldJvmRefer(referenceParameters)) { |
| </span></span><span style="display:flex;"><span> createInvokerForLocal(referenceParameters); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> urls.clear(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isNotEmpty(url)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//url存在则为点对点引用</span> |
| </span></span><span style="display:flex;"><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> parseUrl(referenceParameters); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</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">//这里不是local协议默认这里为空</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从注册表中获取URL并将其聚合。这个其实就是初始化一下注册中心的url配置</span> |
| </span></span><span style="display:flex;"><span> aggregateUrlFromRegistry(referenceParameters); |
| </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">//这个代码非常重要 创建远程引用,创建远程引用调用器</span> |
| </span></span><span style="display:flex;"><span> createInvokerForRemote(); |
| </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">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;Referred dubbo service: [&#34;</span> <span style="color:#719e07">+</span> referenceParameters.get(INTERFACE_KEY) <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;].&#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> (Boolean.parseBoolean(referenceParameters.get(GENERIC_KEY)) <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:#2aa198">&#34; it&#39;s not GenericService reference&#34;</span>)); |
| </span></span><span style="display:flex;"><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(CONSUMER_PROTOCOL, referenceParameters.get(REGISTER_IP_KEY), 0, |
| </span></span><span style="display:flex;"><span> referenceParameters.get(INTERFACE_KEY), referenceParameters); |
| </span></span><span style="display:flex;"><span> consumerUrl <span style="color:#719e07">=</span> consumerUrl.setScopeModel(getScopeModel()); |
| </span></span><span style="display:flex;"><span> consumerUrl <span style="color:#719e07">=</span> consumerUrl.setServiceModel(consumerModel); |
| </span></span><span style="display:flex;"><span> MetadataUtils.publishServiceDefinition(consumerUrl, consumerModel.getServiceModel(), getApplicationModel()); |
| </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:#719e07">return</span> (T) proxyFactory.getProxy(invoker, ProtocolUtils.isGeneric(generic)); |
| </span></span><span style="display:flex;"><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></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:#719e07">if</span> (urls.size() <span style="color:#719e07">==</span> 1) { |
| </span></span><span style="display:flex;"><span> URL curUrl <span style="color:#719e07">=</span> urls.get(0); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个SPI对象是由字节码动态生成的自适应对象Protocol$Adaptie直接看看不到源码,后续可以解析一个字节码生成的类型,这里后续来调用链路即可</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> protocolSPI.refer(interfaceClass, curUrl); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>UrlUtils.isRegistry(curUrl)) { |
| </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.add(invoker); |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> Cluster.getCluster(scopeModel, Cluster.DEFAULT).join(<span style="color:#719e07">new</span> StaticDirectory(curUrl, invokers), <span style="color:#cb4b16">true</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> 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></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (URL url : urls) { |
| </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">// Because this invoker may become available later.</span> |
| </span></span><span style="display:flex;"><span> invokers.add(protocolSPI.refer(interfaceClass, url)); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (UrlUtils.isRegistry(url)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// use last registry url</span> |
| </span></span><span style="display:flex;"><span> registryUrl <span style="color:#719e07">=</span> url; |
| </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">if</span> (registryUrl <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</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">// for multi-subscription scenario, use &#39;zone-aware&#39; policy by default</span> |
| </span></span><span style="display:flex;"><span> String cluster <span style="color:#719e07">=</span> registryUrl.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME); |
| </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">// (RegistryDirectory, routing happens here) -&gt; Invoker</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> Cluster.getCluster(registryUrl.getScopeModel(), cluster, <span style="color:#cb4b16">false</span>).join(<span style="color:#719e07">new</span> StaticDirectory(registryUrl, invokers), <span style="color:#cb4b16">false</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">// not a registry url, must be direct invoke.</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isEmpty(invokers)) { |
| </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;invokers == null&#34;</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> URL curUrl <span style="color:#719e07">=</span> invokers.get(0).getUrl(); |
| </span></span><span style="display:flex;"><span> String cluster <span style="color:#719e07">=</span> curUrl.getParameter(CLUSTER_KEY, Cluster.DEFAULT); |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> Cluster.getCluster(scopeModel, cluster).join(<span style="color:#719e07">new</span> StaticDirectory(curUrl, invokers), <span style="color:#cb4b16">true</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><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:#2aa198">&#34;unchecked&#34;</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>(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, URL url) <span style="color:#268bd2">throws</span> RpcException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个url已经被转换为具体的注册中心协议类型了</span> |
| </span></span><span style="display:flex;"><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> url <span style="color:#719e07">=</span> getRegistryUrl(url); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取用于操作Zookeeper的Registry类型 </span> |
| </span></span><span style="display:flex;"><span> Registry registry <span style="color:#719e07">=</span> getRegistry(url); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (RegistryService.class.equals(type)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> proxyFactory.getInvoker((T) registry, type, 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:#586e75">// group=&#34;a,b&#34; or group=&#34;*&#34;</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> qs <span style="color:#719e07">=</span> (Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span>) url.getAttribute(REFER_KEY); |
| </span></span><span style="display:flex;"><span> String group <span style="color:#719e07">=</span> qs.get(GROUP_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isNotEmpty(group)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> ((COMMA_SPLIT_PATTERN.split(group)).length <span style="color:#719e07">&gt;</span> 1 <span style="color:#719e07">||</span> <span style="color:#2aa198">&#34;*&#34;</span>.equals(group)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> doRefer(Cluster.getCluster(url.getScopeModel(), MergeableCluster.NAME), registry, type, url, qs); |
| </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">//降级容错的逻辑处理对象 类型为Cluster 实际类型为MockClusterWrapper 内部包装的是FailoverCluster</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//后续调用服务失败时候会先失效转移再降级</span> |
| </span></span><span style="display:flex;"><span> Cluster cluster <span style="color:#719e07">=</span> Cluster.getCluster(url.getScopeModel(), qs.get(CLUSTER_KEY)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里才是具体的Invoker对象的创建</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> doRefer(cluster, registry, type, url, qs); |
| </span></span><span style="display:flex;"><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>(Cluster cluster, Registry registry, Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, URL url, Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> parameters) { |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, 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.getAttributes()); |
| </span></span><span style="display:flex;"><span> consumerAttribute.remove(REFER_KEY); |
| </span></span><span style="display:flex;"><span> String p <span style="color:#719e07">=</span> isEmpty(parameters.get(PROTOCOL_KEY)) <span style="color:#719e07">?</span> CONSUMER : parameters.get(PROTOCOL_KEY); |
| </span></span><span style="display:flex;"><span> URL consumerUrl <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceConfigURL ( |
| </span></span><span style="display:flex;"><span> p, |
| </span></span><span style="display:flex;"><span> <span style="color:#cb4b16">null</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#cb4b16">null</span>, |
| </span></span><span style="display:flex;"><span> parameters.get(REGISTER_IP_KEY), |
| </span></span><span style="display:flex;"><span> 0, getPath(parameters, type), |
| </span></span><span style="display:flex;"><span> parameters, |
| </span></span><span style="display:flex;"><span> consumerAttribute |
| </span></span><span style="display:flex;"><span> ); |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url.putAttribute(CONSUMER_URL_KEY, consumerUrl); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重点看这一行 带迁移性质的Invoker对象</span> |
| </span></span><span style="display:flex;"><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">this</span>, cluster, registry, type, url, consumerUrl); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一行回来执行迁移规则创建应用级优先的服务发现Invoker对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> interceptInvoker(migrationInvoker, url, consumerUrl); |
| </span></span><span style="display:flex;"><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>(RegistryProtocol registryProtocol, Cluster cluster, Registry registry, Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, URL url, URL consumerUrl) { |
| </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, cluster, registry, type, url, consumerUrl); |
| </span></span><span style="display:flex;"><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>(ClusterInvoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker, URL url, URL consumerUrl) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取激活的注册协议监听器扩展里面registry.protocol.listener,这里激活的类型为MigrationRuleListener</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>RegistryProtocolListener<span style="color:#719e07">&gt;</span> listeners <span style="color:#719e07">=</span> findRegistryProtocolListeners(url); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isEmpty(listeners)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> invoker; |
| </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">for</span> (RegistryProtocolListener listener : listeners) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里执行MigrationRuleListener类型的onRefer方法</span> |
| </span></span><span style="display:flex;"><span> listener.onRefer(<span style="color:#719e07">this</span>, invoker, consumerUrl, url); |
| </span></span><span style="display:flex;"><span> } |
| </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>该方法尝试加载所有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></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></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>(String name); |
| </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>(String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture.completedFuture(sayHello(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:#719e07">import</span> org.apache.dubbo.rpc.RpcContext; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.Logger; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.LoggerFactory; |
| </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></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></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.getLogger(DemoServiceImpl.class); |
| </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>(String name) { |
| </span></span><span style="display:flex;"><span> logger.info(<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.getServiceContext().getRemoteAddress()); |
| </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.getServiceContext().getLocalAddress(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span>(String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</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><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></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></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.common.constants.CommonConstants; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ApplicationConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.MetadataReportConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ProtocolConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ReferenceConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.RegistryConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.bootstrap.DubboBootstrap; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.rpc.service.GenericService; |
| </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></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>(String<span style="color:#719e07">[]</span> args) { |
| </span></span><span style="display:flex;"><span> runWithBootstrap(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">runWithBootstrap</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.setInterface(DemoService.class); |
| </span></span><span style="display:flex;"><span> reference.setGeneric(<span style="color:#2aa198">&#34;true&#34;</span>); |
| </span></span><span style="display:flex;"><span> reference.setProtocol(<span style="color:#2aa198">&#34;&#34;</span>); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap.getInstance(); |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-consumer&#34;</span>); |
| </span></span><span style="display:flex;"><span> applicationConfig.setQosEnable(<span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><span> applicationConfig.setQosPort(<span style="color:#719e07">-</span>1); |
| </span></span><span style="display:flex;"><span> bootstrap.application(applicationConfig) |
| </span></span><span style="display:flex;"><span> .registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://8.131.79.126:2181&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </span></span><span style="display:flex;"><span> .reference(reference) |
| </span></span><span style="display:flex;"><span> .start(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> DemoService demoService <span style="color:#719e07">=</span> bootstrap.getCache().get(reference); |
| </span></span><span style="display:flex;"><span> String message <span style="color:#719e07">=</span> demoService.sayHello(<span style="color:#2aa198">&#34;dubbo&#34;</span>); |
| </span></span><span style="display:flex;"><span> System.out.println(message); |
| </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> GenericService genericService <span style="color:#719e07">=</span> (GenericService) demoService; |
| </span></span><span style="display:flex;"><span> Object genericInvokeResult <span style="color:#719e07">=</span> genericService.$invoke(<span style="color:#2aa198">&#34;sayHello&#34;</span>, <span style="color:#719e07">new</span> String<span style="color:#719e07">[]</span>{String.class.getName()}, |
| </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></span><span style="display:flex;"><span> System.out.println(genericInvokeResult); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#268bd2">throws</span> IllegalStateException { |
| </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></span><span style="display:flex;"><span> onModuleStarting(); |
| </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> applicationDeployer.initialize(); |
| </span></span><span style="display:flex;"><span> initialize(); |
| </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> exportServices(); |
| </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">// exclude internal module to avoid wait itself</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (moduleModel <span style="color:#719e07">!=</span> moduleModel.getApplicationModel().getInternalModule()) { |
| </span></span><span style="display:flex;"><span> applicationDeployer.prepareInternalModule(); |
| </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">// refer services</span> |
| </span></span><span style="display:flex;"><span> referServices(); |
| </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:#719e07">if</span> (asyncExportingFutures.isEmpty() <span style="color:#719e07">&amp;&amp;</span> asyncReferringFutures.isEmpty()) { |
| </span></span><span style="display:flex;"><span> onModuleStarted(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> .... |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture; |
| </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:#586e75">// export services</span> |
| </span></span><span style="display:flex;"><span> exportServices(); |
| </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></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></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//状态判断是否为启动中如果是则将状态设置为STARTED</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isStarting()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//先修改状态</span> |
| </span></span><span style="display:flex;"><span> setStarted(); |
| </span></span><span style="display:flex;"><span> logger.info(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has started.&#34;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//状态修改成功之后开始通知应用程序发布器模块发布器启动成功了</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer.notifyModuleChanged(moduleModel, DeployState.STARTED); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">finally</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> completeStartFuture(<span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(ModuleModel moduleModel, DeployState state) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据所有模块的状态来判断应用发布器的状态</span> |
| </span></span><span style="display:flex;"><span> checkState(moduleModel, state); |
| </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></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (stateLock) { |
| </span></span><span style="display:flex;"><span> stateLock.notifyAll(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(ModuleModel moduleModel, DeployState moduleState) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在写操作 先加个锁</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (stateLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//非内部模块,并且模块的状态是发布成功了</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>moduleModel.isInternal() <span style="color:#719e07">&amp;&amp;</span> moduleState <span style="color:#719e07">==</span> DeployState.STARTED) { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> prepareApplicationInstance(); |
| </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> DeployState newState <span style="color:#719e07">=</span> calculateState(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">switch</span> (newState) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STARTED: |
| </span></span><span style="display:flex;"><span> onStarted(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STARTING: |
| </span></span><span style="display:flex;"><span> onStarting(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STOPPING: |
| </span></span><span style="display:flex;"><span> onStopping(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> STOPPED: |
| </span></span><span style="display:flex;"><span> onStopped(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> FAILED: |
| </span></span><span style="display:flex;"><span> Throwable error <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> ModuleModel errorModule <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ModuleModel module : applicationModel.getModuleModels()) { |
| </span></span><span style="display:flex;"><span> ModuleDeployer deployer <span style="color:#719e07">=</span> module.getDeployer(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (deployer.isFailed() <span style="color:#719e07">&amp;&amp;</span> deployer.getError() <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> error <span style="color:#719e07">=</span> deployer.getError(); |
| </span></span><span style="display:flex;"><span> errorModule <span style="color:#719e07">=</span> module; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> onFailed(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; found failed module: &#34;</span> <span style="color:#719e07">+</span> errorModule.getDesc(), error); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">case</span> PENDING: |
| </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">// setPending();</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</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><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></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></span><span style="display:flex;"><span> <span style="color:#586e75">//已经注册过应用实例数据了 直接返回 (下面CAS逻辑判断了)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (hasPreparedApplicationInstance.get()) { |
| </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">//注册开关控制默认为true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过将registerConsumer默认设置为“false”来关闭纯使用者进程实例的注册。</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isRegisterConsumerInstance()) { |
| </span></span><span style="display:flex;"><span> exportMetadataService(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (hasPreparedApplicationInstance.compareAndSet(<span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</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> registerServiceInstance(); |
| </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><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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isStarting()) { |
| </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">//这里监听器我们主要关注的类型是ExporterDeployListener类型</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (DeployListener<span style="color:#719e07">&lt;</span>ApplicationModel<span style="color:#719e07">&gt;</span> listener : listeners) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (listener <span style="color:#719e07">instanceof</span> ApplicationDeployListener) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 回调监听器的模块启动成功方法</span> |
| </span></span><span style="display:flex;"><span> ((ApplicationDeployListener) listener).onModuleStarted(applicationModel); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable e) { |
| </span></span><span style="display:flex;"><span> logger.error(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; an exception occurred when handle starting event&#34;</span>, e); |
| </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>前面我们主要关注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>(ApplicationModel applicationModel) { |
| </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">//MetadataServiceDelegation类型为实现提供远程RPC服务以方便元数据信息的查询功能的类型。</span> |
| </span></span><span style="display:flex;"><span> MetadataServiceDelegation metadataService <span style="color:#719e07">=</span> applicationModel.getBeanFactory().getOrRegisterBean(MetadataServiceDelegation.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metadataServiceExporter <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> metadataServiceExporter <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConfigurableMetadataServiceExporter(applicationModel, metadataService); |
| </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">//默认我们是没有配置这个元数据类型的这里元数据类型默认为local 条件是不是remote则开始导出,在前面的博客&lt;&lt;Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&gt;&gt; 中有提到这个配置下面我再说下</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>REMOTE_METADATA_STORAGE_TYPE.equals(getMetadataType(applicationModel))) { |
| </span></span><span style="display:flex;"><span> metadataServiceExporter.export(); |
| </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/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></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据服务配置已经存在或者已经导出或者不可导出情况下是无需导出的</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.serviceConfig <span style="color:#719e07">=</span> buildServiceConfig(); |
| </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></span><span style="display:flex;"><span> serviceConfig.export(); |
| </span></span><span style="display:flex;"><span> metadataService.setMetadataURL(serviceConfig.getExportedUrls().get(0)); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;The MetadataService exports urls : &#34;</span> <span style="color:#719e07">+</span> serviceConfig.getExportedUrls()); |
| </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:#719e07">if</span> (logger.isWarnEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.warn(<span style="color:#2aa198">&#34;The MetadataService has been exported : &#34;</span> <span style="color:#719e07">+</span> serviceConfig.getExportedUrls()); |
| </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">return</span> <span style="color:#719e07">this</span>; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//1 获取当前的应用配置 然后初始化应用配置</span> |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> getApplicationConfig(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建服务配置对象</span> |
| </span></span><span style="display:flex;"><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> serviceConfig.setScopeModel(applicationModel.getInternalModule()); |
| </span></span><span style="display:flex;"><span> serviceConfig.setApplication(applicationConfig); |
| </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> RegistryConfig registryConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;N/A&#34;</span>); |
| </span></span><span style="display:flex;"><span> registryConfig.setId(<span style="color:#2aa198">&#34;internal-metadata-registry&#34;</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> serviceConfig.setRegistry(registryConfig); |
| </span></span><span style="display:flex;"><span> serviceConfig.setRegister(<span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//4 生成协议配置 ,这里会配置一下元数据使用的服务端口号默认使用其他服务的端口20880</span> |
| </span></span><span style="display:flex;"><span> serviceConfig.setProtocol(generateMetadataProtocol()); |
| </span></span><span style="display:flex;"><span> serviceConfig.setInterface(MetadataService.class); |
| </span></span><span style="display:flex;"><span> serviceConfig.setDelay(0); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里也是需要注意的地方服务引用的类型为MetadataServiceDelegation</span> |
| </span></span><span style="display:flex;"><span> serviceConfig.setRef(metadataService); |
| </span></span><span style="display:flex;"><span> serviceConfig.setGroup(applicationConfig.getName()); |
| </span></span><span style="display:flex;"><span> serviceConfig.setVersion(MetadataService.VERSION); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//5 生成方法配置 这里目前提供的服务方法为getAndListenInstanceMetadata方法 后续可以看下这个方法的视线</span> |
| </span></span><span style="display:flex;"><span> serviceConfig.setMethods(generateMethodConfig()); |
| </span></span><span style="display:flex;"><span> serviceConfig.setConnections(1); <span style="color:#586e75">// separate connection</span> |
| </span></span><span style="display:flex;"><span> serviceConfig.setExecutes(100); <span style="color:#586e75">// max tasks running at the same time</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> serviceConfig; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//标记变量设置为true</span> |
| </span></span><span style="display:flex;"><span> registered <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span> ServiceInstanceMetadataUtils.registerMetadataAndInstance(applicationModel); |
| </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;Register instance error&#34;</span>, e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (registered) { |
| </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> asyncMetadataFuture <span style="color:#719e07">=</span> frameworkExecutorRepository.getSharedScheduledExecutor().scheduleWithFixedDelay(() <span style="color:#719e07">-&gt;</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:#719e07">if</span> (applicationModel.isDestroyed()) { |
| </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:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>applicationModel.isDestroyed() <span style="color:#719e07">&amp;&amp;</span> registered) { |
| </span></span><span style="display:flex;"><span> ServiceInstanceMetadataUtils.refreshMetadataAndInstance(applicationModel); |
| </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> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>applicationModel.isDestroyed()) { |
| </span></span><span style="display:flex;"><span> logger.error(<span style="color:#2aa198">&#34;Refresh instance and metadata error&#34;</span>, e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, 0, ConfigurationUtils.get(applicationModel, METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MILLISECONDS); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> LOGGER.info(<span style="color:#2aa198">&#34;Start registering instance address to registry.&#34;</span>); |
| </span></span><span style="display:flex;"><span> RegistryManager registryManager <span style="color:#719e07">=</span> applicationModel.getBeanFactory().getBean(RegistryManager.class); |
| </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></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:#268bd2">throws</span> RuntimeException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一步创建应用的实例信息等待下面注册到注册中心</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.serviceInstance <span style="color:#719e07">=</span> createServiceInstance(<span style="color:#719e07">this</span>.metadataInfo); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isValidInstance(<span style="color:#719e07">this</span>.serviceInstance)) { |
| </span></span><span style="display:flex;"><span> logger.warn(<span style="color:#2aa198">&#34;No valid instance found, stop registering instance address to registry.&#34;</span>); |
| </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></span><span style="display:flex;"><span> <span style="color:#586e75">//是否需要更新</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> revisionUpdated <span style="color:#719e07">=</span> calOrUpdateInstanceRevision(<span style="color:#719e07">this</span>.serviceInstance); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (revisionUpdated) { |
| </span></span><span style="display:flex;"><span> reportMetadata(<span style="color:#719e07">this</span>.metadataInfo); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用的实例信息注册到注册中心之上 ,这个</span> |
| </span></span><span style="display:flex;"><span> doRegister(<span style="color:#719e07">this</span>.serviceInstance); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>.serviceInstance <span style="color:#719e07">=</span> createServiceInstance(<span style="color:#719e07">this</span>.metadataInfo); |
| </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>(MetadataInfo metadataInfo) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里的服务名字为:dubbo-demo-api-provider</span> |
| </span></span><span style="display:flex;"><span> DefaultServiceInstance instance <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DefaultServiceInstance(serviceName, applicationModel); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用服务的元数据 ,可以看下面debug的数据信息</span> |
| </span></span><span style="display:flex;"><span> instance.setServiceMetadata(metadataInfo); |
| </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> setMetadataStorageType(instance, metadataType); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 这个是自定义元数据数据 我们也可以通过实现扩展ServiceInstanceCustomizer来自定义一些元数据</span> |
| </span></span><span style="display:flex;"><span> ServiceInstanceMetadataUtils.customizeInstance(instance, applicationModel); |
| </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><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></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></span><span style="display:flex;"><span>host<span style="color:#719e07">=</span>&#39;192.168.1.169&#39;, |
| </span></span><span style="display:flex;"><span>port<span style="color:#719e07">=</span>20880, |
| </span></span><span style="display:flex;"><span>enabled<span style="color:#719e07">=</span><span style="color:#cb4b16">true</span>, |
| </span></span><span style="display:flex;"><span>healthy<span style="color:#719e07">=</span><span style="color:#cb4b16">true</span>, |
| </span></span><span style="display:flex;"><span> metadata<span style="color:#719e07">=</span>{ |
| </span></span><span style="display:flex;"><span> dubbo.metadata<span style="color:#719e07">-</span>service.url<span style="color:#719e07">-</span>params<span style="color:#719e07">=</span>{<span style="color:#2aa198">&#34;connections&#34;</span>:<span style="color:#2aa198">&#34;1&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;version&#34;</span>:<span style="color:#2aa198">&#34;1.0.0&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo&#34;</span>:<span style="color:#2aa198">&#34;2.0.2&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;release&#34;</span>:<span style="color:#2aa198">&#34;3.0.9&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;side&#34;</span>:<span style="color:#2aa198">&#34;provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;port&#34;</span>:<span style="color:#2aa198">&#34;20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;protocol&#34;</span>:<span style="color:#2aa198">&#34;dubbo&#34;</span> |
| </span></span><span style="display:flex;"><span> }, |
| </span></span><span style="display:flex;"><span> dubbo.endpoints<span style="color:#719e07">=[</span> |
| </span></span><span style="display:flex;"><span> {<span style="color:#2aa198">&#34;port&#34;</span>:20880,<span style="color:#2aa198">&#34;protocol&#34;</span>:<span style="color:#2aa198">&#34;dubbo&#34;</span>}<span style="color:#719e07">]</span>, |
| </span></span><span style="display:flex;"><span> dubbo.metadata.storage<span style="color:#719e07">-</span>type<span style="color:#719e07">=</span>local, |
| </span></span><span style="display:flex;"><span> timestamp<span style="color:#719e07">=</span>1656227493387}} |
| </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>(ServiceInstance instance) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取元数据版本号对应字段dubbo.metadata.revision</span> |
| </span></span><span style="display:flex;"><span> String existingInstanceRevision <span style="color:#719e07">=</span> getExportedServicesRevision(instance); |
| </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> MetadataInfo metadataInfo <span style="color:#719e07">=</span> instance.getServiceMetadata(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//必须在不同线程之间同步计算此实例的状态,如同一实例的修订和修改。此方法的使用仅限于某些点,例如在注册期间。始终尝试使用此选项。改为getRevision()。</span> |
| </span></span><span style="display:flex;"><span> String newRevision <span style="color:#719e07">=</span> metadataInfo.calAndGetRevision(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//版本号发生了变更(元数据发生了变更)版本号是md5元数据信息计算出来HASH验证</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>newRevision.equals(existingInstanceRevision)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//版本号添加到dubbo.metadata.revision字段中</span> |
| </span></span><span style="display:flex;"><span> instance.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, metadataInfo.getRevision()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span>; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">if</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></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> revision; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//应用下没有服务则使用一个空的版本号</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isEmptyMap(services)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.revision <span style="color:#719e07">=</span> EMPTY_REVISION; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> StringBuilder sb <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> StringBuilder(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//app是应用名</span> |
| </span></span><span style="display:flex;"><span> sb.append(app); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Map.Entry<span style="color:#719e07">&lt;</span>String, ServiceInfo<span style="color:#719e07">&gt;</span> entry : <span style="color:#719e07">new</span> TreeMap<span style="color:#719e07">&lt;&gt;</span>(services).entrySet()) { |
| </span></span><span style="display:flex;"><span> sb.append(entry.getValue().toDescString()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> String tempRevision <span style="color:#719e07">=</span> RevisionResolver.calRevision(sb.toString()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>StringUtils.isEquals(<span style="color:#719e07">this</span>.revision, tempRevision)) { |
| </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:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(String.format(<span style="color:#2aa198">&#34;metadata revision changed: %s -&gt; %s, app: %s, services: %d&#34;</span>, <span style="color:#719e07">this</span>.revision, tempRevision, <span style="color:#719e07">this</span>.app, <span style="color:#719e07">this</span>.services.size())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.revision <span style="color:#719e07">=</span> tempRevision; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.rawMetadataInfo <span style="color:#719e07">=</span> JsonUtils.getJson().toJson(<span style="color:#719e07">this</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> revision; |
| </span></span><span style="display:flex;"><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.getMd5(metadata); |
| </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>(MetadataInfo metadataInfo) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//订阅元数据的标识符</span> |
| </span></span><span style="display:flex;"><span> SubscriberMetadataIdentifier identifier <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SubscriberMetadataIdentifier(serviceName, metadataInfo.getRevision()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否远程发布元数据,这里我们是本地注册这个就不会在元数据中心发布这个元数据信息</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> ((DEFAULT_METADATA_STORAGE_TYPE.equals(metadataType) <span style="color:#719e07">&amp;&amp;</span> metadataReport.shouldReportMetadata()) <span style="color:#719e07">||</span> REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) { |
| </span></span><span style="display:flex;"><span> metadataReport.publishAppMetadata(identifier, metadataInfo); |
| </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><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>(ServiceInstance serviceInstance) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//Dubbo实现的ServiceInstance类型对象转 Curator的ServiceInstance</span> |
| </span></span><span style="display:flex;"><span> serviceDiscovery.registerService(build(serviceInstance)); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException(REGISTRY_EXCEPTION, <span style="color:#2aa198">&#34;Failed register instance &#34;</span> <span style="color:#719e07">+</span> serviceInstance.toString(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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> (serialized ServiceInstance) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 2 id <span style="color:#719e07">--&gt;</span> (serialized ServiceInstance) |
| </span></span><span style="display:flex;"><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> (serialized ServiceInstance) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">|</span>__________ instance 2 id <span style="color:#719e07">--&gt;</span> (serialized ServiceInstance) |
| </span></span><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/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.apache.curator.x.discovery.ServiceInstance<span style="color:#719e07">&lt;</span>ZookeeperInstance<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">build</span>(ServiceInstance serviceInstance) { |
| </span></span><span style="display:flex;"><span> ServiceInstanceBuilder builder; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String serviceName <span style="color:#719e07">=</span> serviceInstance.getServiceName(); |
| </span></span><span style="display:flex;"><span> String host <span style="color:#719e07">=</span> serviceInstance.getHost(); |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> port <span style="color:#719e07">=</span> serviceInstance.getPort(); |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> metadata <span style="color:#719e07">=</span> serviceInstance.getSortedMetadata(); |
| </span></span><span style="display:flex;"><span> String id <span style="color:#719e07">=</span> generateId(host, port); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ZookeeperInstance是Dubbo封装的用于存放payload数据 包含服务id,服务名字和元数据</span> |
| </span></span><span style="display:flex;"><span> ZookeeperInstance zookeeperInstance <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ZookeeperInstance(id, serviceName, metadata); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> builder <span style="color:#719e07">=</span> builder() |
| </span></span><span style="display:flex;"><span> .id(id) |
| </span></span><span style="display:flex;"><span> .name(serviceName) |
| </span></span><span style="display:flex;"><span> .address(host) |
| </span></span><span style="display:flex;"><span> .port(port) |
| </span></span><span style="display:flex;"><span> .payload(zookeeperInstance); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RuntimeException(e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> builder.build(); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置</span> |
| </span></span><span style="display:flex;"><span> useRegistryAsMetadataCenterIfNecessary(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...省略掉其他代码防止受到干扰</span> |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//配置缓存中查询元数据配置</span> |
| </span></span><span style="display:flex;"><span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataConfigs <span style="color:#719e07">=</span> configManager.getMetadataConfigs(); |
| </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">//查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> defaultRegistries <span style="color:#719e07">=</span> configManager.getDefaultRegistries(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (defaultRegistries.size() <span style="color:#719e07">&gt;</span> 0) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多注册中心遍历</span> |
| </span></span><span style="display:flex;"><span> defaultRegistries |
| </span></span><span style="display:flex;"><span> .stream() |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)</span> |
| </span></span><span style="display:flex;"><span> .filter(<span style="color:#719e07">this</span>::isUsedRegistryAsMetadataCenter) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心配置映射为元数据中心 映射就是获取需要的配置</span> |
| </span></span><span style="display:flex;"><span> .map(<span style="color:#719e07">this</span>::registryAsMetadataCenter) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将元数据中心配置存储在配置缓存中方便后续使用</span> |
| </span></span><span style="display:flex;"><span> .forEach(metadataReportConfig <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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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.application.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></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>URL<span style="color:#719e07">&gt;</span> registryURLs <span style="color:#719e07">=</span> ConfigValidationUtils.loadRegistries(<span style="color:#719e07">this</span>, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//省略掉后面的代码...</span> |
| </span></span><span style="display:flex;"><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>(AbstractInterfaceConfig interfaceConfig, <span style="color:#dc322f">boolean</span> provider) { |
| </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></span><span style="display:flex;"><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> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> registries <span style="color:#719e07">=</span> interfaceConfig.getRegistries(); |
| </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> genCompatibleRegistries(interfaceConfig.getScopeModel(), registryList, provider); |
| </span></span><span style="display:flex;"><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>(ScopeModel scopeModel, List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryList, <span style="color:#dc322f">boolean</span> provider) { |
| </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.size()); |
| </span></span><span style="display:flex;"><span> registryList.forEach(registryURL <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (provider) { |
| </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> String registerMode; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为了更好理解这里简化掉服务发现注册地址配置的逻辑判断过程仅仅看当前例子提供的值走的逻辑</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">//双注册模式配置查询 对应参数为dubbo.application.register-mode 默认值为all</span> |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> registryURL.getParameter(REGISTER_MODE_KEY, ConfigurationUtils.getCachedDynamicProperty(scopeModel, DUBBO_REGISTER_MODE_DEFAULT_KEY, DEFAULT_REGISTER_MODE_ALL)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果用户配置了一个错误的注册模式配置则只走接口级配置 这里默认值为interface</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isValidRegisterMode(registerMode)) { |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> DEFAULT_REGISTER_MODE_INTERFACE; |
| </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:#719e07">if</span> ((DEFAULT_REGISTER_MODE_INSTANCE.equalsIgnoreCase(registerMode) <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> registryNotExists(registryURL, registryList, SERVICE_REGISTRY_PROTOCOL)) { |
| </span></span><span style="display:flex;"><span> URL serviceDiscoveryRegistryURL <span style="color:#719e07">=</span> URLBuilder.from(registryURL) |
| </span></span><span style="display:flex;"><span> .setProtocol(SERVICE_REGISTRY_PROTOCOL) |
| </span></span><span style="display:flex;"><span> .removeParameter(REGISTRY_TYPE_KEY) |
| </span></span><span style="display:flex;"><span> .build(); |
| </span></span><span style="display:flex;"><span> result.add(serviceDiscoveryRegistryURL); |
| </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:#719e07">if</span> (DEFAULT_REGISTER_MODE_INTERFACE.equalsIgnoreCase(registerMode) <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)) { |
| </span></span><span style="display:flex;"><span> result.add(registryURL); |
| </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">//省略掉若干代码和括号</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></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:#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">//如果url为service-discovery-registry发现则这个实现类型为ServiceDiscoveryRegistry</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry(registryUrl); |
| </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:#268bd2">final</span> URL registeredProviderUrl <span style="color:#719e07">=</span> getUrlToRegistry(providerUrl, registryUrl); |
| </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">//register参数是否 注册数据到注册中心</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> register <span style="color:#719e07">=</span> providerUrl.getParameter(REGISTER_KEY, <span style="color:#cb4b16">true</span>) <span style="color:#719e07">&amp;&amp;</span> registryUrl.getParameter(REGISTER_KEY, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (register) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里有两种情况 接口级注册会将接口级服务提供者数据直接注册到Zookeper上面,服务发现(应用级注册)这里仅仅会将注册数据转换为服务元数据等后面来发布元数据</span> |
| </span></span><span style="display:flex;"><span> register(registry, registeredProviderUrl); |
| </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">// register stated url on provider model</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//向提供者模型注册提供者配置ProviderModel</span> |
| </span></span><span style="display:flex;"><span> registerStatedUrl(registryUrl, registeredProviderUrl, register); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> exporter.setRegisterUrl(registeredProviderUrl); |
| </span></span><span style="display:flex;"><span> exporter.setSubscribeUrl(overrideSubscribeUrl); |
| </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.isServiceDiscovery()) { |
| </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> registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener); |
| </span></span><span style="display:flex;"><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:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry(registryUrl); |
| </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:#268bd2">final</span> URL registryUrl) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为两步先获取注册中心工厂对象</span> |
| </span></span><span style="display:flex;"><span> RegistryFactory registryFactory <span style="color:#719e07">=</span> ScopeModelUtil.getExtensionLoader(RegistryFactory.class, registryUrl.getScopeModel()).getAdaptiveExtension(); |
| </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> registryFactory.getRegistry(registryUrl); |
| </span></span><span style="display:flex;"><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:#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:#268bd2">final</span> URL registryUrl) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为两步先获取注册中心工厂对象</span> |
| </span></span><span style="display:flex;"><span> RegistryFactory registryFactory <span style="color:#719e07">=</span> ScopeModelUtil.getExtensionLoader(RegistryFactory.class, registryUrl.getScopeModel()).getAdaptiveExtension(); |
| </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> registryFactory.getRegistry(registryUrl); |
| </span></span><span style="display:flex;"><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:#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>(URL url) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (registryManager <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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;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></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> Registry defaultNopRegistry <span style="color:#719e07">=</span> registryManager.getDefaultNopRegistryIfDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#cb4b16">null</span> <span style="color:#719e07">!=</span> defaultNopRegistry) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> defaultNopRegistry; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> URLBuilder.from(url) |
| </span></span><span style="display:flex;"><span> .setPath(RegistryService.class.getName()) |
| </span></span><span style="display:flex;"><span> .addParameter(INTERFACE_KEY, RegistryService.class.getName()) |
| </span></span><span style="display:flex;"><span> .removeParameter(TIMESTAMP_KEY) |
| </span></span><span style="display:flex;"><span> .removeAttribute(EXPORT_KEY) |
| </span></span><span style="display:flex;"><span> .removeAttribute(REFER_KEY) |
| </span></span><span style="display:flex;"><span> .build(); |
| </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> String key <span style="color:#719e07">=</span> createRegistryCacheKey(url); |
| </span></span><span style="display:flex;"><span> Registry registry <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</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:#dc322f">boolean</span> check <span style="color:#719e07">=</span> url.getParameter(CHECK_KEY, <span style="color:#cb4b16">true</span>) <span style="color:#719e07">&amp;&amp;</span> url.getPort() <span style="color:#719e07">!=</span> 0; |
| </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></span><span style="display:flex;"><span> registryManager.getRegistryLock().lock(); |
| </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">// double check</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// fix https://github.com/apache/dubbo/issues/7265.</span> |
| </span></span><span style="display:flex;"><span> defaultNopRegistry <span style="color:#719e07">=</span> registryManager.getDefaultNopRegistryIfDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#cb4b16">null</span> <span style="color:#719e07">!=</span> defaultNopRegistry) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> defaultNopRegistry; |
| </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> registry <span style="color:#719e07">=</span> registryManager.getRegistry(key); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (registry <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> registry; |
| </span></span><span style="display:flex;"><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">//使用url创建注册中心操作的逻辑</span> |
| </span></span><span style="display:flex;"><span> registry <span style="color:#719e07">=</span> createRegistry(url); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check配置检查</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (check) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RuntimeException(<span style="color:#2aa198">&#34;Can not create registry &#34;</span> <span style="color:#719e07">+</span> url, e); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> LOGGER.warn(<span style="color:#2aa198">&#34;Failed to obtain or create registry &#34;</span>, e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">finally</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Release the lock</span> |
| </span></span><span style="display:flex;"><span> registryManager.getRegistryLock().unlock(); |
| </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">if</span> (check <span style="color:#719e07">&amp;&amp;</span> registry <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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;Can not create registry &#34;</span> <span style="color:#719e07">+</span> url); |
| </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:#719e07">if</span> (registry <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> registryManager.putRegistry(key, registry); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> registry; |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </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:#719e07">if</span> (UrlUtils.hasServiceDiscoveryRegistryProtocol(url)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//切换下协议:将服务发现协议切换为配置的注册中心协议这里是Zookeeper如下:</span> |
| </span></span><span style="display:flex;"><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> String protocol <span style="color:#719e07">=</span> url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY); |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url.setProtocol(protocol).removeParameter(REGISTRY_KEY); |
| </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:#719e07">return</span> <span style="color:#719e07">new</span> ServiceDiscoveryRegistry(url, applicationModel); |
| </span></span><span style="display:flex;"><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>(URL registryURL, ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(registryURL); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据url创建一个服务发现对象类型为ServiceDiscovery</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.serviceDiscovery <span style="color:#719e07">=</span> createServiceDiscovery(registryURL); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个类型为是serviceNameMapping类型是MetadataServiceNameMapping类型</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.serviceNameMapping <span style="color:#719e07">=</span> (AbstractServiceNameMapping) ServiceNameMapping.getDefaultExtension(registryURL.getScopeModel()); |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.applicationModel <span style="color:#719e07">=</span> applicationModel; |
| </span></span><span style="display:flex;"><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>(URL registryURL) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getServiceDiscovery(registryURL.addParameter(INTERFACE_KEY, ServiceDiscovery.class.getName()) |
| </span></span><span style="display:flex;"><span> .removeParameter(REGISTRY_TYPE_KEY)); |
| </span></span><span style="display:flex;"><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>(URL registryURL) { |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//服务发现工厂对象的获取这里是ServiceDiscoveryFactory类型,</span> |
| </span></span><span style="display:flex;"><span> ServiceDiscoveryFactory factory <span style="color:#719e07">=</span> getExtension(registryURL); |
| </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> factory.getServiceDiscovery(registryURL); |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(url); |
| </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:#719e07">this</span>.retryPeriod <span style="color:#719e07">=</span> url.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD); |
| </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">//因为重试任务不会太多。128个刻度就足够了。Dubbo封装的时间轮用于高效率的重试,这个在Kafka也自定义实现了后续可以单独来看看</span> |
| </span></span><span style="display:flex;"><span> retryTimer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashedWheelTimer(<span style="color:#719e07">new</span> NamedThreadFactory(<span style="color:#2aa198">&#34;DubboRegistryRetryTimer&#34;</span>, <span style="color:#cb4b16">true</span>), retryPeriod, TimeUnit.MILLISECONDS, 128); |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </span></span><span style="display:flex;"><span> setUrl(url); |
| </span></span><span style="display:flex;"><span> registryManager <span style="color:#719e07">=</span> url.getOrDefaultApplicationModel().getBeanFactory().getBean(RegistryManager.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否本地缓存默认为true</span> |
| </span></span><span style="display:flex;"><span> localCacheEnabled <span style="color:#719e07">=</span> url.getParameter(REGISTRY_LOCAL_FILE_CACHE_ENABLED, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> registryCacheExecutor <span style="color:#719e07">=</span> url.getOrDefaultFrameworkModel().getBeanFactory() |
| </span></span><span style="display:flex;"><span> .getBean(FrameworkExecutorRepository.class).getSharedExecutor(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (localCacheEnabled) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Start file save timer 是否同步缓存默认为false</span> |
| </span></span><span style="display:flex;"><span> syncSaveFile <span style="color:#719e07">=</span> url.getParameter(REGISTRY_FILESAVE_SYNC_KEY, <span style="color:#cb4b16">false</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> String defaultFilename <span style="color:#719e07">=</span> System.getProperty(USER_HOME) <span style="color:#719e07">+</span> DUBBO_REGISTRY <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> url.getApplication() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> url.getAddress().replaceAll(<span style="color:#2aa198">&#34;:&#34;</span>, <span style="color:#2aa198">&#34;-&#34;</span>) <span style="color:#719e07">+</span> CACHE; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//未指定缓存的文件名字则用默认的文件名字</span> |
| </span></span><span style="display:flex;"><span> String filename <span style="color:#719e07">=</span> url.getParameter(FILE_KEY, defaultFilename); |
| </span></span><span style="display:flex;"><span> File file <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//父目录创建,保证目录存在</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ConfigUtils.isNotEmpty(filename)) { |
| </span></span><span style="display:flex;"><span> file <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File(filename); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>file.exists() <span style="color:#719e07">&amp;&amp;</span> file.getParentFile() <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>file.getParentFile().exists()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>file.getParentFile().mkdirs()) { |
| </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;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.getParentFile() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;!&#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:#719e07">this</span>.file <span style="color:#719e07">=</span> file; |
| </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">// 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></span><span style="display:flex;"><span> loadProperties(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//变更推送</span> |
| </span></span><span style="display:flex;"><span> notify(url.getBackupUrls()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(Registry registry, URL registeredProviderUrl) { |
| </span></span><span style="display:flex;"><span> registry.register(registeredProviderUrl); |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//逻辑判断比如只有side为提供者时候才能注册</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>shouldRegister(url)) { <span style="color:#586e75">// Should Not Register</span> |
| </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> doRegister(url); |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </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> url <span style="color:#719e07">=</span> addRegistryClusterKey(url); |
| </span></span><span style="display:flex;"><span> serviceDiscovery.register(url); |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//metadaInfo类型为MetadataInfo类型,用来操作元数据的</span> |
| </span></span><span style="display:flex;"><span> metadataInfo.addService(url); |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </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">//元数据参数过滤器扩展获取:MetadataParamsFilter</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.loader <span style="color:#719e07">=</span> url.getOrDefaultApplicationModel().getExtensionLoader(MetadataParamsFilter.class); |
| </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> List<span style="color:#719e07">&lt;</span>MetadataParamsFilter<span style="color:#719e07">&gt;</span> filters <span style="color:#719e07">=</span> loader.getActivateExtension(url, <span style="color:#2aa198">&#34;params-filter&#34;</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></span><span style="display:flex;"><span> ServiceInfo serviceInfo <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceInfo(url, filters); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.services.put(serviceInfo.getMatchKey(), serviceInfo); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// extract common instance level params</span> |
| </span></span><span style="display:flex;"><span> extractInstanceParams(url, filters); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (exportedServiceURLs <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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></span><span style="display:flex;"><span> addURL(exportedServiceURLs, url); |
| </span></span><span style="display:flex;"><span> updated <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><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:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry(registryUrl); |
| </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(providerUrl, registryUrl); |
| </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:#dc322f">boolean</span> register <span style="color:#719e07">=</span> providerUrl.getParameter(REGISTER_KEY, <span style="color:#cb4b16">true</span>) <span style="color:#719e07">&amp;&amp;</span> registryUrl.getParameter(REGISTER_KEY, <span style="color:#cb4b16">true</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:#719e07">if</span> (register) { |
| </span></span><span style="display:flex;"><span> register(registry, registeredProviderUrl); |
| </span></span><span style="display:flex;"><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.elastic.dubbo.entity.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.168.1.9<span style="color:#719e07">%</span>3A20880<span style="color:#719e07">%</span>2Flink.elastic.dubbo.entity.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.0.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.elastic.dubbo.entity.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.0.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>(URL registryURL) { |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//服务发现工厂对象的获取这里是ServiceDiscoveryFactory类型,这里对应ZookeeperServiceDiscoveryFactory</span> |
| </span></span><span style="display:flex;"><span> ServiceDiscoveryFactory factory <span style="color:#719e07">=</span> getExtension(registryURL); |
| </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> factory.getServiceDiscovery(registryURL); |
| </span></span><span style="display:flex;"><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>(URL registryURL) { |
| </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></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> registryURL.toServiceStringWithoutResolving(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> discoveries.computeIfAbsent(key, k <span style="color:#719e07">-&gt;</span> createDiscovery(registryURL)); |
| </span></span><span style="display:flex;"><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>(URL registryURL) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ZookeeperServiceDiscovery(applicationModel, registryURL); |
| </span></span><span style="display:flex;"><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>(ApplicationModel applicationModel, URL registryURL) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//先调用父类AbstractServiceDiscovery 模板类构造器</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(applicationModel, registryURL); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</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:#719e07">this</span>.curatorFramework <span style="color:#719e07">=</span> buildCuratorFramework(registryURL); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用级服务发现的根路径 值为/services 这个可以在Zookeeper上面看到</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.rootPath <span style="color:#719e07">=</span> ROOT_PATH.getParameterValue(registryURL); |
| </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:#719e07">this</span>.serviceDiscovery <span style="color:#719e07">=</span> buildServiceDiscovery(curatorFramework, rootPath); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动服务发现</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.serviceDiscovery.start(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </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;Create zookeeper service discovery failed.&#34;</span>, e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;name&#34;</span> : <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;id&#34;</span> : <span style="color:#2aa198">&#34;192.168.1.9:20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;address&#34;</span> : <span style="color:#2aa198">&#34;192.168.1.9&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;port&#34;</span> : 20880, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;sslPort&#34;</span> : <span style="color:#cb4b16">null</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;payload&#34;</span> : { |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;@class&#34;</span> : <span style="color:#2aa198">&#34;org.apache.dubbo.registry.zookeeper.ZookeeperInstance&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;id&#34;</span> : <span style="color:#2aa198">&#34;192.168.1.9:20880&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;name&#34;</span> : <span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;metadata&#34;</span> : { |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#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:#2aa198">&#34;dubbo.metadata-service.url-params&#34;</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></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;dubbo.metadata.revision&#34;</span> : <span style="color:#2aa198">&#34;a662fd2213a8a49dc6ff43a4c2ae7b9e&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#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:#2aa198">&#34;timestamp&#34;</span> : <span style="color:#2aa198">&#34;1654916298616&#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:#2aa198">&#34;registrationTimeUTC&#34;</span> : 1654917265499, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;serviceType&#34;</span> : <span style="color:#2aa198">&#34;DYNAMIC&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;uriSpec&#34;</span> : <span style="color:#cb4b16">null</span> |
| </span></span><span style="display:flex;"><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>(ApplicationModel applicationModel, URL registryURL) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用重载的构造器</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>(applicationModel.getApplicationName(), registryURL); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.applicationModel <span style="color:#719e07">=</span> applicationModel; |
| </span></span><span style="display:flex;"><span> MetadataReportInstance metadataReportInstance <span style="color:#719e07">=</span> applicationModel.getBeanFactory().getBean(MetadataReportInstance.class); |
| </span></span><span style="display:flex;"><span> metadataType <span style="color:#719e07">=</span> metadataReportInstance.getMetadataType(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.metadataReport <span style="color:#719e07">=</span> metadataReportInstance.getMetadataReport(registryURL.getParameter(REGISTRY_CLUSTER_KEY)); |
| </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></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>(String serviceName, URL registryURL) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.applicationModel <span style="color:#719e07">=</span> ApplicationModel.defaultModel(); |
| </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:#719e07">this</span>.registryURL <span style="color:#719e07">=</span> registryURL; |
| </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:#719e07">this</span>.serviceName <span style="color:#719e07">=</span> serviceName; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//MetadataInfo 用来封装元数据信息</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.metadataInfo <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetadataInfo(serviceName); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个是元数据缓存信息管理的类型 缓存文件使用LRU策略 感兴趣的可以详细看看</span> |
| </span></span><span style="display:flex;"><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:#719e07">this</span>.metaCacheManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetaCacheManager(getCacheNameSuffix(), |
| </span></span><span style="display:flex;"><span> applicationModel.getFrameworkModel().getBeanFactory() |
| </span></span><span style="display:flex;"><span> .getBean(FrameworkExecutorRepository.class).getCacheRefreshingScheduledExecutor()); |
| </span></span><span style="display:flex;"><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>.serviceNameMapping <span style="color:#719e07">=</span> (AbstractServiceNameMapping) ServiceNameMapping.getDefaultExtension(registryURL.getScopeModel()); |
| </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>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(applicationModel); |
| </span></span><span style="display:flex;"><span> metadataReportInstance <span style="color:#719e07">=</span> applicationModel.getBeanFactory().getBean(MetadataReportInstance.class); |
| </span></span><span style="display:flex;"><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>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.applicationModel <span style="color:#719e07">=</span> applicationModel; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//LRU缓存保存服务映射数据</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.mappingCacheManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MappingCacheManager(<span style="color:#2aa198">&#34;&#34;</span>, |
| </span></span><span style="display:flex;"><span> applicationModel.getFrameworkModel().getBeanFactory() |
| </span></span><span style="display:flex;"><span> .getBean(FrameworkExecutorRepository.class).getCacheRefreshingScheduledExecutor()); |
| </span></span><span style="display:flex;"><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>(URL url, List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs) { |
| </span></span><span style="display:flex;"><span> String scope <span style="color:#719e07">=</span> url.getParameter(SCOPE_KEY); |
| </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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>SCOPE_LOCAL.equalsIgnoreCase(scope)) { |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> exportRemote(url, registryURLs); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isGeneric(generic) <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>getScopeModel().isInternal()) { |
| </span></span><span style="display:flex;"><span> MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel()); |
| </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">this</span>.urls.add(url); |
| </span></span><span style="display:flex;"><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>(URL url, ServiceDescriptor serviceDescriptor, ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//查询是否存在元数据存储对象 对应接口MetadataReport 这里对应实现类 ZookeeperMetadataReport</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (getMetadataReports(applicationModel).size() <span style="color:#719e07">==</span> 0) { |
| </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></span><span style="display:flex;"><span> logger.warn(msg); |
| </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> String side <span style="color:#719e07">=</span> url.getSide(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务提供者走这个逻辑</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (PROVIDER_SIDE.equalsIgnoreCase(side)) { |
| </span></span><span style="display:flex;"><span> String serviceKey <span style="color:#719e07">=</span> url.getServiceKey(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取当前服务元数据信息</span> |
| </span></span><span style="display:flex;"><span> FullServiceDefinition serviceDefinition <span style="color:#719e07">=</span> serviceDescriptor.getFullServiceDefinition(serviceKey); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isNotEmpty(serviceKey) <span style="color:#719e07">&amp;&amp;</span> serviceDefinition <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> serviceDefinition.setParameters(url.getParameters()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Map.Entry<span style="color:#719e07">&lt;</span>String, MetadataReport<span style="color:#719e07">&gt;</span> entry : getMetadataReports(applicationModel).entrySet()) { |
| </span></span><span style="display:flex;"><span> MetadataReport metadataReport <span style="color:#719e07">=</span> entry.getValue(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>metadataReport.shouldReportDefinition()) { |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;Report of service definition is disabled for &#34;</span> <span style="color:#719e07">+</span> entry.getKey()); |
| </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">//存储服务提供者的元数据 metadataReport类型为ZookeeperMetadataReport 方法来源于父类模板方法: AbstractMetadataReport类型的storeProviderMetadata模板方法</span> |
| </span></span><span style="display:flex;"><span> metadataReport.storeProviderMetadata( |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> MetadataIdentifier( |
| </span></span><span style="display:flex;"><span> url.getServiceInterface(), |
| </span></span><span style="display:flex;"><span> url.getVersion() <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> : url.getVersion(), |
| </span></span><span style="display:flex;"><span> url.getGroup() <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> : url.getGroup(), |
| </span></span><span style="display:flex;"><span> PROVIDER_SIDE, |
| </span></span><span style="display:flex;"><span> applicationModel.getApplicationName()) |
| </span></span><span style="display:flex;"><span> , serviceDefinition); |
| </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">//服务消费者走这个逻辑</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Map.Entry<span style="color:#719e07">&lt;</span>String, MetadataReport<span style="color:#719e07">&gt;</span> entry : getMetadataReports(applicationModel).entrySet()) { |
| </span></span><span style="display:flex;"><span> MetadataReport metadataReport <span style="color:#719e07">=</span> entry.getValue(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>metadataReport.shouldReportDefinition()) { |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;Report of service definition is disabled for &#34;</span> <span style="color:#719e07">+</span> entry.getKey()); |
| </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> metadataReport.storeConsumerMetadata( |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> MetadataIdentifier( |
| </span></span><span style="display:flex;"><span> url.getServiceInterface(), |
| </span></span><span style="display:flex;"><span> url.getVersion() <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> : url.getVersion(), |
| </span></span><span style="display:flex;"><span> url.getGroup() <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;&#34;</span> : url.getGroup(), |
| </span></span><span style="display:flex;"><span> CONSUMER_SIDE, |
| </span></span><span style="display:flex;"><span> applicationModel.getApplicationName()), |
| </span></span><span style="display:flex;"><span> url.getParameters()); |
| </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> <span style="color:#586e75">//ignore error</span> |
| </span></span><span style="display:flex;"><span> logger.error(<span style="color:#2aa198">&#34;publish service definition metadata error.&#34;</span>, e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否同步配置对应sync-report 默认为异步</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (syncReport) { |
| </span></span><span style="display:flex;"><span> storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> reportCacheExecutor.execute(() <span style="color:#719e07">-&gt;</span> storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(<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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> allMetadataReports.put(providerMetadataIdentifier, serviceDefinition); |
| </span></span><span style="display:flex;"><span> failedReports.remove(providerMetadataIdentifier); |
| </span></span><span style="display:flex;"><span> Gson gson <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Gson(); |
| </span></span><span style="display:flex;"><span> String data <span style="color:#719e07">=</span> gson.toJson(serviceDefinition); |
| </span></span><span style="display:flex;"><span> doStoreProviderMetadata(providerMetadataIdentifier, data); |
| </span></span><span style="display:flex;"><span> saveProperties(providerMetadataIdentifier, data, <span style="color:#cb4b16">true</span>, <span style="color:#719e07">!</span>syncReport); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </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> failedReports.put(providerMetadataIdentifier, serviceDefinition); |
| </span></span><span style="display:flex;"><span> metadataReportRetry.startRetryTask(); |
| </span></span><span style="display:flex;"><span> logger.error(<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.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) { |
| </span></span><span style="display:flex;"><span> storeMetadata(providerMetadataIdentifier, serviceDefinitions); |
| </span></span><span style="display:flex;"><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>(MetadataIdentifier metadataIdentifier, String v) { |
| </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> zkClient.create(getNodePath(metadataIdentifier), v, <span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ServiceConfigBase sc : configManager.getServices()) { |
| </span></span><span style="display:flex;"><span> exportServiceInternal(sc); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ServiceConfigBase sc : configManager.getServices()) { |
| </span></span><span style="display:flex;"><span> exportServiceInternal(sc); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(ServiceConfigBase sc) { |
| </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> (ServiceConfig<span style="color:#719e07">&lt;?&gt;</span>) sc; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务配置刷新 配置优先级覆盖</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>serviceConfig.isRefreshed()) { |
| </span></span><span style="display:flex;"><span> serviceConfig.refresh(); |
| </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:#719e07">if</span> (sc.isExported()) { |
| </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">//是否异步方式导出 全局配置或者服务级其中一个配置了异步则异步处理</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (exportAsync <span style="color:#719e07">||</span> sc.shouldExportAsync()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//异步其实就是使用线程来导出服务serviceExportExecutor</span> |
| </span></span><span style="display:flex;"><span> ExecutorService executor <span style="color:#719e07">=</span> executorRepository.getServiceExportExecutor(); |
| </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.runAsync(() <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>sc.isExported()) { |
| </span></span><span style="display:flex;"><span> sc.export(); |
| </span></span><span style="display:flex;"><span> exportedServices.add(sc); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </span></span><span style="display:flex;"><span> logger.error(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; export async catch error : &#34;</span> <span style="color:#719e07">+</span> t.getMessage(), t); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }, executor); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> asyncExportingFutures.add(future); |
| </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:#719e07">if</span> (<span style="color:#719e07">!</span>sc.isExported()) { |
| </span></span><span style="display:flex;"><span> sc.export(); |
| </span></span><span style="display:flex;"><span> exportedServices.add(sc); |
| </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>这个逻辑里面做了一些基本的操作,可以直接看注释然后调用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></span><span style="display:flex;"><span> <span style="color:#586e75">//已经导出过服务直接放那会</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.exported) { |
| </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></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></span><span style="display:flex;"><span> getScopeModel().getDeployer().start(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//悲观锁</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (<span style="color:#719e07">this</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.exported) { |
| </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">//配置是否刷新 前面初始化时候已经刷新过配置</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>.isRefreshed()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.refresh(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务导出配置配置为false则不导出</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.shouldExport()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务发布前初始化一下元数据对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.init(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (shouldDelay()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置了服务的延迟发布配置则走延迟发布逻辑</span> |
| </span></span><span style="display:flex;"><span> doDelayExport(); |
| </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> doExport(); |
| </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><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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.initialized.compareAndSet(<span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</span>)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载服务监听器 这里暂时没有服务监听器扩展</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load ServiceListeners from extension</span> |
| </span></span><span style="display:flex;"><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>.getExtensionLoader(ServiceListener.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.serviceListeners.addAll(extensionLoader.getSupportedExtensionInstances()); |
| </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> initServiceMetadata(provider); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据</span> |
| </span></span><span style="display:flex;"><span> serviceMetadata.setServiceType(getInterfaceClass()); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> serviceMetadata.setTarget(getRef()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据的key格式为 group/服务接口:版本号</span> |
| </span></span><span style="display:flex;"><span> serviceMetadata.generateServiceKey(); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//取消发布</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (unexported) { |
| </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;The service &#34;</span> <span style="color:#719e07">+</span> interfaceClass.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has already unexported!&#34;</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:#719e07">if</span> (exported) { |
| </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">//服务路径 为空则设置为接口名,本例子中为link.elastic.dubbo.entity.DemoService</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(path)) { |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> interfaceName; |
| </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> doExportUrls(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//</span> |
| </span></span><span style="display:flex;"><span> exported(); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//模块服务存储库</span> |
| </span></span><span style="display:flex;"><span> ModuleServiceRepository repository <span style="color:#719e07">=</span> getScopeModel().getServiceRepository(); |
| </span></span><span style="display:flex;"><span> ServiceDescriptor serviceDescriptor; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ref为服务实现类型 这里对应我们例子的DemoServiceImpl</span> |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span>(serverService){ |
| </span></span><span style="display:flex;"><span> serviceDescriptor<span style="color:#719e07">=</span>((ServerService) ref).getServiceDescriptor(); |
| </span></span><span style="display:flex;"><span> repository.registerService(serviceDescriptor); |
| </span></span><span style="display:flex;"><span> }<span style="color:#719e07">else</span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//我们代码走这个逻辑 注册服务 这个注册不是向注册中心注册 这个是解析服务接口将服务方法等描述信息存放在了服务存储ModuleServiceRepository类型对象的成员变量services中</span> |
| </span></span><span style="display:flex;"><span> serviceDescriptor <span style="color:#719e07">=</span> repository.registerService(getInterfaceClass()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供者领域模型, 提供者领域模型 封装了一些提供者需要的就基本属性同时内部解析封装方法信息 ProviderMethodModel 列表 , 服务标识符 格式group/服务接:版本号</span> |
| </span></span><span style="display:flex;"><span> providerModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ProviderModel(getUniqueServiceName(), |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务实现类DemoServiceImpl</span> |
| </span></span><span style="display:flex;"><span> ref, |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务描述符 描述符里面包含了服务接口的方法信息,不过服务接口通过反射也可以拿到方法信息</span> |
| </span></span><span style="display:flex;"><span> serviceDescriptor, |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前所处模型</span> |
| </span></span><span style="display:flex;"><span> getScopeModel(), |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前服务接口的元数据对象</span> |
| </span></span><span style="display:flex;"><span> serviceMetadata); |
| </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> repository.registerProvider(providerModel); |
| </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">//这里会获取到两个 由dubbo.application.register-mode 双注册配置决定</span> |
| </span></span><span style="display:flex;"><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">//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">//参数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">//这里后面详细说下 服务双注册 dubbo.application.register-mode</span> |
| </span></span><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.loadRegistries(<span style="color:#719e07">this</span>, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ProtocolConfig protocolConfig : protocols) { |
| </span></span><span style="display:flex;"><span> String pathKey <span style="color:#719e07">=</span> URL.buildKey(getContextPath(protocolConfig) |
| </span></span><span style="display:flex;"><span> .map(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></span><span style="display:flex;"><span> .orElse(path), group, version); |
| </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:#719e07">if</span>(<span style="color:#719e07">!</span>serverService) { |
| </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">//模块服务存储库ModuleServiceRepository存储服务接口信息</span> |
| </span></span><span style="display:flex;"><span> repository.registerService(pathKey, interfaceClass); |
| </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> doExportUrlsFor1Protocol(protocolConfig, registryURLs); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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.loadRegistries(<span style="color:#719e07">this</span>, <span style="color:#cb4b16">true</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>(ScopeModel scopeModel, List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryList, <span style="color:#dc322f">boolean</span> provider) { |
| </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.size()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的注册中心 为每个注册中心增加兼容的服务发现注册中心地址配置</span> |
| </span></span><span style="display:flex;"><span> registryList.forEach(registryURL <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:#719e07">if</span> (provider) { |
| </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></span><span style="display:flex;"><span> String registerMode; |
| </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">//前面这个逻辑是直接接给result结果中添加应用级注册,如果是all配置则增加接口级注册信息</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) { |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> registryURL.getParameter(REGISTER_MODE_KEY, ConfigurationUtils.getCachedDynamicProperty(scopeModel, DUBBO_REGISTER_MODE_DEFAULT_KEY, DEFAULT_REGISTER_MODE_INSTANCE)); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isValidRegisterMode(registerMode)) { |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> DEFAULT_REGISTER_MODE_INSTANCE; |
| </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> result.add(registryURL); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> registryNotExists(registryURL, registryList, REGISTRY_PROTOCOL)) { |
| </span></span><span style="display:flex;"><span> URL interfaceCompatibleRegistryURL <span style="color:#719e07">=</span> URLBuilder.from(registryURL) |
| </span></span><span style="display:flex;"><span> .setProtocol(REGISTRY_PROTOCOL) |
| </span></span><span style="display:flex;"><span> .removeParameter(REGISTRY_TYPE_KEY) |
| </span></span><span style="display:flex;"><span> .build(); |
| </span></span><span style="display:flex;"><span> result.add(interfaceCompatibleRegistryURL); |
| </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">//正常情况下我们的配置会走这个逻辑</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> registerMode <span style="color:#719e07">=</span> registryURL.getParameter(REGISTER_MODE_KEY, ConfigurationUtils.getCachedDynamicProperty(scopeModel, DUBBO_REGISTER_MODE_DEFAULT_KEY, DEFAULT_REGISTER_MODE_ALL)); |
| </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(registerMode)) { |
| </span></span><span style="display:flex;"><span> registerMode <span style="color:#719e07">=</span> DEFAULT_REGISTER_MODE_INTERFACE; |
| </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:#719e07">if</span> ((DEFAULT_REGISTER_MODE_INSTANCE.equalsIgnoreCase(registerMode) <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> registryNotExists(registryURL, registryList, SERVICE_REGISTRY_PROTOCOL)) { |
| </span></span><span style="display:flex;"><span> URL serviceDiscoveryRegistryURL <span style="color:#719e07">=</span> URLBuilder.from(registryURL) |
| </span></span><span style="display:flex;"><span> .setProtocol(SERVICE_REGISTRY_PROTOCOL) |
| </span></span><span style="display:flex;"><span> .removeParameter(REGISTRY_TYPE_KEY) |
| </span></span><span style="display:flex;"><span> .build(); |
| </span></span><span style="display:flex;"><span> result.add(serviceDiscoveryRegistryURL); |
| </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:#719e07">if</span> (DEFAULT_REGISTER_MODE_INTERFACE.equalsIgnoreCase(registerMode) <span style="color:#719e07">||</span> DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)) { |
| </span></span><span style="display:flex;"><span> result.add(registryURL); |
| </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> FrameworkStatusReportService reportService <span style="color:#719e07">=</span> ScopeModelUtil.getApplicationModel(scopeModel).getBeanFactory().getBean(FrameworkStatusReportService.class); |
| </span></span><span style="display:flex;"><span> reportService.reportRegistrationStatus(reportService.createRegistrationReport(registerMode)); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> result.add(registryURL); |
| </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">return</span> result; |
| </span></span><span style="display:flex;"><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:#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(protocolConfig, registryURLs); |
| </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:#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>(ProtocolConfig protocolConfig, List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成协议配置具体可见下图中的元数据配置中的attachments</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> map <span style="color:#719e07">=</span> buildAttributes(protocolConfig); |
| </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></span><span style="display:flex;"><span> map.keySet().removeIf(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.get(key) <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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></span><span style="display:flex;"><span> serviceMetadata.getAttachments().putAll(map); |
| </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> URL url <span style="color:#719e07">=</span> buildUrl(protocolConfig, map); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//导出url</span> |
| </span></span><span style="display:flex;"><span> exportUrl(url, registryURLs); |
| </span></span><span style="display:flex;"><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>(URL url, List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs) { |
| </span></span><span style="display:flex;"><span> String scope <span style="color:#719e07">=</span> url.getParameter(SCOPE_KEY); |
| </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:#719e07">if</span> (<span style="color:#719e07">!</span>SCOPE_NONE.equalsIgnoreCase(scope)) { |
| </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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>SCOPE_REMOTE.equalsIgnoreCase(scope)) { |
| </span></span><span style="display:flex;"><span> exportLocal(url); |
| </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">// 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:#719e07">if</span> (<span style="color:#719e07">!</span>SCOPE_LOCAL.equalsIgnoreCase(scope)) { |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> exportRemote(url, registryURLs); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isGeneric(generic) <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>getScopeModel().isInternal()) { |
| </span></span><span style="display:flex;"><span> MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel()); |
| </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">this</span>.urls.add(url); |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </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> URL local <span style="color:#719e07">=</span> URLBuilder.from(url) |
| </span></span><span style="display:flex;"><span> .setProtocol(LOCAL_PROTOCOL) |
| </span></span><span style="display:flex;"><span> .setHost(LOCALHOST_VALUE) |
| </span></span><span style="display:flex;"><span> .setPort(0) |
| </span></span><span style="display:flex;"><span> .build(); |
| </span></span><span style="display:flex;"><span> local <span style="color:#719e07">=</span> local.setScopeModel(getScopeModel()) |
| </span></span><span style="display:flex;"><span> .setServiceModel(providerModel); |
| </span></span><span style="display:flex;"><span> doExportUrl(local, <span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;Export dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to local registry url : &#34;</span> <span style="color:#719e07">+</span> local); |
| </span></span><span style="display:flex;"><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>(URL url, <span style="color:#dc322f">boolean</span> withMetaData) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里是由adaptor扩展类型处理过的 我们直接看默认的类型javassist 对应JavassistProxyFactory代理工厂 获取调用对象 (</span> |
| </span></span><span style="display:flex;"><span> Invoker<span style="color:#719e07">&lt;?&gt;</span> invoker <span style="color:#719e07">=</span> proxyFactory.getInvoker(ref, (Class) interfaceClass, url); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (withMetaData) { |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DelegateProviderMetaDataInvoker(invoker, <span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> Exporter<span style="color:#719e07">&lt;?&gt;</span> exporter <span style="color:#719e07">=</span> protocolSPI.export(invoker); |
| </span></span><span style="display:flex;"><span> exporters.add(exporter); |
| </span></span><span style="display:flex;"><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>(T proxy, Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type, URL url) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</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">// 创建实际服务提供者的代理类型,代理类型后缀为DubboWrap在这里类型为 link.elastic.dubbo.entity.DemoServiceImplDubboWrap0</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> Wrapper wrapper <span style="color:#719e07">=</span> Wrapper.getWrapper(proxy.getClass().getName().indexOf(<span style="color:#2aa198">&#39;$&#39;</span>) <span style="color:#719e07">&lt;</span> 0 <span style="color:#719e07">?</span> proxy.getClass() : type); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个匿名内部类对象 继承自AbstractProxyInvoker的Invoker对象</span> |
| </span></span><span style="display:flex;"><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, type, url) { |
| </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>(T proxy, String methodName, |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;[]</span> parameterTypes, |
| </span></span><span style="display:flex;"><span> Object<span style="color:#719e07">[]</span> arguments) <span style="color:#268bd2">throws</span> Throwable { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments); |
| </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> (Throwable fromJavassist) { |
| </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></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><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.export(invoker); |
| </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>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) <span style="color:#268bd2">throws</span> RpcException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里主要逻辑是将服务提供者url添加到服务存储仓库中</span> |
| </span></span><span style="display:flex;"><span> getFrameworkModel(invoker.getUrl().getScopeModel()).getServiceRepository().registerProviderUrl(invoker.getUrl()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(invoker); |
| </span></span><span style="display:flex;"><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>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) <span style="color:#268bd2">throws</span> RpcException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心的协议导出直接执行</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (UrlUtils.isRegistry(invoker.getUrl())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(invoker); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//过滤器调用链FilterChainBuilder的扩展对象查询</span> |
| </span></span><span style="display:flex;"><span> FilterChainBuilder builder <span style="color:#719e07">=</span> getFilterChainBuilder(invoker.getUrl()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为2步 生成过滤器调用链 然后使用链表中的节点调用 这里值查询provider类型的过滤器</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(builder.buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER)); |
| </span></span><span style="display:flex;"><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:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> originalInvoker, String key, String group) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//originalInvoker代表真正的服务调用器</span> |
| </span></span><span style="display:flex;"><span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> last <span style="color:#719e07">=</span> originalInvoker; |
| </span></span><span style="display:flex;"><span> URL url <span style="color:#719e07">=</span> originalInvoker.getUrl(); |
| </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(url); |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Filter<span style="color:#719e07">&gt;</span> filters; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (moduleModels <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> moduleModels.size() <span style="color:#719e07">==</span> 1) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//类型Filter key为service.filter 分组为provider 所有提供者过滤器拉取</span> |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> ScopeModelUtil.getExtensionLoader(Filter.class, moduleModels.get(0)).getActivateExtension(url, key, group); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> (moduleModels <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> moduleModels.size() <span style="color:#719e07">&gt;</span> 1) { |
| </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> (ModuleModel moduleModel : moduleModels) { |
| </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.getExtensionLoader(Filter.class, moduleModel).getActivateExtension(url, key, group); |
| </span></span><span style="display:flex;"><span> filters.addAll(tempFilters); |
| </span></span><span style="display:flex;"><span> directors.add(moduleModel.getExtensionDirector()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> sortingAndDeduplication(filters, directors); |
| </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> filters <span style="color:#719e07">=</span> ScopeModelUtil.getExtensionLoader(Filter.class, <span style="color:#cb4b16">null</span>).getActivateExtension(url, key, group); |
| </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">//倒序拼接,将过滤器的调用对象添加到链表中 最后倒序遍历之后 last节点指向了调用链路链表头节点的对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>CollectionUtils.isEmpty(filters)) { |
| </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> filters.size() <span style="color:#719e07">-</span> 1; i <span style="color:#719e07">&gt;=</span> 0; i<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.get(i); |
| </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></span><span style="display:flex;"><span> <span style="color:#586e75">//每个invoker对象中都有originalInvoker对象</span> |
| </span></span><span style="display:flex;"><span> last <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CopyOfFilterChainNode<span style="color:#719e07">&lt;&gt;</span>(originalInvoker, next, filter); |
| </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> CallbackRegistrationInvoker<span style="color:#719e07">&lt;&gt;</span>(last, filters); |
| </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> last; |
| </span></span><span style="display:flex;"><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>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) <span style="color:#268bd2">throws</span> RpcException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心地址则直接导出</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (UrlUtils.isRegistry(invoker.getUrl())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(invoker); |
| </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:#719e07">return</span> <span style="color:#719e07">new</span> ListenerExporterWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span>(protocol.export(invoker), |
| </span></span><span style="display:flex;"><span> Collections.unmodifiableList(ScopeModelUtil.getExtensionLoader(ExporterListener.class, invoker.getUrl().getScopeModel()) |
| </span></span><span style="display:flex;"><span> .getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY))); |
| </span></span><span style="display:flex;"><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>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) <span style="color:#268bd2">throws</span> RpcException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心导出的时候开启QOS 默认端口22222</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (UrlUtils.isRegistry(invoker.getUrl())) { |
| </span></span><span style="display:flex;"><span> startQosServer(invoker.getUrl()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(invoker); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(invoker); |
| </span></span><span style="display:flex;"><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>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) <span style="color:#268bd2">throws</span> RpcException { |
| </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, invoker.getUrl().getServiceKey(), exporterMap); |
| </span></span><span style="display:flex;"><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:#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>(URL url, List<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> registryURLs) { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(registryURLs)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有注册地址与注册模式 逐个注册</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (URL registryURL : registryURLs) { |
| </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:#719e07">if</span> (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) { |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url.addParameterIfAbsent(SERVICE_NAME_MAPPING_KEY, <span style="color:#2aa198">&#34;true&#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:#586e75">//if protocol is only injvm ,not register</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { |
| </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">//为协议url 添加动态配置dynamic</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//监控配置暂时为null</span> |
| </span></span><span style="display:flex;"><span> URL monitorUrl <span style="color:#719e07">=</span> ConfigValidationUtils.loadMonitor(<span style="color:#719e07">this</span>, registryURL); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (monitorUrl <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url.putAttribute(MONITOR_KEY, monitorUrl); |
| </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">// For providers, this is used to enable custom proxy to generate invoker</span> |
| </span></span><span style="display:flex;"><span> String proxy <span style="color:#719e07">=</span> url.getParameter(PROXY_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isNotEmpty(proxy)) { |
| </span></span><span style="display:flex;"><span> registryURL <span style="color:#719e07">=</span> registryURL.addParameter(PROXY_KEY, proxy); |
| </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">//开始注册服务了 打印个认知 提示下</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (url.getParameter(REGISTER_KEY, <span style="color:#cb4b16">true</span>)) { |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;Register dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass.getName() <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.getAddress()); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;Export dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to url &#34;</span> <span style="color:#719e07">+</span> url); |
| </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> doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), <span style="color:#cb4b16">true</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">else</span> { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;Export dubbo service &#34;</span> <span style="color:#719e07">+</span> interfaceClass.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to url &#34;</span> <span style="color:#719e07">+</span> url); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> doExportUrl(url, <span style="color:#cb4b16">true</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">return</span> url; |
| </span></span><span style="display:flex;"><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>(URL url, <span style="color:#dc322f">boolean</span> withMetaData) { |
| </span></span><span style="display:flex;"><span> Invoker<span style="color:#719e07">&lt;?&gt;</span> invoker <span style="color:#719e07">=</span> proxyFactory.getInvoker(ref, (Class) interfaceClass, url); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (withMetaData) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//远程服务导出逐个值为true 元数据invoker包装一下</span> |
| </span></span><span style="display:flex;"><span> invoker <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DelegateProviderMetaDataInvoker(invoker, <span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> Exporter<span style="color:#719e07">&lt;?&gt;</span> exporter <span style="color:#719e07">=</span> protocolSPI.export(invoker); |
| </span></span><span style="display:flex;"><span> exporters.add(exporter); |
| </span></span><span style="display:flex;"><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>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) <span style="color:#268bd2">throws</span> RpcException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心的协议导出直接执行</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:#719e07">if</span> (UrlUtils.isRegistry(invoker.getUrl())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(invoker); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//过滤器调用链FilterChainBuilder的扩展对象查询</span> |
| </span></span><span style="display:flex;"><span> FilterChainBuilder builder <span style="color:#719e07">=</span> getFilterChainBuilder(invoker.getUrl()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里分为2步 生成过滤器调用链 然后使用链表中的节点调用 这里值查询provider类型的过滤器</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(builder.buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER)); |
| </span></span><span style="display:flex;"><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>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) <span style="color:#268bd2">throws</span> RpcException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心地址则直接导出</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:#719e07">if</span> (UrlUtils.isRegistry(invoker.getUrl())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(invoker); |
| </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:#719e07">return</span> <span style="color:#719e07">new</span> ListenerExporterWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span>(protocol.export(invoker), |
| </span></span><span style="display:flex;"><span> Collections.unmodifiableList(ScopeModelUtil.getExtensionLoader(ExporterListener.class, invoker.getUrl().getScopeModel()) |
| </span></span><span style="display:flex;"><span> .getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY))); |
| </span></span><span style="display:flex;"><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>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) <span style="color:#268bd2">throws</span> RpcException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心导出的时候开启QOS 默认端口22222</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (UrlUtils.isRegistry(invoker.getUrl())) { |
| </span></span><span style="display:flex;"><span> startQosServer(invoker.getUrl()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(invoker); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocol.export(invoker); |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>hasStarted.compareAndSet(<span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</span>)) { |
| </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></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> qosEnable <span style="color:#719e07">=</span> url.getParameter(QOS_ENABLE, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>qosEnable) { |
| </span></span><span style="display:flex;"><span> logger.info(<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></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></span><span style="display:flex;"><span> String host <span style="color:#719e07">=</span> url.getParameter(QOS_HOST); |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> port <span style="color:#719e07">=</span> url.getParameter(QOS_PORT, QosConstants.DEFAULT_PORT); |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> acceptForeignIp <span style="color:#719e07">=</span> Boolean.parseBoolean(url.getParameter(ACCEPT_FOREIGN_IP, <span style="color:#2aa198">&#34;false&#34;</span>)); |
| </span></span><span style="display:flex;"><span> Server server <span style="color:#719e07">=</span> frameworkModel.getBeanFactory().getBean(Server.class); |
| </span></span><span style="display:flex;"><span> server.setHost(host); |
| </span></span><span style="display:flex;"><span> server.setPort(port); |
| </span></span><span style="display:flex;"><span> server.setAcceptForeignIp(acceptForeignIp); |
| </span></span><span style="display:flex;"><span> server.start(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable throwable) { |
| </span></span><span style="display:flex;"><span> logger.warn(<span style="color:#2aa198">&#34;Fail to start qos server: &#34;</span>, throwable); |
| </span></span><span style="display:flex;"><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:#268bd2">throws</span> Throwable { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>started.compareAndSet(<span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</span>)) { |
| </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">//1个主线程</span> |
| </span></span><span style="display:flex;"><span> boss <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> NioEventLoopGroup(1, <span style="color:#719e07">new</span> DefaultThreadFactory(<span style="color:#2aa198">&#34;qos-boss&#34;</span>, <span style="color:#cb4b16">true</span>)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//0个从线程</span> |
| </span></span><span style="display:flex;"><span> worker <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> NioEventLoopGroup(0, <span style="color:#719e07">new</span> DefaultThreadFactory(<span style="color:#2aa198">&#34;qos-worker&#34;</span>, <span style="color:#cb4b16">true</span>)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务端启动器,和参数设置</span> |
| </span></span><span style="display:flex;"><span> ServerBootstrap serverBootstrap <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServerBootstrap(); |
| </span></span><span style="display:flex;"><span> serverBootstrap.group(boss, worker); |
| </span></span><span style="display:flex;"><span> serverBootstrap.channel(NioServerSocketChannel.class); |
| </span></span><span style="display:flex;"><span> serverBootstrap.option(ChannelOption.SO_REUSEADDR, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> serverBootstrap.childOption(ChannelOption.TCP_NODELAY, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> serverBootstrap.childHandler(<span style="color:#719e07">new</span> ChannelInitializer<span style="color:#719e07">&lt;</span>Channel<span style="color:#719e07">&gt;</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>(Channel ch) <span style="color:#268bd2">throws</span> Exception { |
| </span></span><span style="display:flex;"><span> ch.pipeline().addLast(<span style="color:#719e07">new</span> QosProcessHandler(frameworkModel, welcome, acceptForeignIp)); |
| </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:#719e07">if</span> (StringUtils.isBlank(host)) { |
| </span></span><span style="display:flex;"><span> serverBootstrap.bind(port).sync(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> serverBootstrap.bind(host, port).sync(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;qos-server bind localhost:&#34;</span> <span style="color:#719e07">+</span> port); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable throwable) { |
| </span></span><span style="display:flex;"><span> logger.error(<span style="color:#2aa198">&#34;qos-server can not bind localhost:&#34;</span> <span style="color:#719e07">+</span> port, throwable); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> throwable; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> originInvoker) <span style="color:#268bd2">throws</span> RpcException { |
| </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> URL registryUrl <span style="color:#719e07">=</span> getRegistryUrl(originInvoker); |
| </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">//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> URL providerUrl <span style="color:#719e07">=</span> getProviderUrl(originInvoker); |
| </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">// 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">// 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">// subscription information to cover.</span> |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> URL overrideSubscribeUrl <span style="color:#719e07">=</span> getSubscribedOverrideUrl(providerUrl); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//override配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> OverrideListener overrideSubscribeListener <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> OverrideListener(overrideSubscribeUrl, originInvoker); |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>URL, NotifyListener<span style="color:#719e07">&gt;</span> overrideListeners <span style="color:#719e07">=</span> getProviderConfigurationListener(providerUrl).getOverrideListeners(); |
| </span></span><span style="display:flex;"><span> overrideListeners.put(registryUrl, overrideSubscribeListener); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> providerUrl <span style="color:#719e07">=</span> overrideUrlWithConfig(providerUrl, overrideSubscribeListener); |
| </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:#268bd2">final</span> ExporterChangeableWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> exporter <span style="color:#719e07">=</span> doLocalExport(originInvoker, providerUrl); |
| </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">//通过URL获取 注册中心Registry操作对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> Registry registry <span style="color:#719e07">=</span> getRegistry(registryUrl); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//需要向注册中心注册地址转换</span> |
| </span></span><span style="display:flex;"><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:#268bd2">final</span> URL registeredProviderUrl <span style="color:#719e07">=</span> getUrlToRegistry(providerUrl, registryUrl); |
| </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:#dc322f">boolean</span> register <span style="color:#719e07">=</span> providerUrl.getParameter(REGISTER_KEY, <span style="color:#cb4b16">true</span>) <span style="color:#719e07">&amp;&amp;</span> registryUrl.getParameter(REGISTER_KEY, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//是否向注册中心注册</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (register) { |
| </span></span><span style="display:flex;"><span> register(registry, registeredProviderUrl); |
| </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">// register stated url on provider model</span> |
| </span></span><span style="display:flex;"><span> registerStatedUrl(registryUrl, registeredProviderUrl, register); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> exporter.setRegisterUrl(registeredProviderUrl); |
| </span></span><span style="display:flex;"><span> exporter.setSubscribeUrl(overrideSubscribeUrl); |
| </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.isServiceDiscovery()) { |
| </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> registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener); |
| </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> notifyExport(exporter); |
| </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:#719e07">return</span> <span style="color:#719e07">new</span> DestroyableExporter<span style="color:#719e07">&lt;&gt;</span>(exporter); |
| </span></span><span style="display:flex;"><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:#268bd2">final</span> Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> originInvoker, URL providerUrl) { |
| </span></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> getCacheKey(originInvoker); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (ExporterChangeableWrapper<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span>) bounds.computeIfAbsent(key, s <span style="color:#719e07">-&gt;</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, providerUrl); |
| </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:#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.export(invokerDelegate), originInvoker); |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><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>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) <span style="color:#268bd2">throws</span> RpcException { |
| </span></span><span style="display:flex;"><span> checkDestroyed(); |
| </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> URL url <span style="color:#719e07">=</span> invoker.getUrl(); |
| </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">//生成服务的key参考:link.elastic.dubbo.entity.DemoService:20880</span> |
| </span></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> serviceKey(url); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建导出服务用的导出器DubboExporter</span> |
| </span></span><span style="display:flex;"><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, key, exporterMap); |
| </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">//stub配置校验</span> |
| </span></span><span style="display:flex;"><span> Boolean isStubSupportEvent <span style="color:#719e07">=</span> url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT); |
| </span></span><span style="display:flex;"><span> Boolean isCallbackservice <span style="color:#719e07">=</span> url.getParameter(IS_CALLBACK_SERVICE, <span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isStubSupportEvent <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isCallbackservice) { |
| </span></span><span style="display:flex;"><span> String stubServiceMethods <span style="color:#719e07">=</span> url.getParameter(STUB_EVENT_METHODS_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (stubServiceMethods <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">||</span> stubServiceMethods.length() <span style="color:#719e07">==</span> 0) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isWarnEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.warn(<span style="color:#719e07">new</span> IllegalStateException(<span style="color:#2aa198">&#34;consumer [&#34;</span> <span style="color:#719e07">+</span> url.getParameter(INTERFACE_KEY) <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></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">//创建服务开启服务端口</span> |
| </span></span><span style="display:flex;"><span> openServer(url); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//</span> |
| </span></span><span style="display:flex;"><span> optimizeSerialization(url); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> exporter; |
| </span></span><span style="display:flex;"><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>(URL url) { |
| </span></span><span style="display:flex;"><span> checkDestroyed(); |
| </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> String key <span style="color:#719e07">=</span> url.getAddress(); |
| </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></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> isServer <span style="color:#719e07">=</span> url.getParameter(IS_SERVER_KEY, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isServer) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//协议服务器 下面一个双重校验锁检查,如果为空则创建服务</span> |
| </span></span><span style="display:flex;"><span> ProtocolServer server <span style="color:#719e07">=</span> serverMap.get(key); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (server <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (<span style="color:#719e07">this</span>) { |
| </span></span><span style="display:flex;"><span> server <span style="color:#719e07">=</span> serverMap.get(key); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (server <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> serverMap.put(key, createServer(url)); |
| </span></span><span style="display:flex;"><span> }<span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> server.reset(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">else</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> server.reset(url); |
| </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>为当前地址创建协议服务对应方法如下: |
| 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>(URL url) { |
| </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> url <span style="color:#719e07">=</span> URLBuilder.from(url) |
| </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> .addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString()) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// enable heartbeat by default</span> |
| </span></span><span style="display:flex;"><span> .addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT)) |
| </span></span><span style="display:flex;"><span> .addParameter(CODEC_KEY, DubboCodec.NAME) |
| </span></span><span style="display:flex;"><span> .build(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里服务端使用的网络库这里是默认值netty</span> |
| </span></span><span style="display:flex;"><span> String str <span style="color:#719e07">=</span> url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isNotEmpty(str) <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>url.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).hasExtension(str)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException(<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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//dubbo交换器层对象创建</span> |
| </span></span><span style="display:flex;"><span> ExchangeServer server; |
| </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">//这里通过绑定url和请求处理器来创建交换器对象</span> |
| </span></span><span style="display:flex;"><span> server <span style="color:#719e07">=</span> Exchangers.bind(url, requestHandler); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (RemotingException e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException(<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.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> str <span style="color:#719e07">=</span> url.getParameter(CLIENT_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isNotEmpty(str)) { |
| </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.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).getSupportedExtensions(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>supportedTypes.contains(str)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> RpcException(<span style="color:#2aa198">&#34;Unsupported client type: &#34;</span> <span style="color:#719e07">+</span> str); |
| </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> DubboProtocolServer protocolServer <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DubboProtocolServer(server); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//关闭等待时长默认为10秒</span> |
| </span></span><span style="display:flex;"><span> loadServerProperties(protocolServer); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocolServer; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (initialized) { |
| </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">// Ensure that the initialization is completed when concurrent calls</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (startLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (initialized) { |
| </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">// register shutdown hook</span> |
| </span></span><span style="display:flex;"><span> registerShutdownHook(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> startConfigCenter(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> loadApplicationConfigs(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initModuleDeployers(); |
| </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> startMetadataCenter(); |
| </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></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has been initialized!&#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><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></span><span style="display:flex;"><span> <span style="color:#586e75">//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置</span> |
| </span></span><span style="display:flex;"><span> useRegistryAsMetadataCenterIfNecessary(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用的配置信息</span> |
| </span></span><span style="display:flex;"><span> ApplicationConfig applicationConfig <span style="color:#719e07">=</span> getApplication(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//元数据配置类型 元数据类型, local 或 remote,,如果选择远程,则需要进一步指定元数据中心</span> |
| </span></span><span style="display:flex;"><span> String metadataType <span style="color:#719e07">=</span> applicationConfig.getMetadataType(); |
| </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></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.getMetadataConfigs(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isEmpty(metadataReportConfigs)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个就是判断 如果选择远程,则需要进一步指定元数据中心 否则就抛出来异常</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) { |
| </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;No MetadataConfig found, Metadata Center address is required when &#39;metadata=remote&#39; is enabled.&#34;</span>); |
| </span></span><span style="display:flex;"><span> } |
| </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">//MetadataReport实例的存储库对象获取</span> |
| </span></span><span style="display:flex;"><span> MetadataReportInstance metadataReportInstance <span style="color:#719e07">=</span> applicationModel.getBeanFactory().getBean(MetadataReportInstance.class); |
| </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.size()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (MetadataReportConfig metadataReportConfig : metadataReportConfigs) { |
| </span></span><span style="display:flex;"><span> ConfigValidationUtils.validateMetadataConfig(metadataReportConfig); |
| </span></span><span style="display:flex;"><span> validMetadataReportConfigs.add(metadataReportConfig); |
| </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> metadataReportInstance.init(validMetadataReportConfigs); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//MetadataReport实例的存储库对象初始化失败则抛出异常</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>metadataReportInstance.inited()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException(String.format(<span style="color:#2aa198">&#34;%s MetadataConfigs found, but none of them is valid.&#34;</span>, metadataReportConfigs.size())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//配置缓存中查询元数据配置</span> |
| </span></span><span style="display:flex;"><span> Collection<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataConfigs <span style="color:#719e07">=</span> configManager.getMetadataConfigs(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置存在则直接返回</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(metadataConfigs)) { |
| </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">////查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> defaultRegistries <span style="color:#719e07">=</span> configManager.getDefaultRegistries(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (defaultRegistries.size() <span style="color:#719e07">&gt;</span> 0) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多注册中心遍历</span> |
| </span></span><span style="display:flex;"><span> defaultRegistries |
| </span></span><span style="display:flex;"><span> .stream() |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)</span> |
| </span></span><span style="display:flex;"><span> .filter(<span style="color:#719e07">this</span>::isUsedRegistryAsMetadataCenter) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心配置映射为元数据中心 映射就是获取需要的配置</span> |
| </span></span><span style="display:flex;"><span> .map(<span style="color:#719e07">this</span>::registryAsMetadataCenter) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将元数据中心配置存储在配置缓存中方便后续使用</span> |
| </span></span><span style="display:flex;"><span> .forEach(metadataReportConfig <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metadataReportConfig.getId() <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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.getMetadataConfigs(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(metadataReportConfigs)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (MetadataReportConfig existedConfig : metadataReportConfigs) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (existedConfig.getId() <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> existedConfig.getAddress().equals(metadataReportConfig.getAddress())) { |
| </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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> configManager.addMetadataReport(metadataReportConfig); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</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.getConfig(MetadataReportConfig.class, metadataReportConfig.getId()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (configOptional.isPresent()) { |
| </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> configManager.addMetadataReport(metadataReportConfig); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;use registry as metadata-center: &#34;</span> <span style="color:#719e07">+</span> metadataReportConfig); |
| </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>这个代码有些细节就不细说了 我们概括下顺序梳理下思路:</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> metadataReportInstance.init(validMetadataReportConfigs); |
| </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>(List<span style="color:#719e07">&lt;</span>MetadataReportConfig<span style="color:#719e07">&gt;</span> metadataReportConfigs) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//CAS判断是否有初始化过</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>init.compareAndSet(<span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</span>)) { |
| </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">//元数据类型配置如果未配置则默认为local </span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.metadataType <span style="color:#719e07">=</span> applicationModel.getApplicationConfigManager().getApplicationOrElseThrow().getMetadataType(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metadataType <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.metadataType <span style="color:#719e07">=</span> DEFAULT_METADATA_STORAGE_TYPE; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取MetadataReportFactory 工厂类型</span> |
| </span></span><span style="display:flex;"><span> MetadataReportFactory metadataReportFactory <span style="color:#719e07">=</span> applicationModel.getExtensionLoader(MetadataReportFactory.class).getAdaptiveExtension(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多元数据中心初始化</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (MetadataReportConfig metadataReportConfig : metadataReportConfigs) { |
| </span></span><span style="display:flex;"><span> init(metadataReportConfig, metadataReportFactory); |
| </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">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span>(MetadataReportConfig config, MetadataReportFactory metadataReportFactory) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置转url</span> |
| </span></span><span style="display:flex;"><span> URL url <span style="color:#719e07">=</span> config.toUrl(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (METADATA_REPORT_KEY.equals(url.getProtocol())) { |
| </span></span><span style="display:flex;"><span> String protocol <span style="color:#719e07">=</span> url.getParameter(METADATA_REPORT_KEY, DEFAULT_DIRECTORY); |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> URLBuilder.from(url) |
| </span></span><span style="display:flex;"><span> .setProtocol(protocol) |
| </span></span><span style="display:flex;"><span> .setScopeModel(config.getScopeModel()) |
| </span></span><span style="display:flex;"><span> .removeParameter(METADATA_REPORT_KEY) |
| </span></span><span style="display:flex;"><span> .build(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url.addParameterIfAbsent(APPLICATION_KEY, applicationModel.getCurrentConfig().getName()); |
| </span></span><span style="display:flex;"><span> String relatedRegistryId <span style="color:#719e07">=</span> isEmpty(config.getRegistry()) <span style="color:#719e07">?</span> (isEmpty(config.getId()) <span style="color:#719e07">?</span> DEFAULT_KEY : config.getId()) : config.getRegistry(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从元数据工厂中获取元数据</span> |
| </span></span><span style="display:flex;"><span> MetadataReport metadataReport <span style="color:#719e07">=</span> metadataReportFactory.getMetadataReport(url); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存元数据到内存</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> metadataReports.put(relatedRegistryId, metadataReport); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//与Zookeeper交互的传输器</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> ZookeeperTransporter zookeeperTransporter; |
| </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> ApplicationModel applicationModel; |
| </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>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.applicationModel <span style="color:#719e07">=</span> applicationModel; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.zookeeperTransporter <span style="color:#719e07">=</span> ZookeeperTransporter.getExtension(applicationModel); |
| </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">@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>(ZookeeperTransporter zookeeperTransporter) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.zookeeperTransporter <span style="color:#719e07">=</span> zookeeperTransporter; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetadataReport <span style="color:#268bd2">createMetadataReport</span>(URL url) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ZookeeperMetadataReport(url, zookeeperTransporter); |
| </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>元数据工厂的实现比较简单</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> MetadataReport metadataReport <span style="color:#719e07">=</span> metadataReportFactory.getMetadataReport(url); |
| </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> MetadataReport metadataReport <span style="color:#719e07">=</span> metadataReportFactory.getMetadataReport(url); |
| </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>(URL url) { |
| </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">//如果存在export则移除</span> |
| </span></span><span style="display:flex;"><span> url <span style="color:#719e07">=</span> url.setPath(MetadataReport.class.getName()) |
| </span></span><span style="display:flex;"><span> .removeParameters(EXPORT_KEY, REFER_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成元数据缓存key 元数据维度 地址+名字 </span> |
| </span></span><span style="display:flex;"><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> String key <span style="color:#719e07">=</span> url.toServiceString(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中查询 查到则直接返回</span> |
| </span></span><span style="display:flex;"><span> MetadataReport metadataReport <span style="color:#719e07">=</span> serviceStoreMap.get(key); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> metadataReport; |
| </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">// 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></span><span style="display:flex;"><span> lock.lock(); |
| </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> metadataReport <span style="color:#719e07">=</span> serviceStoreMap.get(key); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> metadataReport; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//check参数 查元数据报错是否抛出异常</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> check <span style="color:#719e07">=</span> url.getParameter(CHECK_KEY, <span style="color:#cb4b16">true</span>) <span style="color:#719e07">&amp;&amp;</span> url.getPort() <span style="color:#719e07">!=</span> 0; |
| </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> metadataReport <span style="color:#719e07">=</span> createMetadataReport(url); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>check) { |
| </span></span><span style="display:flex;"><span> logger.warn(<span style="color:#2aa198">&#34;The metadata reporter failed to initialize&#34;</span>, e); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e; |
| </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逻辑检查</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (check <span style="color:#719e07">&amp;&amp;</span> metadataReport <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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;Can not create metadata Report &#34;</span> <span style="color:#719e07">+</span> url); |
| </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:#719e07">if</span> (metadataReport <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> serviceStoreMap.put(key, metadataReport); |
| </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:#719e07">return</span> metadataReport; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">finally</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Release the lock</span> |
| </span></span><span style="display:flex;"><span> lock.unlock(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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(url); |
| </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>(URL url) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> ZookeeperMetadataReport(url, zookeeperTransporter); |
| </span></span><span style="display:flex;"><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>(URL url, ZookeeperTransporter zookeeperTransporter) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//url即配置 配置传递给抽象类 做一些公共的逻辑</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:#268bd2">super</span>(url); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (url.isAnyHost()) { |
| </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;registry address == null&#34;</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> String group <span style="color:#719e07">=</span> url.getGroup(DEFAULT_ROOT); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>group.startsWith(PATH_SEPARATOR)) { |
| </span></span><span style="display:flex;"><span> group <span style="color:#719e07">=</span> PATH_SEPARATOR <span style="color:#719e07">+</span> group; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.root <span style="color:#719e07">=</span> group; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//连接Zookeeper</span> |
| </span></span><span style="display:flex;"><span> zkClient <span style="color:#719e07">=</span> zookeeperTransporter.connect(url); |
| </span></span><span style="display:flex;"><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>(URL reportServerURL) { |
| </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> setUrl(reportServerURL); |
| </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></span><span style="display:flex;"><span> <span style="color:#586e75">//格式为: 用户目录+/.dubbo/dubbo-metadata- + 应用程序名字application + url地址(IP+端口) + 后缀.cache 如下所示</span> |
| </span></span><span style="display:flex;"><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> String defaultFilename <span style="color:#719e07">=</span> System.getProperty(USER_HOME) <span style="color:#719e07">+</span> DUBBO_METADATA <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> reportServerURL.getApplication() <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(reportServerURL.getAddress(), <span style="color:#2aa198">&#34;:&#34;</span>, <span style="color:#2aa198">&#34;-&#34;</span>) <span style="color:#719e07">+</span> CACHE; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果用户配置了缓存文件名字则以用户配置为准file</span> |
| </span></span><span style="display:flex;"><span> String filename <span style="color:#719e07">=</span> reportServerURL.getParameter(FILE_KEY, defaultFilename); |
| </span></span><span style="display:flex;"><span> File file <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件名字不为空</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ConfigUtils.isNotEmpty(filename)) { |
| </span></span><span style="display:flex;"><span> file <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File(filename); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件和父目录不存在则创建文件目录</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>file.exists() <span style="color:#719e07">&amp;&amp;</span> file.getParentFile() <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>file.getParentFile().exists()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>file.getParentFile().mkdirs()) { |
| </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;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.getParentFile() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;!&#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:#586e75">// if this file exists, firstly delete it.</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//还未初始化则已存在的历史文件删除掉</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>initialized.getAndSet(<span style="color:#cb4b16">true</span>) <span style="color:#719e07">&amp;&amp;</span> file.exists()) { |
| </span></span><span style="display:flex;"><span> file.delete(); |
| </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">//赋值给成员变量后续继续可以用</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.file <span style="color:#719e07">=</span> file; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//文件存在则直接加载文件中的内容</span> |
| </span></span><span style="display:flex;"><span> loadProperties(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//sync-report配置的值为同步配置还异步配置,true是同步配置,默认为false为异步配置</span> |
| </span></span><span style="display:flex;"><span> syncReport <span style="color:#719e07">=</span> reportServerURL.getParameter(SYNC_REPORT_KEY, <span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重试属性与逻辑也封装了一个类型 创建对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//retry-times重试次数配置 默认为100次</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//retry-period 重试间隔配置 默认为3000</span> |
| </span></span><span style="display:flex;"><span> metadataReportRetry <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetadataReportRetry(reportServerURL.getParameter(RETRY_TIMES_KEY, DEFAULT_METADATA_REPORT_RETRY_TIMES), |
| </span></span><span style="display:flex;"><span> reportServerURL.getParameter(RETRY_PERIOD_KEY, DEFAULT_METADATA_REPORT_RETRY_PERIOD)); |
| </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></span><span style="display:flex;"><span> <span style="color:#586e75">//cycle-report配置默认为true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (reportServerURL.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//开启重试定时器 24个小时间隔从元数据中心同步一次</span> |
| </span></span><span style="display:flex;"><span> reportTimerScheduler <span style="color:#719e07">=</span> Executors.newSingleThreadScheduledExecutor(<span style="color:#719e07">new</span> NamedThreadFactory(<span style="color:#2aa198">&#34;DubboMetadataReportTimer&#34;</span>, <span style="color:#cb4b16">true</span>)); |
| </span></span><span style="display:flex;"><span> reportTimerScheduler.scheduleAtFixedRate(<span style="color:#719e07">this</span>::publishAll, calculateStartTime(), ONE_DAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS); |
| </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">this</span>.reportMetadata <span style="color:#719e07">=</span> reportServerURL.getParameter(REPORT_METADATA_KEY, <span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.reportDefinition <span style="color:#719e07">=</span> reportServerURL.getParameter(REPORT_DEFINITION_KEY, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><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.newSingleThreadScheduledExecutor(<span style="color:#719e07">new</span> NamedThreadFactory(<span style="color:#2aa198">&#34;DubboMetadataReportTimer&#34;</span>, <span style="color:#cb4b16">true</span>)); |
| </span></span><span style="display:flex;"><span> reportTimerScheduler.scheduleAtFixedRate(<span style="color:#719e07">this</span>::publishAll, calculateStartTime(), ONE_DAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS); |
| </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></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;start to publish all metadata.&#34;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.doHandleMetadataCollection(allMetadataReports); |
| </span></span><span style="display:flex;"><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>(Map<span style="color:#719e07">&lt;</span>MetadataIdentifier, Object<span style="color:#719e07">&gt;</span> metadataMap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metadataMap.isEmpty()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> Iterator<span style="color:#719e07">&lt;</span>Map.Entry<span style="color:#719e07">&lt;</span>MetadataIdentifier, Object<span style="color:#719e07">&gt;&gt;</span> iterable <span style="color:#719e07">=</span> metadataMap.entrySet().iterator(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> (iterable.hasNext()) { |
| </span></span><span style="display:flex;"><span> Map.Entry<span style="color:#719e07">&lt;</span>MetadataIdentifier, Object<span style="color:#719e07">&gt;</span> item <span style="color:#719e07">=</span> iterable.next(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (PROVIDER_SIDE.equals(item.getKey().getSide())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供端的元数据则存储提供端元数据</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.storeProviderMetadata(item.getKey(), (FullServiceDefinition) item.getValue()); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> (CONSUMER_SIDE.equals(item.getKey().getSide())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//消费端的元数据则存储提供端元数据</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.storeConsumerMetadata(item.getKey(), (Map) item.getValue()); |
| </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">return</span> <span style="color:#cb4b16">false</span>; |
| </span></span><span style="display:flex;"><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>(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (syncReport) { |
| </span></span><span style="display:flex;"><span> storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> reportCacheExecutor.execute(() <span style="color:#719e07">-&gt;</span> storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(<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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> allMetadataReports.put(providerMetadataIdentifier, serviceDefinition); |
| </span></span><span style="display:flex;"><span> failedReports.remove(providerMetadataIdentifier); |
| </span></span><span style="display:flex;"><span> Gson gson <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Gson(); |
| </span></span><span style="display:flex;"><span> String data <span style="color:#719e07">=</span> gson.toJson(serviceDefinition); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内存中的元数据同步到元数据中心</span> |
| </span></span><span style="display:flex;"><span> doStoreProviderMetadata(providerMetadataIdentifier, data); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//内存中的元数据同步到本地文件</span> |
| </span></span><span style="display:flex;"><span> saveProperties(providerMetadataIdentifier, data, <span style="color:#cb4b16">true</span>, <span style="color:#719e07">!</span>syncReport); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </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> failedReports.put(providerMetadataIdentifier, serviceDefinition); |
| </span></span><span style="display:flex;"><span> metadataReportRetry.startRetryTask(); |
| </span></span><span style="display:flex;"><span> logger.error(<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.getMessage(), e); |
| </span></span><span style="display:flex;"><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:#586e75">//内存中的元数据同步到元数据中心</span> |
| </span></span><span style="display:flex;"><span>doStoreProviderMetadata(providerMetadataIdentifier, data); |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//内存中的元数据同步到本地文件</span> |
| </span></span><span style="display:flex;"><span>saveProperties(providerMetadataIdentifier, data, <span style="color:#cb4b16">true</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>(MetadataIdentifier metadataIdentifier, String v) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用zkClient创建一个节点数据为参数V v是前面说的服务定义数据</span> |
| </span></span><span style="display:flex;"><span> zkClient.create(getNodePath(metadataIdentifier), v, <span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><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>(MetadataIdentifier metadataIdentifier, String value, <span style="color:#dc322f">boolean</span> add, <span style="color:#dc322f">boolean</span> sync) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (file <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </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></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (add) { |
| </span></span><span style="display:flex;"><span> properties.setProperty(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), value); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> properties.remove(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">long</span> version <span style="color:#719e07">=</span> lastCacheChanged.incrementAndGet(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (sync) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取最新修改版本持久化到磁盘</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> SaveProperties(version).run(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> reportCacheExecutor.execute(<span style="color:#719e07">new</span> SaveProperties(version)); |
| </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> (Throwable t) { |
| </span></span><span style="display:flex;"><span> logger.warn(t.getMessage(), t); |
| </span></span><span style="display:flex;"><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:#719e07">new</span> SaveProperties(version).run(); |
| </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></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">long</span> version; |
| </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:#dc322f">long</span> version) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.version <span style="color:#719e07">=</span> version; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">run</span>() { |
| </span></span><span style="display:flex;"><span> doSaveProperties(version); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#dc322f">long</span> version) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不是最新的就不要持久化了</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (version <span style="color:#719e07">&lt;</span> lastCacheChanged.get()) { |
| </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:#719e07">if</span> (file <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </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">// Save</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></span><span style="display:flex;"><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> File lockfile <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File(file.getAbsolutePath() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;.lock&#34;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//锁文件不存在则创建锁文件</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>lockfile.exists()) { |
| </span></span><span style="display:flex;"><span> lockfile.createNewFile(); |
| </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:#719e07">try</span> (RandomAccessFile raf <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> RandomAccessFile(lockfile, <span style="color:#2aa198">&#34;rw&#34;</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">//返回与此文件关联的唯一FileChannel对象。</span> |
| </span></span><span style="display:flex;"><span> FileChannel channel <span style="color:#719e07">=</span> raf.getChannel()) { |
| </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> FileLock lock <span style="color:#719e07">=</span> channel.tryLock(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果多个线程同时进来未获取锁的则抛出异常</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (lock <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IOException(<span style="color:#2aa198">&#34;Can not lock the metadataReport cache file &#34;</span> <span style="color:#719e07">+</span> file.getAbsolutePath() <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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Save</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">///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:#719e07">if</span> (<span style="color:#719e07">!</span>file.exists()) { |
| </span></span><span style="display:flex;"><span> file.createNewFile(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Properties tmpProperties; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>syncReport) { |
| </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">// thread(reportCacheExecutor), so deep copy is not required</span> |
| </span></span><span style="display:flex;"><span> tmpProperties <span style="color:#719e07">=</span> properties; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</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">// under multi-threading, so deep copy a new container</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//异步存储会导致锁争用 使用此的store方法和setProperty方法。属性将导致多线程下的锁争用,因此深度复制新容器</span> |
| </span></span><span style="display:flex;"><span> tmpProperties <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties(); |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>Map.Entry<span style="color:#719e07">&lt;</span>Object, Object<span style="color:#719e07">&gt;&gt;</span> entries <span style="color:#719e07">=</span> properties.entrySet(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Map.Entry<span style="color:#719e07">&lt;</span>Object, Object<span style="color:#719e07">&gt;</span> entry : entries) { |
| </span></span><span style="display:flex;"><span> tmpProperties.setProperty((String) entry.getKey(), (String) entry.getValue()); |
| </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">try</span> (FileOutputStream outputFile <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FileOutputStream(file)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//Properties类型自带的方法:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将此属性表中的属性列表(键和元素对)以适合使用load(Reader)方法的格式写入输出字符流。</span> |
| </span></span><span style="display:flex;"><span> tmpProperties.store(outputFile, <span style="color:#2aa198">&#34;Dubbo metadataReport Cache&#34;</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">finally</span> { |
| </span></span><span style="display:flex;"><span> lock.release(); |
| </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> (Throwable e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (version <span style="color:#719e07">&lt;</span> lastCacheChanged.get()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span>; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> reportCacheExecutor.execute(<span style="color:#719e07">new</span> SaveProperties(lastCacheChanged.incrementAndGet())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个代码太诡异了如果是lock失败也会打印异常给人非常疑惑的感觉 后续会修复</span> |
| </span></span><span style="display:flex;"><span> logger.warn(<span style="color:#2aa198">&#34;Failed to save service store file, cause: &#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></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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (initialized) { |
| </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">// Ensure that the initialization is completed when concurrent calls</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (startLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (initialized) { |
| </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">// register shutdown hook</span> |
| </span></span><span style="display:flex;"><span> registerShutdownHook(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> startConfigCenter(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> loadApplicationConfigs(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> initModuleDeployers(); |
| </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> startMetadataCenter(); |
| </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></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has been initialized!&#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>初始化过程中会先启动配置中心配置信息处理,然后 调用加载初始化应用程序配置方法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></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.initialize(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleServiceRepository(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.moduleConfigManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleConfigManager(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.moduleConfigManager.initialize(); |
| </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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (moduleEnvironment <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> moduleEnvironment <span style="color:#719e07">=</span> (ModuleEnvironment) <span style="color:#719e07">this</span>.getExtensionLoader(ModuleExt.class) |
| </span></span><span style="display:flex;"><span> .getExtension(ModuleEnvironment.NAME); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> moduleEnvironment; |
| </span></span><span style="display:flex;"><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>(T instance) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">instanceof</span> Lifecycle) { |
| </span></span><span style="display:flex;"><span> Lifecycle lifecycle <span style="color:#719e07">=</span> (Lifecycle) instance; |
| </span></span><span style="display:flex;"><span> lifecycle.initialize(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#268bd2">throws</span> IllegalStateException { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (initialized.compareAndSet(<span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</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:#719e07">this</span>.propertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> PropertiesConfiguration(scopeModel); |
| </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:#719e07">this</span>.systemConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SystemConfiguration(); |
| </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:#719e07">this</span>.environmentConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> EnvironmentConfiguration(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从远程配置中心的全局配置获取对应配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.externalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration(<span style="color:#2aa198">&#34;ExternalConfig&#34;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从远程配置中心的应用配置获取对应配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.appExternalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration(<span style="color:#2aa198">&#34;AppExternalConfig&#34;</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:#719e07">this</span>.appConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration(<span style="color:#2aa198">&#34;AppConfig&#34;</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> loadMigrationRule(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#719e07">this</span>.propertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> PropertiesConfiguration(scopeModel); |
| </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>(ScopeModel scopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.scopeModel <span style="color:#719e07">=</span> scopeModel; |
| </span></span><span style="display:flex;"><span> refresh(); |
| </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">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refresh</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置获取的过程是借助工具类ConfigUtils来获取的</span> |
| </span></span><span style="display:flex;"><span> properties <span style="color:#719e07">=</span> ConfigUtils.getProperties(scopeModel.getClassLoaders()); |
| </span></span><span style="display:flex;"><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>(Set<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">//这个配置的KEY是dubbo.properties.file System.getProperty是从JVM参数中获取配置的 一般情况下我们在启动Java进程的时候会指定Dubbo配置文件 如配置:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//-Ddubbo.properties.file=/dubbo.properties</span> |
| </span></span><span style="display:flex;"><span> String path <span style="color:#719e07">=</span> System.getProperty(CommonConstants.DUBBO_PROPERTIES_KEY); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(path)) { |
| </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">//例如我们在环境变量中配置 dubbo.properties.file=/dubbo.properties</span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> System.getenv(CommonConstants.DUBBO_PROPERTIES_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(path)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果在JVM参数和环境变量都拿不到这个配置文件的路径我们就用默认的吧</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认的路径是类路径下的资源文件 这个路径是: dubbo.properties </span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> CommonConstants.DEFAULT_DUBBO_PROPERTIES; |
| </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> ConfigUtils.loadProperties(classLoaders, path, <span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><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.loadProperties(classLoaders, path, <span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</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>(Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders, String fileName, <span style="color:#dc322f">boolean</span> allowMultiFile, <span style="color:#dc322f">boolean</span> optional) { |
| </span></span><span style="display:flex;"><span> Properties properties <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties(); |
| </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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (checkFileNameExist(fileName)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> FileInputStream input <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FileInputStream(fileName); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> properties.load(input); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">finally</span> { |
| </span></span><span style="display:flex;"><span> input.close(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable e) { |
| </span></span><span style="display:flex;"><span> logger.warn(<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.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties; |
| </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">//为什么会有下面的逻辑呢,如果仅仅使用上面的加载方式只能加载到本系统下的配置文件,无法加载封装在jar中的根路径的配置</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>java.net.URL<span style="color:#719e07">&gt;</span> set <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</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.add(ClassUtils.getClassLoader()); |
| </span></span><span style="display:flex;"><span> classLoadersToLoad.addAll(classLoaders); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个方法loadResources在扩展加载的时候说过</span> |
| </span></span><span style="display:flex;"><span> set <span style="color:#719e07">=</span> ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad).values().stream().reduce(<span style="color:#719e07">new</span> LinkedHashSet<span style="color:#719e07">&lt;&gt;</span>(), (a, i) <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> a.addAll(i); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> a; |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </span></span><span style="display:flex;"><span> logger.warn(<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.getMessage(), t); |
| </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">if</span> (CollectionUtils.isEmpty(set)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>optional) { |
| </span></span><span style="display:flex;"><span> logger.warn(<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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties; |
| </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">if</span> (<span style="color:#719e07">!</span>allowMultiFile) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (set.size() <span style="color:#719e07">&gt;</span> 1) { |
| </span></span><span style="display:flex;"><span> String errMsg <span style="color:#719e07">=</span> String.format(<span style="color:#2aa198">&#34;only 1 %s file is expected, but %d dubbo.properties files found on class path: %s&#34;</span>, |
| </span></span><span style="display:flex;"><span> fileName, set.size(), set); |
| </span></span><span style="display:flex;"><span> logger.warn(errMsg); |
| </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">// fall back to use method getResourceAsStream</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> properties.load(ClassUtils.getClassLoader().getResourceAsStream(fileName)); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable e) { |
| </span></span><span style="display:flex;"><span> logger.warn(<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.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> properties; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> logger.info(<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></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (java.net.URL url : set) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> Properties p <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties(); |
| </span></span><span style="display:flex;"><span> InputStream input <span style="color:#719e07">=</span> url.openStream(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (input <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> p.load(input); |
| </span></span><span style="display:flex;"><span> properties.putAll(p); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">finally</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> input.close(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </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> (Throwable e) { |
| </span></span><span style="display:flex;"><span> logger.warn(<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.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">return</span> properties; |
| </span></span><span style="display:flex;"><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></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>(String key) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> System.getProperty(key); |
| </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">public</span> Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getProperties</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (Map) System.getProperties(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></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>(String key) { |
| </span></span><span style="display:flex;"><span> String value <span style="color:#719e07">=</span> System.getenv(key); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(value)) { |
| </span></span><span style="display:flex;"><span> value <span style="color:#719e07">=</span> System.getenv(StringUtils.toOSStyleKey(key)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> 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:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getProperties</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> System.getenv(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String name; |
| </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:#268bd2">private</span> Map<span style="color:#719e07">&lt;</span>String, 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></span><span style="display:flex;"><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>(String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.name <span style="color:#719e07">=</span> name; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">InmemoryConfiguration</span>(Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> properties) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.setProperties(properties); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Object <span style="color:#268bd2">getInternalProperty</span>(String key) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> store.get(key); |
| </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">/** |
| </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>(String key, String value) { |
| </span></span><span style="display:flex;"><span> store.put(key, 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">/** |
| </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>(Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> properties) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (properties <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.store.putAll(properties); |
| </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">/** |
| </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>(Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> properties) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (properties <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.store <span style="color:#719e07">=</span> properties; |
| </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">public</span> Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getProperties</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> store; |
| </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><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></span><span style="display:flex;"><span> <span style="color:#586e75">//JVM参数的dubbo.migration.file配置</span> |
| </span></span><span style="display:flex;"><span> String path <span style="color:#719e07">=</span> System.getProperty(CommonConstants.DUBBO_MIGRATION_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(path)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//环境变量的dubbo.migration.file配置</span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> System.getenv(CommonConstants.DUBBO_MIGRATION_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(path)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//默认的迁移配置文件 dubbo-migration.yaml</span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> CommonConstants.DEFAULT_DUBBO_MIGRATION_FILE; |
| </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">this</span>.localMigrationRule <span style="color:#719e07">=</span> ConfigUtils.loadMigrationRule(scopeModel.getClassLoaders(), path); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//发布器还是不处理配置加载的逻辑还是交给配置管理器</span> |
| </span></span><span style="display:flex;"><span> configManager.loadConfigs(); |
| </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">@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></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">// load dubbo.applications.xxx</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载应用配置</span> |
| </span></span><span style="display:flex;"><span> loadConfigsOfTypeFromProps(ApplicationConfig.class); |
| </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></span><span style="display:flex;"><span> loadConfigsOfTypeFromProps(MonitorConfig.class); |
| </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></span><span style="display:flex;"><span> loadConfigsOfTypeFromProps(MetricsConfig.class); |
| </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">// load dubbo.protocols.xxx</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载协议配置</span> |
| </span></span><span style="display:flex;"><span> loadConfigsOfTypeFromProps(ProtocolConfig.class); |
| </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> loadConfigsOfTypeFromProps(RegistryConfig.class); |
| </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></span><span style="display:flex;"><span> loadConfigsOfTypeFromProps(MetadataReportConfig.class); |
| </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">//loadConfigsOfTypeFromProps(ConfigCenterConfig.class);</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> refreshAll(); |
| </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> checkConfigs(); |
| </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:#719e07">if</span> (StringUtils.isBlank(applicationModel.getModelName())) { |
| </span></span><span style="display:flex;"><span> applicationModel.setModelName(applicationModel.getApplicationName()); |
| </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/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></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">//加载应用程序配置 (配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置,也可能是多应用,多注册中心这样的配置)</span> |
| </span></span><span style="display:flex;"><span> configManager.loadConfigsOfTypeFromProps(ApplicationConfig.class); |
| </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:#719e07">if</span> (StringUtils.isBlank(applicationModel.getModelName())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置一下模块名字和模块描述(我们再Debug里面经常会看到这个描述信息 toString直接返回了Dubbo为我们改造的对象信息)</span> |
| </span></span><span style="display:flex;"><span> applicationModel.setModelName(applicationModel.tryGetApplicationName()); |
| </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">// load config centers</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载配置中心配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置,也可能是多应用,多注册中心这样的配置)</span> |
| </span></span><span style="display:flex;"><span> configManager.loadConfigsOfTypeFromProps(ConfigCenterConfig.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//出于兼容性目的,如果没有明确指定配置中心,并且registryConfig的UseAConfigCenter为null或true,请使用registry作为默认配置中心</span> |
| </span></span><span style="display:flex;"><span> useRegistryAsConfigCenterIfNecessary(); |
| </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></span><span style="display:flex;"><span> Collection<span style="color:#719e07">&lt;</span>ConfigCenterConfig<span style="color:#719e07">&gt;</span> configCenters <span style="color:#719e07">=</span> configManager.getConfigCenters(); |
| </span></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">if</span> (CollectionUtils.isEmpty(configCenters)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心不存在的配置刷新</span> |
| </span></span><span style="display:flex;"><span> ConfigCenterConfig configCenterConfig <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConfigCenterConfig(); |
| </span></span><span style="display:flex;"><span> configCenterConfig.setScopeModel(applicationModel); |
| </span></span><span style="display:flex;"><span> configCenterConfig.refresh(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//验证配置</span> |
| </span></span><span style="display:flex;"><span> ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (configCenterConfig.isValid()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置合法则将配置放入配置管理器中</span> |
| </span></span><span style="display:flex;"><span> configManager.addConfigCenter(configCenterConfig); |
| </span></span><span style="display:flex;"><span> configCenters <span style="color:#719e07">=</span> configManager.getConfigCenters(); |
| </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">//一个或者多个配置中心配置存在的情况下的配置刷新</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ConfigCenterConfig configCenterConfig : configCenters) { |
| </span></span><span style="display:flex;"><span> configCenterConfig.refresh(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//验证配置</span> |
| </span></span><span style="display:flex;"><span> ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig); |
| </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">//配置中心配置不为空则将配置中心配置添加到environment中</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(configCenters)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//多配置中心本地动态配置对象创建CompositeDynamicConfiguration</span> |
| </span></span><span style="display:flex;"><span> CompositeDynamicConfiguration compositeDynamicConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CompositeDynamicConfiguration(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置中心的相关配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ConfigCenterConfig configCenter : configCenters) { |
| </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></span><span style="display:flex;"><span> environment.updateExternalConfigMap(configCenter.getExternalConfiguration()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置中心的应用配置,添加到环境里面</span> |
| </span></span><span style="display:flex;"><span> environment.updateAppExternalConfigMap(configCenter.getAppExternalConfiguration()); |
| </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></span><span style="display:flex;"><span> compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter)); |
| </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> environment.setDynamicConfiguration(compositeDynamicConfiguration); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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.loadConfigsOfTypeFromProps(ApplicationConfig.class); |
| </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>(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> cls) { |
| </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></span><span style="display:flex;"><span> PropertiesConfiguration properties <span style="color:#719e07">=</span> environment.getPropertiesConfiguration(); |
| </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">//多注册中心配置id查询</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"> 搜索属性并提取指定类型的配置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>.getConfigIdsFromProps(cls); |
| </span></span><span style="display:flex;"><span> configIds.forEach(id <span style="color:#719e07">-&gt;</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:#719e07">if</span> (<span style="color:#719e07">!</span><span style="color:#719e07">this</span>.getConfig(cls, id).isPresent()) { |
| </span></span><span style="display:flex;"><span> T config; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建配置对象 为配置对象初始化配置id</span> |
| </span></span><span style="display:flex;"><span> config <span style="color:#719e07">=</span> createConfig(cls, scopeModel); |
| </span></span><span style="display:flex;"><span> config.setId(id); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </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;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.getSimpleName()); |
| </span></span><span style="display:flex;"><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></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></span><span style="display:flex;"><span> <span style="color:#719e07">try</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> 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.getPluralTagName(cls) <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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (properties.getProperty(key) <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> properties.setProperty(key, id); |
| </span></span><span style="display:flex;"><span> addDefaultNameConfig <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新配置信息 好理解点就是Dubbo配置属性重写 </span> |
| </span></span><span style="display:flex;"><span> config.refresh(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将当前配置信息添加到配置缓存中configsCache成员变量</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.addConfig(config); |
| </span></span><span style="display:flex;"><span> tmpConfigs.add(config); |
| </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;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.getSimpleName(), e); |
| </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;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.getSimpleName()); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">finally</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (addDefaultNameConfig <span style="color:#719e07">&amp;&amp;</span> key <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> properties.remove(key); |
| </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 none config of the type, try load single config</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果没有该类型的配置,请尝试加载单个配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.getConfigs(cls).isEmpty()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// load single config</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;&gt;</span> configurationMaps <span style="color:#719e07">=</span> environment.getConfigurationMaps(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ConfigurationUtils.hasSubProperties(configurationMaps, AbstractConfig.getTypePrefix(cls))) { |
| </span></span><span style="display:flex;"><span> T config; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> config <span style="color:#719e07">=</span> createConfig(cls, scopeModel); |
| </span></span><span style="display:flex;"><span> config.refresh(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </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;create default config instance failed, type:&#34;</span> <span style="color:#719e07">+</span> cls.getSimpleName()); |
| </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">this</span>.addConfig(config); |
| </span></span><span style="display:flex;"><span> tmpConfigs.add(config); |
| </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">return</span> tmpConfigs; |
| </span></span><span style="display:flex;"><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></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">//我们使用DynamicConfiguration的加载状态来决定是否已启动ConfigCenter。配置中心配置加载完成之后会初始化动态配置defaultDynamicConfiguration</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (environment.getDynamicConfiguration().isPresent()) { |
| </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">//从配置缓存中查询是否存在config-center相关配置 ,如果已经存在配置了就无需使用注册中心的配置地址直接返回</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(configManager.getConfigCenters())) { |
| </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></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></span><span style="display:flex;"><span> configManager.loadConfigsOfTypeFromProps(RegistryConfig.class); |
| </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> List<span style="color:#719e07">&lt;</span>RegistryConfig<span style="color:#719e07">&gt;</span> defaultRegistries <span style="color:#719e07">=</span> configManager.getDefaultRegistries(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在注册中心</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (defaultRegistries.size() <span style="color:#719e07">&gt;</span> 0) { |
| </span></span><span style="display:flex;"><span> defaultRegistries |
| </span></span><span style="display:flex;"><span> .stream() |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//判断当前注册中心是否可以作为配置中心</span> |
| </span></span><span style="display:flex;"><span> .filter(<span style="color:#719e07">this</span>::isUsedRegistryAsConfigCenter) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将注册中心配置映射转换为配置中心</span> |
| </span></span><span style="display:flex;"><span> .map(<span style="color:#719e07">this</span>::registryAsConfigCenter) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历配置中心流</span> |
| </span></span><span style="display:flex;"><span> .forEach(configCenter <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (configManager.getConfigCenter(configCenter.getId()).isPresent()) { |
| </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">//配置管理器中添加配置中心,方便后去读取配置中心的配置信息</span> |
| </span></span><span style="display:flex;"><span> configManager.addConfigCenter(configCenter); |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;use registry as config-center: &#34;</span> <span style="color:#719e07">+</span> configCenter); |
| </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><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>(RegistryConfig registryConfig, Supplier<span style="color:#719e07">&lt;</span>Boolean<span style="color:#719e07">&gt;</span> usedRegistryAsCenter, |
| </span></span><span style="display:flex;"><span> String centerType, |
| </span></span><span style="display:flex;"><span> Class<span style="color:#719e07">&lt;?&gt;</span> extensionClass) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> <span style="color:#dc322f">boolean</span> supported; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个useAsConfigCenter参数是来自注册中心的配置 如果配置了这个值则以这个值为准,如果配置了false则这个注册中心不能做为配置中心</span> |
| </span></span><span style="display:flex;"><span> Boolean configuredValue <span style="color:#719e07">=</span> usedRegistryAsCenter.get(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (configuredValue <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { <span style="color:#586e75">// If configured, take its value.</span> |
| </span></span><span style="display:flex;"><span> supported <span style="color:#719e07">=</span> configuredValue.booleanValue(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { <span style="color:#586e75">// Or check the extension existence</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个逻辑的话是判断下注册中心的协议是否满足要求,我们例子代码中使用的是zookeeper</span> |
| </span></span><span style="display:flex;"><span> String protocol <span style="color:#719e07">=</span> registryConfig.getProtocol(); |
| </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">//动态配置的扩展类型为:interface org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zookeeper协议肯定是支持的因为zookeeper协议实现了这个动态配置工厂 ,这个扩展类型为ZookeeperDynamicConfigurationFactory</span> |
| </span></span><span style="display:flex;"><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> supported <span style="color:#719e07">=</span> supportsExtension(extensionClass, protocol); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心走注册中心会打印一条日志</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(format(<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> , extensionClass.getSimpleName(), protocol, supported <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;supports&#34;</span> : <span style="color:#2aa198">&#34;does not support&#34;</span>, centerType)); |
| </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">//配置中心走注册中心会打印一条日志</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(format(<span style="color:#2aa198">&#34;The registry[%s] will be %s as the %s center&#34;</span>, registryConfig, |
| </span></span><span style="display:flex;"><span> supported <span style="color:#719e07">?</span> <span style="color:#2aa198">&#34;used&#34;</span> : <span style="color:#2aa198">&#34;not used&#34;</span>, centerType)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> supported; |
| </span></span><span style="display:flex;"><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>(RegistryConfig registryConfig) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心协议获取这里例子中的是zookeeper协议</span> |
| </span></span><span style="display:flex;"><span> String protocol <span style="color:#719e07">=</span> registryConfig.getProtocol(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心端口 2181</span> |
| </span></span><span style="display:flex;"><span> Integer port <span style="color:#719e07">=</span> registryConfig.getPort(); |
| </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> URL url <span style="color:#719e07">=</span> URL.valueOf(registryConfig.getAddress(), registryConfig.getScopeModel()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//生成当前配置中心的id 封装之后的内容为:</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> 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.getHost() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;-&#34;</span> <span style="color:#719e07">+</span> port; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置中心配置对象创建</span> |
| </span></span><span style="display:flex;"><span> ConfigCenterConfig cc <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConfigCenterConfig(); |
| </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> cc.setId(id); |
| </span></span><span style="display:flex;"><span> cc.setScopeModel(applicationModel); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (cc.getParameters() <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> cc.setParameters(<span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;</span>()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmptyMap(registryConfig.getParameters())) { |
| </span></span><span style="display:flex;"><span> cc.getParameters().putAll(registryConfig.getParameters()); <span style="color:#586e75">// copy the parameters</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> cc.getParameters().put(CLIENT_KEY, registryConfig.getClient()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zookeeper</span> |
| </span></span><span style="display:flex;"><span> cc.setProtocol(protocol); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//2181</span> |
| </span></span><span style="display:flex;"><span> cc.setPort(port); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isNotEmpty(registryConfig.getGroup())) { |
| </span></span><span style="display:flex;"><span> cc.setGroup(registryConfig.getGroup()); |
| </span></span><span style="display:flex;"><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> cc.setAddress(getRegistryCompatibleAddress(registryConfig)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册中心分组做为配置中心命名空间 这里为null</span> |
| </span></span><span style="display:flex;"><span> cc.setNamespace(registryConfig.getGroup()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zk认证信息</span> |
| </span></span><span style="display:flex;"><span> cc.setUsername(registryConfig.getUsername()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//zk认证信息</span> |
| </span></span><span style="display:flex;"><span> cc.setPassword(registryConfig.getPassword()); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (registryConfig.getTimeout() <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> cc.setTimeout(registryConfig.getTimeout().longValue()); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> cc.setHighestPriority(<span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cc; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> refreshed.set(<span style="color:#cb4b16">true</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">// check and init before do refresh</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新之前执行的逻辑 这里并做什么逻辑</span> |
| </span></span><span style="display:flex;"><span> preProcessRefresh(); |
| </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> Environment environment <span style="color:#719e07">=</span> getScopeModel().getModelEnvironment(); |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;&gt;</span> configurationMaps <span style="color:#719e07">=</span> environment.getConfigurationMaps(); |
| </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> String preferredPrefix <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (String prefix : getPrefixes()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ConfigurationUtils.hasSubProperties(configurationMaps, prefix)) { |
| </span></span><span style="display:flex;"><span> preferredPrefix <span style="color:#719e07">=</span> prefix; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</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">if</span> (preferredPrefix <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> preferredPrefix <span style="color:#719e07">=</span> getPrefixes().get(0); |
| </span></span><span style="display:flex;"><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> Collection<span style="color:#719e07">&lt;</span>Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;&gt;</span> instanceConfigMaps <span style="color:#719e07">=</span> environment.getConfigurationMaps(<span style="color:#719e07">this</span>, preferredPrefix); |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> subProperties <span style="color:#719e07">=</span> ConfigurationUtils.getSubProperties(instanceConfigMaps, preferredPrefix); |
| </span></span><span style="display:flex;"><span> InmemoryConfiguration subPropsConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration(subProperties); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isDebugEnabled()) { |
| </span></span><span style="display:flex;"><span> String idOrName <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;&#34;</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.hasText(<span style="color:#719e07">this</span>.getId())) { |
| </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>.getId() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;]&#34;</span>; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> ReflectUtils.getProperty(<span style="color:#719e07">this</span>, <span style="color:#2aa198">&#34;getName&#34;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.hasText(name)) { |
| </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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> logger.debug(<span style="color:#2aa198">&#34;Refreshing &#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span>.getClass().getSimpleName() <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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> assignProperties(<span style="color:#719e07">this</span>, environment, subProperties, subPropsConfiguration); |
| </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> processExtraRefresh(preferredPrefix, subPropsConfiguration); |
| </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 override field value of config bean: &#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span>, e); |
| </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;Failed to override field value of config bean: &#34;</span> <span style="color:#719e07">+</span> <span style="color:#719e07">this</span>, e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> postProcessRefresh(); |
| </span></span><span style="display:flex;"><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></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>(String<span style="color:#719e07">[]</span> args) <span style="color:#268bd2">throws</span> Exception { |
| </span></span><span style="display:flex;"><span> startWithBootstrap(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制</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> <span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了</span> |
| </span></span><span style="display:flex;"><span> service.setInterface(DemoService.class); |
| </span></span><span style="display:flex;"><span> service.setRef(<span style="color:#719e07">new</span> DemoServiceImpl()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一个篇章主要说这里:</span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap.getInstance(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化应用配置</span> |
| </span></span><span style="display:flex;"><span> bootstrap.application(<span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>)) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化注册中心配置</span> |
| </span></span><span style="display:flex;"><span> .registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span>)) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化协议配置</span> |
| </span></span><span style="display:flex;"><span> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化服务配置</span> |
| </span></span><span style="display:flex;"><span> .service(service) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动</span> |
| </span></span><span style="display:flex;"><span> .start() |
| </span></span><span style="display:flex;"><span> .await(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//调用重载的方法进行启动参数代表是否等待启动结束</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.start(<span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span>; |
| </span></span><span style="display:flex;"><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:#dc322f">boolean</span> wait) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个发布器是在ApplicationModel对象创建之后初始化的时候进行初始化的具体类型为DefaultApplicationDeployer</span> |
| </span></span><span style="display:flex;"><span> Future future <span style="color:#719e07">=</span> applicationDeployer.start(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (wait) { |
| </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> future.get(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动失败则抛出一个异常</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;await dubbo application start finish failure&#34;</span>, e); |
| </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> <span style="color:#719e07">this</span>; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//启动锁,防止重复启动</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (startLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//发布器,状态已经设置为停止或者失败了就直接抛出异常</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isStopping() <span style="color:#719e07">||</span> isStopped() <span style="color:#719e07">||</span> isFailed()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is stopping or stopped, can not start again&#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:#719e07">try</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">//可能在添加新模块后再次调用start,检查是否有任何新模块</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里遍历当前应用程序下的所有模块如果某个模块是PENDING状态则这里hasPendingModule的值为true</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> hasPendingModule <span style="color:#719e07">=</span> hasPendingModule(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//发布器状态正在启动中</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isStarting()) { |
| </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">// if it has new modules, start them</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在挂器的模块</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (hasPendingModule) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动模块</span> |
| </span></span><span style="display:flex;"><span> startModules(); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture; |
| </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 is started and no new module, just 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">if</span> (isStarted() <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>hasPendingModule) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture.completedFuture(<span style="color:#cb4b16">false</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">// pending -&gt; starting : first start app</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// started -&gt; starting : re-start app</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动状态切换,将启动状态切换到STARTING(pending和started状态无需切换)</span> |
| </span></span><span style="display:flex;"><span> onStarting(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//核心初始化逻辑,这里主要做一些应用级别启动比如配置中心,元数据中心</span> |
| </span></span><span style="display:flex;"><span> initialize(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动模块(我们的服务提供和服务引用是在这个模块级别的)</span> |
| </span></span><span style="display:flex;"><span> doStart(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable e) { |
| </span></span><span style="display:flex;"><span> onFailed(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; start failure&#34;</span>, e); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e; |
| </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> startFuture; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//状态判断 如果已经初始化过了就直接返回</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (initialized) { |
| </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">// Ensure that the initialization is completed when concurrent calls</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动锁,确保在并发调用时完成初始化</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (startLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁 如果已经初始化过了就直接返回</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (initialized) { |
| </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">// register shutdown hook</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注册关闭钩子,这个逻辑基本每个中间件应用都必须要要做的事情了,正常关闭应用回收资源,一般没这个逻辑情况下容易出现一些异常,让我们开发人员很疑惑,而这个逻辑往往并不好处理的干净。</span> |
| </span></span><span style="display:flex;"><span> registerShutdownHook(); |
| </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> startConfigCenter(); |
| </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> loadApplicationConfigs(); |
| </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> initModuleDeployers(); |
| </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></span><span style="display:flex;"><span> startMetadataCenter(); |
| </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> initialized <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (logger.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> logger.info(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; has been initialized!&#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>这个是个生命周期整体概览的方法,将具体逻辑拆分到各个子方法中,是代码重构的一种策略,上面注释也很清楚了就不细说了,上面每个方法在后面会有单独的博客来分析。</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></span><span style="display:flex;"><span> <span style="color:#586e75">// 启动模块</span> |
| </span></span><span style="display:flex;"><span> startModules(); |
| </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></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">//确保初始化并首先启动内部模块,Dubbo3中将模块分为内部和外部,内部是核心代码已经提供的一些服务比如元数据服务,外部是我们自己写的服务</span> |
| </span></span><span style="display:flex;"><span> prepareInternalModule(); |
| </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></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ModuleModel moduleModel : <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(applicationModel.getModuleModels())) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个状态默认就是PENDING的</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (moduleModel.getDeployer().isPending()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块启动器,发布服务</span> |
| </span></span><span style="display:flex;"><span> moduleModel.getDeployer().start(); |
| </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>这个模块的启动其实就是用来启动服务的 先启动内部服务,再启动外部服务 |
| 内部服务有个元数据服务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:#268bd2">throws</span> IllegalStateException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块发布器已经停止或者启动失败则直接抛出异常返回</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isStopping() <span style="color:#719e07">||</span> isStopped() <span style="color:#719e07">||</span> isFailed()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalStateException(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is stopping or stopped, can not start again&#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:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//启动中或者已经启动了则直接返回一个Future对象 </span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isStarting() <span style="color:#719e07">||</span> isStarted()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//切换模块启动状态为STARTING</span> |
| </span></span><span style="display:flex;"><span> onModuleStarting(); |
| </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></span><span style="display:flex;"><span> applicationDeployer.initialize(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//模块发布器进行初始化</span> |
| </span></span><span style="display:flex;"><span> initialize(); |
| </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></span><span style="display:flex;"><span> exportServices(); |
| </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">// exclude internal module to avoid wait itself</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (moduleModel <span style="color:#719e07">!=</span> moduleModel.getApplicationModel().getInternalModule()) { |
| </span></span><span style="display:flex;"><span> applicationDeployer.prepareInternalModule(); |
| </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">// refer services</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//引用服务</span> |
| </span></span><span style="display:flex;"><span> referServices(); |
| </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">//非异步启动则直接切换状态为STARTED</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (asyncExportingFutures.isEmpty() <span style="color:#719e07">&amp;&amp;</span> asyncReferringFutures.isEmpty()) { |
| </span></span><span style="display:flex;"><span> onModuleStarted(); |
| </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> frameworkExecutorRepository.getSharedExecutor().submit(() <span style="color:#719e07">-&gt;</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">// wait for export finish</span> |
| </span></span><span style="display:flex;"><span> waitExportFinish(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// wait for refer finish</span> |
| </span></span><span style="display:flex;"><span> waitReferFinish(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable e) { |
| </span></span><span style="display:flex;"><span> logger.warn(<span style="color:#2aa198">&#34;wait for export/refer services occurred an exception&#34;</span>, e); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">finally</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//异步回调完成 所有服务都启动了,再切换状态</span> |
| </span></span><span style="display:flex;"><span> onModuleStarted(); |
| </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> (Throwable e) { |
| </span></span><span style="display:flex;"><span> onModuleFailed(getIdentifier() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; start failed: &#34;</span> <span style="color:#719e07">+</span> e, e); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> e; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> startFuture; |
| </span></span><span style="display:flex;"><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></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></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></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></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></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></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></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></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.getInstance(); |
| </span></span><span style="display:flex;"><span> bootstrap.application(<span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </span></span><span style="display:flex;"><span> .service(service) |
| </span></span><span style="display:flex;"><span> .start() |
| </span></span><span style="display:flex;"><span> .await(); |
| </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>.protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </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> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </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>(ProtocolConfig protocolConfig) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置信息转List</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> protocols(singletonList(protocolConfig)); |
| </span></span><span style="display:flex;"><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>(List<span style="color:#719e07">&lt;</span>ProtocolConfig<span style="color:#719e07">&gt;</span> protocolConfigs) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isEmpty(protocolConfigs)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ProtocolConfig protocolConfig : protocolConfigs) { |
| </span></span><span style="display:flex;"><span> protocolConfig.setScopeModel(applicationModel); |
| </span></span><span style="display:flex;"><span> configManager.addProtocol(protocolConfig); |
| </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></span><span style="display:flex;"><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.getInstance(); |
| </span></span><span style="display:flex;"><span> bootstrap.application(<span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </span></span><span style="display:flex;"><span> .service(service) |
| </span></span><span style="display:flex;"><span> .start() |
| </span></span><span style="display:flex;"><span> .await(); |
| </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">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</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>.registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</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>(String address) { |
| </span></span><span style="display:flex;"><span> setAddress(address); |
| </span></span><span style="display:flex;"><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>(String address) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//保存地址</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.address <span style="color:#719e07">=</span> address; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//下面是支持将参数在url地址后面 比如用户名,密码,协议,端口,这几个参数提前做解析放入成员变量中</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (address <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</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">//地址转Dubbo的URL对象 这个URL是Dubbo自行实现的URL封装信息的类型</span> |
| </span></span><span style="display:flex;"><span> URL url <span style="color:#719e07">=</span> URL.valueOf(address); |
| </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">//值不存在时候更新属性,非常巧妙的代码 重构了多个if判断</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一个参数值不存在则调用第二个方法,第二个方法的参数为第三方方法 </span> |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent(<span style="color:#719e07">this</span>::getUsername, <span style="color:#719e07">this</span>::setUsername, url.getUsername()); |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent(<span style="color:#719e07">this</span>::getPassword, <span style="color:#719e07">this</span>::setPassword, url.getPassword()); |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent(<span style="color:#719e07">this</span>::getProtocol, <span style="color:#719e07">this</span>::setProtocol, url.getProtocol()); |
| </span></span><span style="display:flex;"><span> updatePropertyIfAbsent(<span style="color:#719e07">this</span>::getPort, <span style="color:#719e07">this</span>::setPort, url.getPort()); |
| </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> Map<span style="color:#719e07">&lt;</span>String, String<span style="color:#719e07">&gt;</span> params <span style="color:#719e07">=</span> url.getParameters(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmptyMap(params)) { |
| </span></span><span style="display:flex;"><span> params.remove(BACKUP_KEY); |
| </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> updateParameters(params); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception ignored) { |
| </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>然后再回过头来看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>(RegistryConfig registryConfig) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将applicationModel对象设置给注册中心配置对象</span> |
| </span></span><span style="display:flex;"><span> registryConfig.setScopeModel(applicationModel); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将注册中心配置对象添加到配置管理器中</span> |
| </span></span><span style="display:flex;"><span> configManager.addRegistry(registryConfig); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span>; |
| </span></span><span style="display:flex;"><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>(RegistryConfig registryConfig) { |
| </span></span><span style="display:flex;"><span> addConfig(registryConfig); |
| </span></span><span style="display:flex;"><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>(AbstractConfig config) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig </span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isSupportConfigType(config.getClass())) { |
| </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;Unsupported config type: &#34;</span> <span style="color:#719e07">+</span> config); |
| </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">if</span> (config.getScopeModel() <span style="color:#719e07">!=</span> scopeModel) { |
| </span></span><span style="display:flex;"><span> config.setScopeModel(scopeModel); |
| </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> Map<span style="color:#719e07">&lt;</span>String, AbstractConfig<span style="color:#719e07">&gt;</span> configsMap <span style="color:#719e07">=</span> configsCache.computeIfAbsent(getTagName(config.getClass()), 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">// fast check duplicated equivalent config before write lock</span> |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (AbstractConfig value : configsMap.values()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (value.equals(config)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) value; |
| </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">// lock by config type</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//添加配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (configsMap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) addIfAbsent(config, configsMap); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(C config, Map<span style="color:#719e07">&lt;</span>String, C<span style="color:#719e07">&gt;</span> configsMap) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">throws</span> IllegalStateException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置信息为空直接返回</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config; |
| </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">// find by value</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据配置规则判断,配置存在则返回</span> |
| </span></span><span style="display:flex;"><span> Optional<span style="color:#719e07">&lt;</span>C<span style="color:#719e07">&gt;</span> prevConfig <span style="color:#719e07">=</span> findDuplicatedConfig(configsMap, config); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (prevConfig.isPresent()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> prevConfig.get(); |
| </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">//生成配置key</span> |
| </span></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> config.getId(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (key <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">do</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> key <span style="color:#719e07">=</span> generateConfigId(config); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">while</span> (configsMap.containsKey(key)); |
| </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">//不相同的配置key重复则抛出异常</span> |
| </span></span><span style="display:flex;"><span> C existedConfig <span style="color:#719e07">=</span> configsMap.get(key); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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(existedConfig, config)) { |
| </span></span><span style="display:flex;"><span> String type <span style="color:#719e07">=</span> config.getClass().getSimpleName(); |
| </span></span><span style="display:flex;"><span> logger.warn(String.format(<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></span><span style="display:flex;"><span> type, type, type, type, key, existedConfig, config)); |
| </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 existed config if any</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置对象存入configsMap对象中,configsMap来源于configsCache</span> |
| </span></span><span style="display:flex;"><span> configsMap.put(key, config); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config; |
| </span></span><span style="display:flex;"><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.getInstance(); |
| </span></span><span style="display:flex;"><span> bootstrap.application(<span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </span></span><span style="display:flex;"><span> .service(service) |
| </span></span><span style="display:flex;"><span> .start() |
| </span></span><span style="display:flex;"><span> .await(); |
| </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.application(<span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</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.getInstance(); |
| </span></span><span style="display:flex;"><span> bootstrap.application(<span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</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>(ApplicationConfig applicationConfig) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将启动器构造器中初始化的默认应用程序模型对象传递给配置对象</span> |
| </span></span><span style="display:flex;"><span> applicationConfig.setScopeModel(applicationModel); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置信息添加到配置管理器中</span> |
| </span></span><span style="display:flex;"><span> configManager.setApplication(applicationConfig); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span>; |
| </span></span><span style="display:flex;"><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>(ApplicationConfig application) { |
| </span></span><span style="display:flex;"><span> addConfig(application); |
| </span></span><span style="display:flex;"><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>(AbstractConfig config) { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (config <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig,</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isSupportConfigType(config.getClass())) { |
| </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;Unsupported config type: &#34;</span> <span style="color:#719e07">+</span> config); |
| </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">if</span> (config.getScopeModel() <span style="color:#719e07">!=</span> scopeModel) { |
| </span></span><span style="display:flex;"><span> config.setScopeModel(scopeModel); |
| </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">//缓存中是否存在</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, AbstractConfig<span style="color:#719e07">&gt;</span> configsMap <span style="color:#719e07">=</span> configsCache.computeIfAbsent(getTagName(config.getClass()), 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></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (AbstractConfig value : configsMap.values()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (value.equals(config)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) value; |
| </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">// lock by config type</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//添加配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (configsMap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (T) addIfAbsent(config, configsMap); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(C config, Map<span style="color:#719e07">&lt;</span>String, C<span style="color:#719e07">&gt;</span> configsMap) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">throws</span> IllegalStateException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//配置信息为空直接返回</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config; |
| </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">// find by value</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据配置规则判断,配置存在则返回</span> |
| </span></span><span style="display:flex;"><span> Optional<span style="color:#719e07">&lt;</span>C<span style="color:#719e07">&gt;</span> prevConfig <span style="color:#719e07">=</span> findDuplicatedConfig(configsMap, config); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (prevConfig.isPresent()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> prevConfig.get(); |
| </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">//生成配置key</span> |
| </span></span><span style="display:flex;"><span> String key <span style="color:#719e07">=</span> config.getId(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (key <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">do</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> key <span style="color:#719e07">=</span> generateConfigId(config); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">while</span> (configsMap.containsKey(key)); |
| </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">//不相同的配置key重复则抛出异常</span> |
| </span></span><span style="display:flex;"><span> C existedConfig <span style="color:#719e07">=</span> configsMap.get(key); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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(existedConfig, config)) { |
| </span></span><span style="display:flex;"><span> String type <span style="color:#719e07">=</span> config.getClass().getSimpleName(); |
| </span></span><span style="display:flex;"><span> logger.warn(String.format(<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></span><span style="display:flex;"><span> type, type, type, type, key, existedConfig, config)); |
| </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 existed config if any</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将配置对象存入configsMap对象中,configsMap来源于configsCache</span> |
| </span></span><span style="display:flex;"><span> configsMap.put(key, config); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> config; |
| </span></span><span style="display:flex;"><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></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>(String<span style="color:#719e07">[]</span> args) <span style="color:#268bd2">throws</span> Exception { |
| </span></span><span style="display:flex;"><span> startWithBootstrap(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制</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> <span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了</span> |
| </span></span><span style="display:flex;"><span> service.setInterface(DemoService.class); |
| </span></span><span style="display:flex;"><span> service.setRef(<span style="color:#719e07">new</span> DemoServiceImpl()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这一个篇章主要说这里:</span> |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap.getInstance(); |
| </span></span><span style="display:flex;"><span> bootstrap.application(<span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </span></span><span style="display:flex;"><span> .service(service) |
| </span></span><span style="display:flex;"><span> .start() |
| </span></span><span style="display:flex;"><span> .await(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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.getInstance(); |
| </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></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁第一次判断空</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为空都进行排队</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (DubboBootstrap.class) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁第二次判断空 上面为空的都排队了这里得判断下</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//调用重载方法获取对象</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> DubboBootstrap.getInstance(ApplicationModel.defaultModel()); |
| </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">return</span> instance; |
| </span></span><span style="display:flex;"><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>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> instanceMap.computeIfAbsent(applicationModel, _k <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> DubboBootstrap(applicationModel)); |
| </span></span><span style="display:flex;"><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>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存储应用程序启动模型</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.applicationModel <span style="color:#719e07">=</span> applicationModel; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取配置管理器ConfigManager: 配置管理器的扩展类型ApplicationExt ,扩展名字config</span> |
| </span></span><span style="display:flex;"><span> configManager <span style="color:#719e07">=</span> applicationModel.getApplicationConfigManager(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取环境信息Environment: 环境信息的扩展类型为ApplicationExt,扩展名字为environment</span> |
| </span></span><span style="display:flex;"><span> environment <span style="color:#719e07">=</span> applicationModel.getModelEnvironment(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//执行器存储仓库(线程池)ExecutorRepository: 扩展类型为ExecutorRepository,默认扩展扩展名字为default</span> |
| </span></span><span style="display:flex;"><span> executorRepository <span style="color:#719e07">=</span> applicationModel.getExtensionLoader(ExecutorRepository.class).getDefaultExtension(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化并启动应用程序实例ApplicationDeployer,DefaultApplicationDeployer类型</span> |
| </span></span><span style="display:flex;"><span> applicationDeployer <span style="color:#719e07">=</span> applicationModel.getDeployer(); |
| </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></span><span style="display:flex;"><span> applicationDeployer.addDeployListener(<span style="color:#719e07">new</span> DeployListenerAdapter<span style="color:#719e07">&lt;</span>ApplicationModel<span style="color:#719e07">&gt;</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>(ApplicationModel scopeModel) { |
| </span></span><span style="display:flex;"><span> notifyStarted(applicationModel); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onStopped</span>(ApplicationModel scopeModel) { |
| </span></span><span style="display:flex;"><span> notifyStopped(applicationModel); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onFailure</span>(ApplicationModel scopeModel, Throwable cause) { |
| </span></span><span style="display:flex;"><span> notifyStopped(applicationModel); |
| </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">//将启动器对象注册到应用程序模型applicationModel的Bean工厂中</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// register DubboBootstrap bean</span> |
| </span></span><span style="display:flex;"><span> applicationModel.getBeanFactory().registerBean(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> filters <span style="color:#719e07">=</span> ScopeModelUtil.getExtensionLoader(Filter.class, moduleModels.get(0)).getActivateExtension(url, key, group); |
| </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>(URL url, String key) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getActivateExtension(url, key, <span style="color:#cb4b16">null</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:#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>(URL url, String key, String group) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从参数中获取url指定的值</span> |
| </span></span><span style="display:flex;"><span> String value <span style="color:#719e07">=</span> url.getParameter(key); |
| </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> getActivateExtension(url, StringUtils.isEmpty(value) <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> : COMMA_SPLIT_PATTERN.split(value), group); |
| </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:#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>(URL url, String<span style="color:#719e07">[]</span> values, String group) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查扩展加载器是否被销毁了</span> |
| </span></span><span style="display:flex;"><span> checkDestroyed(); |
| </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">//创建个有序的Map集合,用来对扩展进行排序</span> |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化扩展名字,指定了扩展名字values不为空</span> |
| </span></span><span style="display:flex;"><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) : asList(values); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展名字使用Set集合进行去重</span> |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//参数常量是 -default 扩展名字是否不包含默认的</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>namesSet.contains(REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> DEFAULT_KEY)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次进来肯定是没有缓存对象双重校验锁检查下</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (cachedActivateGroups.size() <span style="color:#719e07">==</span> 0) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (cachedActivateGroups) { |
| </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:#719e07">if</span> (cachedActivateGroups.size() <span style="color:#719e07">==</span> 0) { |
| </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> getExtensionClasses(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Map.Entry<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> entry : cachedActivates.entrySet()) { |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> entry.getKey(); |
| </span></span><span style="display:flex;"><span> Object activate <span style="color:#719e07">=</span> entry.getValue(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> activateGroup, activateValue; |
| </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:#719e07">if</span> (activate <span style="color:#719e07">instanceof</span> Activate) { |
| </span></span><span style="display:flex;"><span> activateGroup <span style="color:#719e07">=</span> ((Activate) activate).group(); |
| </span></span><span style="display:flex;"><span> activateValue <span style="color:#719e07">=</span> ((Activate) activate).value(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> (activate <span style="color:#719e07">instanceof</span> com.alibaba.dubbo.common.extension.Activate) { |
| </span></span><span style="display:flex;"><span> activateGroup <span style="color:#719e07">=</span> ((com.alibaba.dubbo.common.extension.Activate) activate).group(); |
| </span></span><span style="display:flex;"><span> activateValue <span style="color:#719e07">=</span> ((com.alibaba.dubbo.common.extension.Activate) activate).value(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</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> cachedActivateGroups.put(name, <span style="color:#719e07">new</span> HashSet<span style="color:#719e07">&lt;&gt;</span>(Arrays.asList(activateGroup))); |
| </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.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:#719e07">for</span> (<span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0; i <span style="color:#719e07">&lt;</span> activateValue.length; i<span style="color:#719e07">++</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (activateValue<span style="color:#719e07">[</span>i<span style="color:#719e07">]</span>.contains(<span style="color:#2aa198">&#34;:&#34;</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:#2aa198">&#34;:&#34;</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">else</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></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> cachedActivateValues.put(name, keyPairs); |
| </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">// traverse all cached extensions</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有激活的扩展名字和扩展分组集合</span> |
| </span></span><span style="display:flex;"><span> cachedActivateGroups.forEach((name, activateGroup) <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:#719e07">if</span> (isMatchGroup(group, activateGroup) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不能是指定的扩展名字</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet.contains(name) |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//也不能是带有 -指定扩展名字</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet.contains(REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> name) |
| </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">//如果未配置value属性则默认都是匹配的(cachedActivateValues中不存在对应扩展名字的缓存的时候默认为true)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> isActive(cachedActivateValues.get(name), url)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存激活的扩展类型映射的扩展名字</span> |
| </span></span><span style="display:flex;"><span> activateExtensionsMap.put(getExtensionClass(name), getExtension(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 style="color:#719e07">if</span> (namesSet.contains(DEFAULT_KEY)) { |
| </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">// `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> 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.size() <span style="color:#719e07">+</span> names.size()); |
| </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> 0; i <span style="color:#719e07">&lt;</span> names.size(); i<span style="color:#719e07">++</span>) { |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> names.get(i); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>name.startsWith(REMOVE_VALUE_PREFIX) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet.contains(REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>DEFAULT_KEY.equals(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (containsExtension(name)) { |
| </span></span><span style="display:flex;"><span> extensionsResult.add(getExtension(name)); |
| </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> extensionsResult.addAll(activateExtensionsMap.values()); |
| </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">return</span> extensionsResult; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</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:#719e07">for</span> (<span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0; i <span style="color:#719e07">&lt;</span> names.size(); i<span style="color:#719e07">++</span>) { |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> names.get(i); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>name.startsWith(REMOVE_VALUE_PREFIX) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>namesSet.contains(REMOVE_VALUE_PREFIX <span style="color:#719e07">+</span> name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>DEFAULT_KEY.equals(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (containsExtension(name)) { |
| </span></span><span style="display:flex;"><span> activateExtensionsMap.put(getExtensionClass(name), getExtension(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 style="color:#719e07">return</span> <span style="color:#719e07">new</span> ArrayList<span style="color:#719e07">&lt;&gt;</span>(activateExtensionsMap.values()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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> (clazz.isAnnotationPresent(Adaptive.class)) { |
| </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:#719e07">if</span> (isWrapperClass(clazz)) { |
| </span></span><span style="display:flex;"><span> cacheWrapperClass(clazz); |
| </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> (StringUtils.isEmpty(name)) { |
| </span></span><span style="display:flex;"><span> name <span style="color:#719e07">=</span> findAnnotationName(clazz); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (name.length() <span style="color:#719e07">==</span> 0) { |
| </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;No such extension name for the class &#34;</span> <span style="color:#719e07">+</span> clazz.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in the config &#34;</span> <span style="color:#719e07">+</span> resourceURL); |
| </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> String<span style="color:#719e07">[]</span> names <span style="color:#719e07">=</span> NAME_SEPARATOR.split(name); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ArrayUtils.isNotEmpty(names)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//位置在这里其他地方就不标记注释了,前面判断了如果不是Adaptive也不是Wrapper类型则我们可以来判断是否为Activate 类型如果是的话调用cacheActivateClass方法将扩展缓存进cachedActivates缓存中</span> |
| </span></span><span style="display:flex;"><span> cacheActivateClass(clazz, 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> (String n : names) { |
| </span></span><span style="display:flex;"><span> cacheName(clazz, n); |
| </span></span><span style="display:flex;"><span> saveInExtensionClass(extensionClasses, clazz, n, overridden); |
| </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><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size: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>(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.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (activate <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注解存在则加入激活注解缓存</span> |
| </span></span><span style="display:flex;"><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> com.alibaba.dubbo.common.extension.Activate oldActivate <span style="color:#719e07">=</span> clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (oldActivate <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </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>原文: <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> (ConfigManager) <span style="color:#719e07">this</span>.getExtensionLoader(ApplicationExt.class) |
| </span></span><span style="display:flex;"><span> .getExtension(ConfigManager.NAME); |
| </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>(String name, <span style="color:#dc322f">boolean</span> wrap) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查扩展加载器是否已被销毁</span> |
| </span></span><span style="display:flex;"><span> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(name)) { |
| </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;Extension name == null&#34;</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展名字为true则加载默认扩展</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#2aa198">&#34;true&#34;</span>.equals(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> getDefaultExtension(); |
| </span></span><span style="display:flex;"><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">//wrap是aop机制 俗称切面,这个origin在aop里面可以称为切点,下面的wrap扩展可以称为增强通知的类型,普通扩展和wrap扩展的扩展名字是一样的</span> |
| </span></span><span style="display:flex;"><span> String cacheKey <span style="color:#719e07">=</span> name; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>wrap) { |
| </span></span><span style="display:flex;"><span> cacheKey <span style="color:#719e07">+=</span> <span style="color:#2aa198">&#34;_origin&#34;</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从cachedInstances缓存中查询</span> |
| </span></span><span style="display:flex;"><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(cacheKey); |
| </span></span><span style="display:flex;"><span> Object instance <span style="color:#719e07">=</span> holder.get(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中不存在则创建扩展对象 双重校验锁</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (holder) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁的方式</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> holder.get(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建扩展对象</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> createExtension(name, wrap); |
| </span></span><span style="display:flex;"><span> holder.set(instance); |
| </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">return</span> (T) instance; |
| </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> T <span style="color:#268bd2">getDefaultExtension</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载扩展类型对应的所有扩展SPI实现类型,在加载所有扩展实现类型的时候会缓存这个扩展的默认实现类型,将对象缓存在cachedDefaultName中</span> |
| </span></span><span style="display:flex;"><span> getExtensionClasses(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isBlank(cachedDefaultName) <span style="color:#719e07">||</span> <span style="color:#2aa198">&#34;true&#34;</span>.equals(cachedDefaultName)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</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:#719e07">return</span> getExtension(cachedDefaultName); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//检查当前扩展加载器是否已经被销毁</span> |
| </span></span><span style="display:flex;"><span> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从自适应扩展缓存中查询扩展对象如果存在就直接返回,这个自适应扩展类型只会有一个扩展实现类型如果是多个的话根据是否可以覆盖参数决定扩展实现类是否可以相互覆盖</span> |
| </span></span><span style="display:flex;"><span> Object instance <span style="color:#719e07">=</span> cachedAdaptiveInstance.get(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个if判断不太优雅 容易多层嵌套,上面instance不为空就可以直接返回了</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建异常则抛出异常直接返回(多线程场景下可能第一个线程异常了第二个线程进来之后走到这里)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (createAdaptiveInstanceError <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</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;Failed to create adaptive instance: &#34;</span> <span style="color:#719e07">+</span> |
| </span></span><span style="display:flex;"><span> createAdaptiveInstanceError.toString(), |
| </span></span><span style="display:flex;"><span> createAdaptiveInstanceError); |
| </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:#268bd2">synchronized</span> (cachedAdaptiveInstance) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加锁的时候对象都是空的,进来之后先判断下防止重复创建</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> cachedAdaptiveInstance.get(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//只有第一个进来锁的对象为空开始创建扩展对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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">//根据SPI机制获取类型,创建对象</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> createAdaptiveExtension(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存入缓存</span> |
| </span></span><span style="display:flex;"><span> cachedAdaptiveInstance.set(instance); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </span></span><span style="display:flex;"><span> createAdaptiveInstanceError <span style="color:#719e07">=</span> t; |
| </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;Failed to create adaptive instance: &#34;</span> <span style="color:#719e07">+</span> t.toString(), t); |
| </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:#719e07">return</span> (T) instance; |
| </span></span><span style="display:flex;"><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></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> T instance <span style="color:#719e07">=</span> (T) getAdaptiveExtensionClass().newInstance(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象之前的回调方法</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessBeforeInitialization(instance, <span style="color:#cb4b16">null</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> injectExtension(instance); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//注入扩展对象之后的回调方法</span> |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> postProcessAfterInitialization(instance, <span style="color:#cb4b16">null</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">//参考样例第一个instance为ExtensionInjector的自适应扩展对象类型为AdaptiveExtensionInjector,自适应扩展注入器(适配器)用来查询具体支持的扩展注入器比如scope,spi,spring注入器</span> |
| </span></span><span style="display:flex;"><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> (Exception e) { |
| </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;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.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//获取扩展类型,将扩展类型存入成员变量cachedClasses中进行缓存</span> |
| </span></span><span style="display:flex;"><span> getExtensionClasses(); |
| </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:#719e07">if</span> (cachedAdaptiveClass <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cachedAdaptiveClass; |
| </span></span><span style="display:flex;"><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">//刚刚我们扫描到了扩展类型然后将其存入cachedClasses集合中了 接下来我们看下如何创建扩展类型</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> cachedAdaptiveClass <span style="color:#719e07">=</span> createAdaptiveExtensionClass(); |
| </span></span><span style="display:flex;"><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, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> getExtensionClasses() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存中查询扩展类型是否存在</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> classes <span style="color:#719e07">=</span> cachedClasses.get(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (classes <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//单例模式双重校验锁判断</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (cachedClasses) { |
| </span></span><span style="display:flex;"><span> classes <span style="color:#719e07">=</span> cachedClasses.get(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (classes <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载扩展类型</span> |
| </span></span><span style="display:flex;"><span> classes <span style="color:#719e07">=</span> loadExtensionClasses(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将我们扫描到的扩展类型存入成员变量cachedClasses中</span> |
| </span></span><span style="display:flex;"><span> cachedClasses.set(classes); |
| </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">return</span> classes; |
| </span></span><span style="display:flex;"><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>(Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses, LoadingStrategy strategy, String type) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加载并根据策略的参数来加载</span> |
| </span></span><span style="display:flex;"><span> loadDirectory(extensionClasses, strategy.directory(), type, strategy.preferExtensionClassLoader(), |
| </span></span><span style="display:flex;"><span> strategy.overridden(), strategy.includedPackages(), strategy.excludedPackages(), strategy.onlyExtensionClassLoaderPackages()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//下面两行就是要兼容alibaba的扩展包了 </span> |
| </span></span><span style="display:flex;"><span> String oldType <span style="color:#719e07">=</span> type.replace(<span style="color:#2aa198">&#34;org.apache&#34;</span>, <span style="color:#2aa198">&#34;com.alibaba&#34;</span>); |
| </span></span><span style="display:flex;"><span> loadDirectory(extensionClasses, strategy.directory(), oldType, strategy.preferExtensionClassLoader(), |
| </span></span><span style="display:flex;"><span> strategy.overridden(), strategy.includedPackagesInCompatibleType(), strategy.excludedPackages(), strategy.onlyExtensionClassLoaderPackages()); |
| </span></span><span style="display:flex;"><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>(Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses, String dir, String type, |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> extensionLoaderClassLoaderFirst, <span style="color:#dc322f">boolean</span> overridden, String<span style="color:#719e07">[]</span> includedPackages, |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> excludedPackages, String<span style="color:#719e07">[]</span> onlyExtensionClassLoaderPackages) { |
| </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> String fileName <span style="color:#719e07">=</span> dir <span style="color:#719e07">+</span> type; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</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></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (extensionLoaderClassLoaderFirst) { |
| </span></span><span style="display:flex;"><span> ClassLoader extensionLoaderClassLoader <span style="color:#719e07">=</span> ExtensionLoader.class.getClassLoader(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ClassLoader.getSystemClassLoader() <span style="color:#719e07">!=</span> extensionLoaderClassLoader) { |
| </span></span><span style="display:flex;"><span> classLoadersToLoad.add(extensionLoaderClassLoader); |
| </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">// load from scope model</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取域模型对象的类型加载器 ,这个域模型对象在初始化的时候会将自己的类加载器放入集合中可以参考《3.2.2 初始化ScopeModel》章节</span> |
| </span></span><span style="display:flex;"><span> Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders <span style="color:#719e07">=</span> scopeModel.getClassLoaders(); |
| </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:#719e07">if</span> (CollectionUtils.isEmpty(classLoaders)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从用于加载类的搜索路径中查找指定名称的所有资源。</span> |
| </span></span><span style="display:flex;"><span> Enumeration<span style="color:#719e07">&lt;</span>java.net.URL<span style="color:#719e07">&gt;</span> resources <span style="color:#719e07">=</span> ClassLoader.getSystemResources(fileName); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (resources <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> (resources.hasMoreElements()) { |
| </span></span><span style="display:flex;"><span> loadResource(extensionClasses, <span style="color:#cb4b16">null</span>, resources.nextElement(), overridden, includedPackages, excludedPackages, onlyExtensionClassLoaderPackages); |
| </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> classLoadersToLoad.addAll(classLoaders); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用类加载资源加载器(ClassLoaderResourceLoader)来加载具体的资源</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>ClassLoader, Set<span style="color:#719e07">&lt;</span>java.net.URL<span style="color:#719e07">&gt;&gt;</span> resources <span style="color:#719e07">=</span> ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad); |
| </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">//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> resources.forEach(((classLoader, urls) <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从文件中加载完资源之后开始根据类加载器和url加载具体的扩展类型,最后将扩展存放进extensionClasses集合</span> |
| </span></span><span style="display:flex;"><span> loadFromClass(extensionClasses, overridden, urls, classLoader, includedPackages, excludedPackages, onlyExtensionClassLoaderPackages); |
| </span></span><span style="display:flex;"><span> })); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </span></span><span style="display:flex;"><span> logger.error(<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>, t); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses, <span style="color:#dc322f">boolean</span> overridden, Set<span style="color:#719e07">&lt;</span>java.net.URL<span style="color:#719e07">&gt;</span> urls, ClassLoader classLoader, |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> includedPackages, String<span style="color:#719e07">[]</span> excludedPackages, String<span style="color:#719e07">[]</span> onlyExtensionClassLoaderPackages) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isNotEmpty(urls)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (java.net.URL url : urls) { |
| </span></span><span style="display:flex;"><span> loadResource(extensionClasses, classLoader, url, overridden, includedPackages, excludedPackages, onlyExtensionClassLoaderPackages); |
| </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类型中的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>(Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses, ClassLoader classLoader, |
| </span></span><span style="display:flex;"><span> java.net.URL resourceURL, <span style="color:#dc322f">boolean</span> overridden, String<span style="color:#719e07">[]</span> includedPackages, String<span style="color:#719e07">[]</span> excludedPackages, String<span style="color:#719e07">[]</span> onlyExtensionClassLoaderPackages) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里固定了文件的格式为utf8</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> (BufferedReader reader <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> BufferedReader(<span style="color:#719e07">new</span> InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { |
| </span></span><span style="display:flex;"><span> String line; |
| </span></span><span style="display:flex;"><span> String clazz; |
| </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:#719e07">while</span> ((line <span style="color:#719e07">=</span> reader.readLine()) <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</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> <span style="color:#dc322f">int</span> ci <span style="color:#719e07">=</span> line.indexOf(<span style="color:#2aa198">&#39;#&#39;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ci <span style="color:#719e07">&gt;=</span> 0) { |
| </span></span><span style="display:flex;"><span> line <span style="color:#719e07">=</span> line.substring(0, ci); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> line <span style="color:#719e07">=</span> line.trim(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (line.length() <span style="color:#719e07">&gt;</span> 0) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> String name <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</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:#dc322f">int</span> i <span style="color:#719e07">=</span> line.indexOf(<span style="color:#2aa198">&#39;=&#39;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (i <span style="color:#719e07">&gt;</span> 0) { |
| </span></span><span style="display:flex;"><span> name <span style="color:#719e07">=</span> line.substring(0, i).trim(); |
| </span></span><span style="display:flex;"><span> clazz <span style="color:#719e07">=</span> line.substring(i <span style="color:#719e07">+</span> 1).trim(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> clazz <span style="color:#719e07">=</span> line; |
| </span></span><span style="display:flex;"><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">//isIncluded是否为加载策略包含的类型,参数这里为空代表全部文件皆可包含 </span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//onlyExtensionClassLoaderPackages参数是否只有扩展类的类加载器可以加载扩展,其他扩展类型的类加载器不能加载扩展 这里结果为false 不排除任何类加载器</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isNotEmpty(clazz) <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isExcluded(clazz, excludedPackages) <span style="color:#719e07">&amp;&amp;</span> isIncluded(clazz, includedPackages) |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">&amp;&amp;</span> <span style="color:#719e07">!</span>isExcludedByClassLoader(clazz, classLoader, onlyExtensionClassLoaderPackages)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据类全路径加载类到内存</span> |
| </span></span><span style="display:flex;"><span> loadClass(extensionClasses, resourceURL, Class.forName(clazz, <span style="color:#cb4b16">true</span>, classLoader), name, overridden); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </span></span><span style="display:flex;"><span> IllegalStateException e <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> IllegalStateException(<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.getMessage(), t); |
| </span></span><span style="display:flex;"><span> exceptions.put(line, 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></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable t) { |
| </span></span><span style="display:flex;"><span> logger.error(<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, t); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(Map<span style="color:#719e07">&lt;</span>String, Class<span style="color:#719e07">&lt;?&gt;&gt;</span> extensionClasses, java.net.URL resourceURL, Class<span style="color:#719e07">&lt;?&gt;</span> clazz, String name, |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> overridden) <span style="color:#268bd2">throws</span> NoSuchMethodException { |
| </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">//这里第一次访问到的type是ExtensionInjector,clazz是SpringExtensionInjector 父子类型关系满足情况</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>type.isAssignableFrom(clazz)) { |
| </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;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.getName() <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.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is not subtype of interface.&#34;</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//扩展子类型是否存在这个注解@Adaptive</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (clazz.isAnnotationPresent(Adaptive.class)) { |
| </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:#719e07">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> cacheWrapperClass(clazz); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//无自适应注解,也没有构造器是扩展类型参数 ,这个name我们在扩展文件中找到了就是等号前面那个</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(name)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//低版本中可以使用@Extension 扩展注解来标注扩展类型,这里获取注解有两个渠道:</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//先查询@Extension注解是否存在如果存在则取value值,如果不存在@Extension注解则获取当前类型的名字</span> |
| </span></span><span style="display:flex;"><span> name <span style="color:#719e07">=</span> findAnnotationName(clazz); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (name.length() <span style="color:#719e07">==</span> 0) { |
| </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;No such extension name for the class &#34;</span> <span style="color:#719e07">+</span> clazz.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; in the config &#34;</span> <span style="color:#719e07">+</span> resourceURL); |
| </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">//获取扩展名字数组,扩展名字可能为逗号隔开的</span> |
| </span></span><span style="display:flex;"><span> String<span style="color:#719e07">[]</span> names <span style="color:#719e07">=</span> NAME_SEPARATOR.split(name); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ArrayUtils.isNotEmpty(names)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//@Activate注解修饰的扩展</span> |
| </span></span><span style="display:flex;"><span> cacheActivateClass(clazz, 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> (String n : names) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//cachedNames缓存集合缓存当前扩展类型的扩展名字</span> |
| </span></span><span style="display:flex;"><span> cacheName(clazz, n); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将扩展类型加入结果集合extensionClasses中,不允许覆盖的话出现同同名字扩展将抛出异常</span> |
| </span></span><span style="display:flex;"><span> saveInExtensionClass(extensionClasses, clazz, n, overridden); |
| </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>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>(Class<span style="color:#719e07">&lt;?&gt;</span> clazz, <span style="color:#dc322f">boolean</span> overridden) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (cachedAdaptiveClass <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span> <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> cachedAdaptiveClass <span style="color:#719e07">=</span> clazz; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>cachedAdaptiveClass.equals(clazz)) { |
| </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;More than 1 adaptive class found: &#34;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">+</span> cachedAdaptiveClass.getName() |
| </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.getName()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(Class<span style="color:#719e07">&lt;?&gt;</span> clazz) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (cachedWrapperClasses <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</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></span><span style="display:flex;"><span> <span style="color:#586e75">//缓存这个Wrapper类型的扩展</span> |
| </span></span><span style="display:flex;"><span> cachedWrapperClasses.add(clazz); |
| </span></span><span style="display:flex;"><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></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></span><span style="display:flex;"><span> ClassLoader classLoader <span style="color:#719e07">=</span> type.getClassLoader(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</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:#719e07">if</span> (NativeUtils.isNative()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> classLoader.loadClass(type.getName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;$Adaptive&#34;</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable ignore) { |
| </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">//创建一个代码生成器,来生成代码 详细内容我们就下一章来看</span> |
| </span></span><span style="display:flex;"><span> String code <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取编译器</span> |
| </span></span><span style="display:flex;"><span> org.apache.dubbo.common.compiler.Compiler compiler <span style="color:#719e07">=</span> extensionDirector.getExtensionLoader( |
| </span></span><span style="display:flex;"><span> org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); |
| </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> compiler.compile(type, code, classLoader); |
| </span></span><span style="display:flex;"><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> (T) getAdaptiveExtensionClass().newInstance(); |
| </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>injectExtension(instance); |
| </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>(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">//遍历所有的扩展注入器</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ExtensionInjector injector : injectors) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历所有的扩展注入器,如果可以获取到扩展对象则直接返回</span> |
| </span></span><span style="display:flex;"><span> T extension <span style="color:#719e07">=</span> injector.getInstance(type, name); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (extension <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extension; |
| </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> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><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>Set<span style="color:#719e07">&lt;</span>TypeBuilder<span style="color:#719e07">&gt;</span> tbs <span style="color:#719e07">=</span> model.getExtensionLoader(TypeBuilder.class).getSupportedExtensionInstances(); |
| </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>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>.getExtensionLoader(ScopeModelInitializer.class); |
| </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.getSupportedExtensionInstances(); |
| </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>ExtensionLoader<span style="color:#719e07">&lt;</span>OrderedPropertiesProvider<span style="color:#719e07">&gt;</span> propertiesProviderExtensionLoader <span style="color:#719e07">=</span> moduleModel.getExtensionLoader(OrderedPropertiesProvider.class); |
| </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> configManager <span style="color:#719e07">=</span> (ConfigManager) <span style="color:#719e07">this</span>.getExtensionLoader(ApplicationExt.class) |
| </span></span><span style="display:flex;"><span> .getExtension(ConfigManager.NAME); |
| </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>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>.getExtensionLoader(ModuleExt.class).getSupportedExtensionInstances(); |
| </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>environment <span style="color:#719e07">=</span> (Environment) <span style="color:#719e07">this</span>.getExtensionLoader(ApplicationExt.class) |
| </span></span><span style="display:flex;"><span> .getExtension(Environment.NAME); |
| </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>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>.getExtensionLoader(ApplicationInitListener.class); |
| </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.getSupportedExtensions(); |
| </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:#719e07">this</span>.extensionDirector <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ExtensionDirector(parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent.getExtensionDirector() : <span style="color:#cb4b16">null</span>, scope, <span style="color:#719e07">this</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.initBuilders(<span style="color:#719e07">this</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>(FrameworkModel model) { |
| </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.getExtensionLoader(TypeBuilder.class).getSupportedExtensionInstances(); |
| </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></span><span style="display:flex;"><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.getExtensionLoader(TypeBuilder.class) |
| </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>(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:#719e07">return</span> <span style="color:#719e07">this</span>.getExtensionDirector().getExtensionLoader(type); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> extensionDirector; |
| </span></span><span style="display:flex;"><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>.extensionDirector <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ExtensionDirector(parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent.getExtensionDirector() : <span style="color:#cb4b16">null</span>, scope, <span style="color:#719e07">this</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>(scope <span style="color:#719e07">=</span> ExtensionScope.FRAMEWORK) |
| </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></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>(Class<span style="color:#719e07">&lt;?&gt;</span> clazz); |
| </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>(Type type, Class<span style="color:#719e07">&lt;?&gt;</span> clazz, Map<span style="color:#719e07">&lt;</span>String, TypeDefinition<span style="color:#719e07">&gt;</span> typeCache); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><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>(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> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里参数类型传的是TypeBuilder.class不为空</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (type <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </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;Extension type == null&#34;</span>); |
| </span></span><span style="display:flex;"><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:#719e07">if</span> (<span style="color:#719e07">!</span>type.isInterface()) { |
| </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;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></span><span style="display:flex;"><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:#719e07">if</span> (<span style="color:#719e07">!</span>withExtensionAnnotation(type)) { |
| </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;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.class.getSimpleName() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;!&#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:#586e75">// 1. find in local cache</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//被加载的扩展类型对应的扩展加载器会放到extensionLoadersMap这个ConcurrentHashMap类型的集合中方便缓存</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> (ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span>) extensionLoadersMap.get(type); |
| </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">//extensionScopeMap为ConcurrentHashMap类型的扩展域缓存集合</span> |
| </span></span><span style="display:flex;"><span> ExtensionScope scope <span style="color:#719e07">=</span> extensionScopeMap.get(type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (scope <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> SPI annotation <span style="color:#719e07">=</span> type.getAnnotation(SPI.class); |
| </span></span><span style="display:flex;"><span> scope <span style="color:#719e07">=</span> annotation.scope(); |
| </span></span><span style="display:flex;"><span> extensionScopeMap.put(type, scope); |
| </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">//1)如果 扩展域为SELF 自给自足,为每个作用域创建一个实例,用于特殊的SPI扩展,如{@link ExtensionInjector}</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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.SELF) { |
| </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> loader <span style="color:#719e07">=</span> createExtensionLoader0(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:#586e75">// 2. find in parent</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//3) 从父扩展加载器中查询当前扩展加载器是否存在,这里parent是空的先不考虑</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> loader <span style="color:#719e07">=</span> <span style="color:#719e07">this</span>.parent.getExtensionLoader(type); |
| </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">// 3. create it</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//4) 这个是我们本次会走的逻辑,大部分是会走这个逻辑来创建扩展加载器对象的</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (loader <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> loader <span style="color:#719e07">=</span> createExtensionLoader(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> loader; |
| </span></span><span style="display:flex;"><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>(Class<span style="color:#719e07">&lt;?&gt;</span> type) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> type.isAnnotationPresent(SPI.class); |
| </span></span><span style="display:flex;"><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>(Class<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> type) { |
| </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></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">//当前类型ExtensionDirector的scope是在构造器中传递的,在Model对象初始化的时候创建的本类型</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isScopeMatched(type)) { |
| </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> loader <span style="color:#719e07">=</span> createExtensionLoader0(type); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</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></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader; |
| </span></span><span style="display:flex;"><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>(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> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> loader; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为当前扩展类型创建一个扩展访问器并缓存到,当前成员变量extensionLoadersMap中</span> |
| </span></span><span style="display:flex;"><span> extensionLoadersMap.putIfAbsent(type, <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">this</span>, scopeModel)); |
| </span></span><span style="display:flex;"><span> loader <span style="color:#719e07">=</span> (ExtensionLoader<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span>) extensionLoadersMap.get(type); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> loader; |
| </span></span><span style="display:flex;"><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(Class<span style="color:#719e07">&lt;?&gt;</span> type, ExtensionDirector extensionDirector, ScopeModel scopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前扩展加载器,需要加载的扩展的类型</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.type <span style="color:#719e07">=</span> type; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建扩展加载器的扩展访问器对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.extensionDirector <span style="color:#719e07">=</span> extensionDirector; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//从扩展访问器中获取扩展执行前后的回调器</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.extensionPostProcessors <span style="color:#719e07">=</span> extensionDirector.getExtensionPostProcessors(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建实例化对象的策略对象</span> |
| </span></span><span style="display:flex;"><span> initInstantiationStrategy(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果当前扩展类型为扩展注入器类型则设置当前注入器变量为空,否则的话获取一个扩展注入器扩展对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.injector <span style="color:#719e07">=</span> (type <span style="color:#719e07">==</span> ExtensionInjector.class <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> : extensionDirector.getExtensionLoader(ExtensionInjector.class) |
| </span></span><span style="display:flex;"><span> .getAdaptiveExtension()); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建Activate注解的排序器 </span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.activateComparator <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ActivateComparator(extensionDirector); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为扩展加载器下的域模型对象赋值</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.scopeModel <span style="color:#719e07">=</span> scopeModel; |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ExtensionPostProcessor extensionPostProcessor : extensionPostProcessors) { |
| </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:#719e07">if</span> (extensionPostProcessor <span style="color:#719e07">instanceof</span> ScopeModelAccessor) { |
| </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((ScopeModelAccessor) extensionPostProcessor); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">break</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">if</span> (instantiationStrategy <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> instantiationStrategy <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InstantiationStrategy(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#719e07">this</span>.injector <span style="color:#719e07">=</span> (type <span style="color:#719e07">==</span> ExtensionInjector.class <span style="color:#719e07">?</span> <span style="color:#cb4b16">null</span> : extensionDirector.getExtensionLoader(ExtensionInjector.class) |
| </span></span><span style="display:flex;"><span> .getAdaptiveExtension()); |
| </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></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(ApplicationModel.defaultModel().getDefaultModule()); |
| </span></span><span style="display:flex;"><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.defaultModel().getDefaultModule() |
| </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></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:#719e07">return</span> FrameworkModel.defaultModel().defaultApplication(); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//双重校验锁的形式创建单例对象</span> |
| </span></span><span style="display:flex;"><span> FrameworkModel instance <span style="color:#719e07">=</span> defaultInstance; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (instance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (globalLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//重置默认框架模型</span> |
| </span></span><span style="display:flex;"><span> resetDefaultFrameworkModel(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (defaultInstance <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> defaultInstance <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FrameworkModel(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> instance <span style="color:#719e07">=</span> defaultInstance; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> Assert.notNull(instance, <span style="color:#2aa198">&#34;Default FrameworkModel is null&#34;</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="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></span><span style="display:flex;"><span> <span style="color:#586e75">//全局悲观锁,同一个时刻只能有一个线程执行重置操作</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (globalLock) { |
| </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:#719e07">if</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.isDestroyed()) { |
| </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> FrameworkModel oldDefaultFrameworkModel <span style="color:#719e07">=</span> defaultInstance; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//存在实例模型列表则直接从内存缓存中查后续不需要创建了</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (allInstances.size() <span style="color:#719e07">&gt;</span> 0) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当前存在的有FrameworkModel框架实例多个列表则取第一个为默认的</span> |
| </span></span><span style="display:flex;"><span> defaultInstance <span style="color:#719e07">=</span> allInstances.get(0); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> defaultInstance <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (oldDefaultFrameworkModel <span style="color:#719e07">!=</span> defaultInstance) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (LOGGER.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> LOGGER.info(<span style="color:#2aa198">&#34;Reset global default framework from &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc(oldDefaultFrameworkModel) <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc(defaultInstance)); |
| </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>上面单例做了很多的初始化操作,这里开始调用构造器来创建框架模型对象,如下代码:</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></span><span style="display:flex;"><span> <span style="color:#586e75">//调用父类型ScopeModel传递参数,这个构造器的第一个参数为空代表这是一个顶层的域模型,第二个代表了这个是框架FRAMEWORK域,第三个false不是内部域</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(<span style="color:#cb4b16">null</span>, ExtensionScope.FRAMEWORK, <span style="color:#cb4b16">false</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">//FrameworkModel(索引=1)-&gt;ApplicationModel(索引=2)-&gt;ModuleModel(索引=1,第一个用户模块)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个index变量是static类型的为静态全局变量默认值从1开始,如果有多个框架模型对象则internalId编号从1开始依次递增</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.setInternalId(String.valueOf(index.getAndIncrement())); |
| </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></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (globalLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将当前框架模型实例添加到所有框架模型缓存对象中</span> |
| </span></span><span style="display:flex;"><span> allInstances.add(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如上面代码所示重置默认的框架模型对象,这里将会是缓存实例列表的第一个,新增了一个刷新默认实例对象</span> |
| </span></span><span style="display:flex;"><span> resetDefaultFrameworkModel(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (LOGGER.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> LOGGER.info(getDesc() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is created&#34;</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> initialize(); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//这里初始化之前先调用下父类型ScopeModel的初始化方法我们在下面来看</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.initialize(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用TypeDefinitionBuilder的静态方法initBuilders来初始化类型构建器TypeBuilder类型集合</span> |
| </span></span><span style="display:flex;"><span> TypeDefinitionBuilder.initBuilders(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//框架服务存储仓库对象,可以用于快速查询服务提供者信息</span> |
| </span></span><span style="display:flex;"><span> serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FrameworkServiceRepository(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取ScopeModelInitializer类型(域模型初始化器)的扩展加载器ExtensionLoader,每个扩展类型都会创建一个扩展加载器缓存起来</span> |
| </span></span><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>.getExtensionLoader(ScopeModelInitializer.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取ScopeModelInitializer类型的支持的扩展集合,这里当前版本存在8个扩展类型实现</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.getSupportedExtensionInstances(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历这些扩展实现调用他们的initializeFrameworkModel方法来传递FrameworkModel类型对象,细节我们待会再详细说下</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ScopeModelInitializer initializer : initializers) { |
| </span></span><span style="display:flex;"><span> initializer.initializeFrameworkModel(<span style="color:#719e07">this</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> internalApplicationModel <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ApplicationModel(<span style="color:#719e07">this</span>, <span style="color:#cb4b16">true</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">//获取ConfigManager类型对象,然后设置添加当前应用配置对象</span> |
| </span></span><span style="display:flex;"><span> internalApplicationModel.getApplicationConfigManager().setApplication( |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ApplicationConfig(internalApplicationModel, CommonConstants.DUBBO_INTERNAL_APPLICATION)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置公开的模块名字为常量DUBBO_INTERNAL_APPLICATION</span> |
| </span></span><span style="display:flex;"><span> internalApplicationModel.setModelName(CommonConstants.DUBBO_INTERNAL_APPLICATION); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化ExtensionDirector是一个作用域扩展加载程序管理器。</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">//查找和创建扩展实例的方法类似于Java classloader。</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.extensionDirector <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ExtensionDirector(parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent.getExtensionDirector() : <span style="color:#cb4b16">null</span>, scope, <span style="color:#719e07">this</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:#719e07">this</span>.extensionDirector.addExtensionPostProcessor(<span style="color:#719e07">new</span> ScopeModelAwareExtensionProcessor(<span style="color:#719e07">this</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:#719e07">this</span>.beanFactory <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ScopeBeanFactory(parent <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span> <span style="color:#719e07">?</span> parent.getBeanFactory() : <span style="color:#cb4b16">null</span>, extensionDirector); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//使用数据结构链表,创建销毁监听器容器,一般用于关闭进程,重置应用程序对象等操作时候调用</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</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:#719e07">this</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:#719e07">this</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">//将当前类的加载器存入加载器集合classLoaders中</span> |
| </span></span><span style="display:flex;"><span> ClassLoader dubboClassLoader <span style="color:#719e07">=</span> ScopeModel.class.getClassLoader(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (dubboClassLoader <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.addClassLoader(dubboClassLoader); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(FrameworkModel model) { |
| </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.getExtensionLoader(TypeBuilder.class).getSupportedExtensionInstances(); |
| </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></span><span style="display:flex;"><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>(FrameworkModel frameworkModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.frameworkModel <span style="color:#719e07">=</span> frameworkModel; |
| </span></span><span style="display:flex;"><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>.getExtensionLoader(ScopeModelInitializer.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取ScopeModelInitializer类型的支持的扩展集合,这里当前版本存在8个扩展类型实现</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.getSupportedExtensionInstances(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//遍历这些扩展实现调用他们的initializeFrameworkModel方法来传递FrameworkModel类型对象,细节我们待会再详细说下</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ScopeModelInitializer initializer : initializers) { |
| </span></span><span style="display:flex;"><span> initializer.initializeFrameworkModel(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><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></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>(FrameworkModel frameworkModel) { |
| </span></span><span style="display:flex;"><span> ScopeBeanFactory beanFactory <span style="color:#719e07">=</span> frameworkModel.getBeanFactory(); |
| </span></span><span style="display:flex;"><span> beanFactory.registerBean(RouterSnapshotSwitcher.class); |
| </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> <span style="color:#268bd2">class</span> <span style="color:#268bd2">CommonScopeModelInitializer</span> <span style="color:#268bd2">implements</span> ScopeModelInitializer { |
| </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>(FrameworkModel frameworkModel) { |
| </span></span><span style="display:flex;"><span> ScopeBeanFactory beanFactory <span style="color:#719e07">=</span> frameworkModel.getBeanFactory(); |
| </span></span><span style="display:flex;"><span> beanFactory.registerBean(FrameworkExecutorRepository.class); |
| </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> <span style="color:#268bd2">class</span> <span style="color:#268bd2">ConfigScopeModelInitializer</span> <span style="color:#268bd2">implements</span> ScopeModelInitializer { |
| </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>(FrameworkModel frameworkModel) { |
| </span></span><span style="display:flex;"><span> frameworkModel.addDestroyListener(<span style="color:#719e07">new</span> FrameworkModelCleaner()); |
| </span></span><span style="display:flex;"><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> internalApplicationModel.getApplicationConfigManager().setApplication( |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ApplicationConfig(internalApplicationModel, CommonConstants.DUBBO_INTERNAL_APPLICATION)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//设置公开的模块名字为常量DUBBO_INTERNAL_APPLICATION</span> |
| </span></span><span style="display:flex;"><span> internalApplicationModel.setModelName(CommonConstants.DUBBO_INTERNAL_APPLICATION); |
| </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">this</span>, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> internalApplicationModel.getApplicationConfigManager().setApplication( |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> ApplicationConfig(internalApplicationModel, CommonConstants.DUBBO_INTERNAL_APPLICATION)); |
| </span></span><span style="display:flex;"><span> internalApplicationModel.setModelName(CommonConstants.DUBBO_INTERNAL_APPLICATION); |
| </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>(FrameworkModel frameworkModel, <span style="color:#dc322f">boolean</span> isInternal) { |
| </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:#268bd2">super</span>(frameworkModel, ExtensionScope.APPLICATION, isInternal); |
| </span></span><span style="display:flex;"><span> Assert.notNull(frameworkModel, <span style="color:#2aa198">&#34;FrameworkModel can not be null&#34;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//应用程序域成员变量记录frameworkModel对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.frameworkModel <span style="color:#719e07">=</span> frameworkModel; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//frameworkModel对象添加当前应用程序域对象</span> |
| </span></span><span style="display:flex;"><span> frameworkModel.addApplication(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (LOGGER.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> LOGGER.info(getDesc() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is created&#34;</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> initialize(); |
| </span></span><span style="display:flex;"><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>(ApplicationModel applicationModel) { |
| </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">//检查FrameworkModel对象是否已经被标记为销毁状态,如果已经被销毁了则抛出异常无需执行逻辑</span> |
| </span></span><span style="display:flex;"><span> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (instLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果还未添加过当前参数传递应用模型</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>.applicationModels.contains(applicationModel)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为当前应用模型生成内部id</span> |
| </span></span><span style="display:flex;"><span> applicationModel.setInternalId(buildInternalId(getInternalId(), appIndex.getAndIncrement())); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//添加到成员变量集合applicationModels中</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.applicationModels.add(applicationModel); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果非内部的则也向公开应用模型集合pubApplicationModels中添加一下</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>applicationModel.isInternal()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.pubApplicationModels.add(applicationModel); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> resetDefaultAppModel(); |
| </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>内部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>(String parentInternalId, <span style="color:#dc322f">long</span> childIndex) { |
| </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">// ApplicationModel 1.1</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// ModuleModel 1.1.1</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.hasText(parentInternalId)) { |
| </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></span><span style="display:flex;"><span> } <span style="color:#719e07">else</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></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (instLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</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>.defaultAppModel.isDestroyed()) { |
| </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">//取第一个公开的应用模型做为默认应用模型</span> |
| </span></span><span style="display:flex;"><span> ApplicationModel oldDefaultAppModel <span style="color:#719e07">=</span> <span style="color:#719e07">this</span>.defaultAppModel; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (pubApplicationModels.size() <span style="color:#719e07">&gt;</span> 0) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.defaultAppModel <span style="color:#719e07">=</span> pubApplicationModels.get(0); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.defaultAppModel <span style="color:#719e07">=</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</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>.defaultAppModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (LOGGER.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> LOGGER.info(<span style="color:#2aa198">&#34;Reset global default application from &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc(oldDefaultAppModel) <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; to &#34;</span> <span style="color:#719e07">+</span> safeGetModelDesc(<span style="color:#719e07">this</span>.defaultAppModel)); |
| </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><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></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:#268bd2">super</span>.initialize(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个内部的模块模型对象</span> |
| </span></span><span style="display:flex;"><span> internalModule <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleModel(<span style="color:#719e07">this</span>, <span style="color:#cb4b16">true</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建一个独立服务存储对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceRepository(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用程序初始化监听器ApplicationInitListener扩展</span> |
| </span></span><span style="display:flex;"><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>.getExtensionLoader(ApplicationInitListener.class); |
| </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> listenerNames <span style="color:#719e07">=</span> extensionLoader.getSupportedExtensions(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (String listenerName : listenerNames) { |
| </span></span><span style="display:flex;"><span> extensionLoader.getExtension(listenerName).init(); |
| </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> initApplicationExts(); |
| </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> 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>.getExtensionLoader(ScopeModelInitializer.class); |
| </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.getSupportedExtensionInstances(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ScopeModelInitializer initializer : initializers) { |
| </span></span><span style="display:flex;"><span> initializer.initializeApplicationModel(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#586e75">//这个扩展实现一共有两个可以看下面那个图扩展类型为ConfigManager和Environment</span> |
| </span></span><span style="display:flex;"><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>.getExtensionLoader(ApplicationExt.class).getSupportedExtensionInstances(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ApplicationExt ext : exts) { |
| </span></span><span style="display:flex;"><span> ext.initialize(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#268bd2">throws</span> IllegalStateException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//乐观锁判断是否初始化过</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>initialized.compareAndSet(<span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</span>)) { |
| </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">//从模块环境中获取组合配置,目前Environment中有6种重要的配置,我们后面详细说</span> |
| </span></span><span style="display:flex;"><span> CompositeConfiguration configuration <span style="color:#719e07">=</span> scopeModel.getModelEnvironment().getConfiguration(); |
| </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> String configModeStr <span style="color:#719e07">=</span> (String) configuration.getProperty(ConfigKeys.DUBBO_CONFIG_MODE); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.hasText(configModeStr)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.configMode <span style="color:#719e07">=</span> ConfigMode.valueOf(configModeStr.toUpperCase()); |
| </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> String msg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Illegal &#39;&#34;</span> <span style="color:#719e07">+</span> ConfigKeys.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.toString(ConfigMode.values()); |
| </span></span><span style="display:flex;"><span> logger.error(msg, e); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException(msg, e); |
| </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">// dubbo.config.ignore-duplicated-interface</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//忽略重复的接口(服务/引用)配置。默认值为false</span> |
| </span></span><span style="display:flex;"><span> String ignoreDuplicatedInterfaceStr <span style="color:#719e07">=</span> (String) configuration |
| </span></span><span style="display:flex;"><span> .getProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ignoreDuplicatedInterfaceStr <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.ignoreDuplicatedInterface <span style="color:#719e07">=</span> Boolean.parseBoolean(ignoreDuplicatedInterfaceStr); |
| </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">// print 打印配置信息</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, 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.put(ConfigKeys.DUBBO_CONFIG_MODE, configMode); |
| </span></span><span style="display:flex;"><span> map.put(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE, <span style="color:#719e07">this</span>.ignoreDuplicatedInterface); |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;Config settings: &#34;</span> <span style="color:#719e07">+</span> map); |
| </span></span><span style="display:flex;"><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:#268bd2">throws</span> IllegalStateException { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//乐观锁判断是否进行过初始化</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (initialized.compareAndSet(<span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</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:#719e07">this</span>.propertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> PropertiesConfiguration(scopeModel); |
| </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:#719e07">this</span>.systemConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SystemConfiguration(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//EnvironmentConfiguration是从环境变量中获取的配置</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.environmentConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> EnvironmentConfiguration(); |
| </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:#719e07">this</span>.externalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration(<span style="color:#2aa198">&#34;ExternalConfig&#34;</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:#719e07">this</span>.appExternalConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration(<span style="color:#2aa198">&#34;AppExternalConfig&#34;</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:#719e07">this</span>.appConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> InmemoryConfiguration(<span style="color:#2aa198">&#34;AppConfig&#34;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//服务迁移配置加载 dubbo2升级dubbo3的一些配置</span> |
| </span></span><span style="display:flex;"><span> loadMigrationRule(); |
| </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">//服务迁移配置加载 JVM &gt; env &gt; 代码路径dubbo-migration.yaml</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">loadMigrationRule</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">// JVM参数中获取</span> |
| </span></span><span style="display:flex;"><span> String path <span style="color:#719e07">=</span> System.getProperty(CommonConstants.DUBBO_MIGRATION_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(path)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//env环境变量中获取</span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> System.getenv(CommonConstants.DUBBO_MIGRATION_KEY); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.isEmpty(path)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//类路径下获取文件dubbo-migration.yaml</span> |
| </span></span><span style="display:flex;"><span> path <span style="color:#719e07">=</span> CommonConstants.DEFAULT_DUBBO_MIGRATION_FILE; |
| </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">this</span>.localMigrationRule <span style="color:#719e07">=</span> ConfigUtils.loadMigrationRule(scopeModel.getClassLoaders(), path); |
| </span></span><span style="display:flex;"><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>(Set<span style="color:#719e07">&lt;</span>ClassLoader<span style="color:#719e07">&gt;</span> classLoaders, String fileName) { |
| </span></span><span style="display:flex;"><span> String rawRule <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;&#34;</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (checkFileNameExist(fileName)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> (FileInputStream input <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> FileInputStream(fileName)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> readString(input); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Throwable e) { |
| </span></span><span style="display:flex;"><span> logger.warn(<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.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">try</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.add(ClassUtils.getClassLoader()); |
| </span></span><span style="display:flex;"><span> classLoadersToLoad.addAll(classLoaders); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Set<span style="color:#719e07">&lt;</span>URL<span style="color:#719e07">&gt;</span> urls : ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad).values()) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (URL url : urls) { |
| </span></span><span style="display:flex;"><span> InputStream is <span style="color:#719e07">=</span> url.openStream(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (is <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> readString(is); |
| </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> (Throwable e) { |
| </span></span><span style="display:flex;"><span> logger.warn(<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.getMessage(), e); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> rawRule; |
| </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">private</span> <span style="color:#268bd2">static</span> String <span style="color:#268bd2">readString</span>(InputStream is) { |
| </span></span><span style="display:flex;"><span> StringBuilder stringBuilder <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> StringBuilder(); |
| </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> (BufferedReader reader <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> BufferedReader(<span style="color:#719e07">new</span> InputStreamReader(is))) { |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> n; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">while</span> ((n <span style="color:#719e07">=</span> reader.read(buffer)) <span style="color:#719e07">!=</span> <span style="color:#719e07">-</span>1) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (n <span style="color:#719e07">&lt;</span> 10) { |
| </span></span><span style="display:flex;"><span> buffer <span style="color:#719e07">=</span> Arrays.copyOf(buffer, n); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> stringBuilder.append(String.valueOf(buffer)); |
| </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></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (IOException e) { |
| </span></span><span style="display:flex;"><span> logger.error(<span style="color:#2aa198">&#34;Read migration file error.&#34;</span>, e); |
| </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> stringBuilder.toString(); |
| </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">/** |
| </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>(String fileName) { |
| </span></span><span style="display:flex;"><span> File file <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> File(fileName); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> file.exists(); |
| </span></span><span style="display:flex;"><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">this</span>, <span style="color:#cb4b16">true</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>(ApplicationModel applicationModel, <span style="color:#dc322f">boolean</span> isInternal) { |
| </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:#268bd2">super</span>(applicationModel, ExtensionScope.MODULE, isInternal); |
| </span></span><span style="display:flex;"><span> Assert.notNull(applicationModel, <span style="color:#2aa198">&#34;ApplicationModel can not be null&#34;</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化成员变量applicationModel</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.applicationModel <span style="color:#719e07">=</span> applicationModel; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将模块模型添加至应用模型中</span> |
| </span></span><span style="display:flex;"><span> applicationModel.addModule(<span style="color:#719e07">this</span>, isInternal); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (LOGGER.isInfoEnabled()) { |
| </span></span><span style="display:flex;"><span> LOGGER.info(getDesc() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34; is created&#34;</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> initialize(); |
| </span></span><span style="display:flex;"><span> Assert.notNull(serviceRepository, <span style="color:#2aa198">&#34;ModuleServiceRepository can not be null&#34;</span>); |
| </span></span><span style="display:flex;"><span> Assert.notNull(moduleConfigManager, <span style="color:#2aa198">&#34;ModuleConfigManager can not be null&#34;</span>); |
| </span></span><span style="display:flex;"><span> Assert.assertTrue(moduleConfigManager.isInitialized(), <span style="color:#2aa198">&#34;ModuleConfigManager can not be initialized&#34;</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></span><span style="display:flex;"><span> ApplicationDeployer applicationDeployer <span style="color:#719e07">=</span> applicationModel.getDeployer(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (applicationDeployer <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> applicationDeployer.notifyModuleChanged(<span style="color:#719e07">this</span>, DeployState.PENDING); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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.addModule(<span style="color:#719e07">this</span>, isInternal); |
| </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>(ModuleModel moduleModel, <span style="color:#dc322f">boolean</span> isInternal) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//加锁</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">synchronized</span> (moduleLock) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//不存在则添加</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>.moduleModels.contains(moduleModel)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查应用模型是否已销毁</span> |
| </span></span><span style="display:flex;"><span> checkDestroyed(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//添加至模块模型成员变量中</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.moduleModels.add(moduleModel); |
| </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">//可以参考 3.3.2 将ApplicationModel添加至FrameworkModel容器中</span> |
| </span></span><span style="display:flex;"><span> moduleModel.setInternalId(buildInternalId(getInternalId(), moduleIndex.getAndIncrement())); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果不是内部模型则添加到公开模块模型中</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>isInternal) { |
| </span></span><span style="display:flex;"><span> pubModuleModels.add(moduleModel); |
| </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><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></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:#268bd2">super</span>.initialize(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建模块服务存储库对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleServiceRepository(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//创建模块配置管理对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.moduleConfigManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleConfigManager(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块配置管理对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.moduleConfigManager.initialize(); |
| </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> initModuleExt(); |
| </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> 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>.getExtensionLoader(ScopeModelInitializer.class); |
| </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.getSupportedExtensionInstances(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ScopeModelInitializer initializer : initializers) { |
| </span></span><span style="display:flex;"><span> initializer.initializeModuleModel(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#719e07">this</span>.serviceRepository <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleServiceRepository(<span style="color:#719e07">this</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>(ModuleModel moduleModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块模型</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.moduleModel <span style="color:#719e07">=</span> moduleModel; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//</span> |
| </span></span><span style="display:flex;"><span> frameworkServiceRepository <span style="color:#719e07">=</span> ScopeModelUtil.getFrameworkModel(moduleModel).getServiceRepository(); |
| </span></span><span style="display:flex;"><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.getFrameworkModel(moduleModel).getServiceRepository() |
| </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>(ScopeModel scopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (scopeModel <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> FrameworkModel.defaultModel(); |
| </span></span><span style="display:flex;"><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:#719e07">if</span> (scopeModel <span style="color:#719e07">instanceof</span> ApplicationModel) { |
| </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> ((ApplicationModel) scopeModel).getFrameworkModel(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> (scopeModel <span style="color:#719e07">instanceof</span> ModuleModel) { |
| </span></span><span style="display:flex;"><span> ModuleModel moduleModel <span style="color:#719e07">=</span> (ModuleModel) scopeModel; |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//间接通过ApplicationModel获取,不越级获取</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> moduleModel.getApplicationModel().getFrameworkModel(); |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> <span style="color:#719e07">if</span> (scopeModel <span style="color:#719e07">instanceof</span> FrameworkModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> (FrameworkModel) scopeModel; |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">else</span> { |
| </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;Unable to get FrameworkModel from &#34;</span> <span style="color:#719e07">+</span> scopeModel); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#719e07">this</span>.moduleConfigManager <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ModuleConfigManager(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化模块配置管理对象</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.moduleConfigManager.initialize(); |
| </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>(ModuleModel moduleModel) { |
| </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></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(moduleModel, Arrays.asList(ModuleConfig.class, ServiceConfigBase.class, ReferenceConfigBase.class, ProviderConfig.class, ConsumerConfig.class)); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取应用程序配置管理器</span> |
| </span></span><span style="display:flex;"><span> applicationConfigManager <span style="color:#719e07">=</span> moduleModel.getApplicationModel().getApplicationConfigManager(); |
| </span></span><span style="display:flex;"><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:#268bd2">throws</span> IllegalStateException { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>initialized.compareAndSet(<span style="color:#cb4b16">false</span>, <span style="color:#cb4b16">true</span>)) { |
| </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">//获取组合配置对象</span> |
| </span></span><span style="display:flex;"><span> CompositeConfiguration configuration <span style="color:#719e07">=</span> scopeModel.getModelEnvironment().getConfiguration(); |
| </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">//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> String configModeStr <span style="color:#719e07">=</span> (String) configuration.getProperty(ConfigKeys.DUBBO_CONFIG_MODE); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (StringUtils.hasText(configModeStr)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.configMode <span style="color:#719e07">=</span> ConfigMode.valueOf(configModeStr.toUpperCase()); |
| </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> String msg <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;Illegal &#39;&#34;</span> <span style="color:#719e07">+</span> ConfigKeys.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.toString(ConfigMode.values()); |
| </span></span><span style="display:flex;"><span> logger.error(msg, e); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">throw</span> <span style="color:#719e07">new</span> IllegalArgumentException(msg, e); |
| </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">// dubbo.config.ignore-duplicated-interface</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//忽略重复的接口(服务/引用)配置。默认值为false</span> |
| </span></span><span style="display:flex;"><span> String ignoreDuplicatedInterfaceStr <span style="color:#719e07">=</span> (String) configuration |
| </span></span><span style="display:flex;"><span> .getProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (ignoreDuplicatedInterfaceStr <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.ignoreDuplicatedInterface <span style="color:#719e07">=</span> Boolean.parseBoolean(ignoreDuplicatedInterfaceStr); |
| </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">// print 打印配置信息到控制台</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, 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.put(ConfigKeys.DUBBO_CONFIG_MODE, configMode); |
| </span></span><span style="display:flex;"><span> map.put(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE, <span style="color:#719e07">this</span>.ignoreDuplicatedInterface); |
| </span></span><span style="display:flex;"><span> logger.info(<span style="color:#2aa198">&#34;Config settings: &#34;</span> <span style="color:#719e07">+</span> map); |
| </span></span><span style="display:flex;"><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></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></span><span style="display:flex;"><span><span style="color:#586e75">//目前这里的扩展只支持有一个类型ModuleEnvironment</span> |
| </span></span><span style="display:flex;"><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>.getExtensionLoader(ModuleExt.class).getSupportedExtensionInstances(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ModuleExt ext : exts) { |
| </span></span><span style="display:flex;"><span> ext.initialize(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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:#268bd2">throws</span> IllegalStateException { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.orderedPropertiesConfiguration <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> OrderedPropertiesConfiguration(moduleModel); |
| </span></span><span style="display:flex;"><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>(ModuleModel moduleModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.moduleModel <span style="color:#719e07">=</span> moduleModel; |
| </span></span><span style="display:flex;"><span> refresh(); |
| </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">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refresh</span>() { |
| </span></span><span style="display:flex;"><span> properties <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Properties(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//有序的配置提供器扩展获取</span> |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>OrderedPropertiesProvider<span style="color:#719e07">&gt;</span> propertiesProviderExtensionLoader <span style="color:#719e07">=</span> moduleModel.getExtensionLoader(OrderedPropertiesProvider.class); |
| </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.getSupportedExtensions(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (CollectionUtils.isEmpty(propertiesProviderNames)) { |
| </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> 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> (String propertiesProviderName : propertiesProviderNames) { |
| </span></span><span style="display:flex;"><span> orderedPropertiesProviders.add(propertiesProviderExtensionLoader.getExtension(propertiesProviderName)); |
| </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">//order the propertiesProvider according the priority descending</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据优先级进行排序,值越小优先级越高</span> |
| </span></span><span style="display:flex;"><span> orderedPropertiesProviders.sort((a, b) <span style="color:#719e07">-&gt;</span> b.priority() <span style="color:#719e07">-</span> a.priority()); |
| </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:#719e07">for</span> (OrderedPropertiesProvider orderedPropertiesProvider : orderedPropertiesProviders) { |
| </span></span><span style="display:flex;"><span> properties.putAll(orderedPropertiesProvider.initProperties()); |
| </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/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></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>(String<span style="color:#719e07">[]</span> args) <span style="color:#268bd2">throws</span> Exception { |
| </span></span><span style="display:flex;"><span> startWithBootstrap(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</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.setInterface(DemoService.class); |
| </span></span><span style="display:flex;"><span> service.setRef(<span style="color:#719e07">new</span> DemoServiceImpl()); |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap.getInstance(); |
| </span></span><span style="display:flex;"><span> bootstrap.application(<span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </span></span><span style="display:flex;"><span> .service(service) |
| </span></span><span style="display:flex;"><span> .start() |
| </span></span><span style="display:flex;"><span> .await(); |
| </span></span><span style="display:flex;"><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>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></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(ApplicationModel.defaultModel().getDefaultModule()); |
| </span></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>(ApplicationModel.defaultModel()); |
| </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">AbstractConfig</span>(ScopeModel scopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.setScopeModel(scopeModel); |
| </span></span><span style="display:flex;"><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>(ScopeModel scopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//第一次初始化的当前成员变量是空的可以设置变量</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</span>.scopeModel <span style="color:#719e07">!=</span> scopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//检查参数是否合法</span> |
| </span></span><span style="display:flex;"><span> checkScopeModel(scopeModel); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化对象</span> |
| </span></span><span style="display:flex;"><span> ScopeModel oldScopeModel <span style="color:#719e07">=</span> <span style="color:#719e07">this</span>.scopeModel; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.scopeModel <span style="color:#719e07">=</span> scopeModel; |
| </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></span><span style="display:flex;"><span> <span style="color:#586e75">//子类应该重写此方法以初始化其SPI扩展并更改引用的配置的范围模型。</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.postProcessAfterScopeModelChanged(oldScopeModel, <span style="color:#719e07">this</span>.scopeModel); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(ScopeModel scopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (scopeModel <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </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;scopeModel cannot be null&#34;</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>(scopeModel <span style="color:#719e07">instanceof</span> ApplicationModel)) { |
| </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;Invalid scope model, expect to be a ApplicationModel but got: &#34;</span> <span style="color:#719e07">+</span> scopeModel); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(ScopeModel oldScopeModel, ScopeModel newScopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.postProcessAfterScopeModelChanged(oldScopeModel, newScopeModel); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化当前协议对象,通过扩展机制获取协议Protocol类型的对象</span> |
| </span></span><span style="display:flex;"><span> protocolSPI <span style="color:#719e07">=</span> <span style="color:#719e07">this</span>.getExtensionLoader(Protocol.class).getAdaptiveExtension(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化当前代理工厂对象,通过扩展机制获取ProxyFactory类型的对象</span> |
| </span></span><span style="display:flex;"><span> proxyFactory <span style="color:#719e07">=</span> <span style="color:#719e07">this</span>.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); |
| </span></span><span style="display:flex;"><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>(ScopeModel oldScopeModel, ScopeModel newScopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.postProcessAfterScopeModelChanged(oldScopeModel, newScopeModel); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//当服务提供者配置对象不为空时候为服务提供者对象设置域模型,这里服务提供者对象仍旧为空,这个一般用在兼容Dubbo低版本</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</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>.provider.getScopeModel() <span style="color:#719e07">!=</span> scopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.provider.setScopeModel(scopeModel); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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>(ScopeModel oldScopeModel, ScopeModel newScopeModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.postProcessAfterScopeModelChanged(oldScopeModel, newScopeModel); |
| </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></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></span><span style="display:flex;"><span> ApplicationModel applicationModel <span style="color:#719e07">=</span> ScopeModelUtil.getApplicationModel(scopeModel); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为配置中心对象设置ApplicationModel类型对象(当前阶段配置中心配置对象为空)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</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>.configCenter.getScopeModel() <span style="color:#719e07">!=</span> applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.configCenter.setScopeModel(applicationModel); |
| </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:#719e07">if</span> (<span style="color:#719e07">this</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>.metadataReportConfig.getScopeModel() <span style="color:#719e07">!=</span> applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.metadataReportConfig.setScopeModel(applicationModel); |
| </span></span><span style="display:flex;"><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:#719e07">if</span> (<span style="color:#719e07">this</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>.monitor.getScopeModel() <span style="color:#719e07">!=</span> applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.monitor.setScopeModel(applicationModel); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这个if判断和上面的上面是重复的估计是写代码人加班加的太久了,没注意看</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">this</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>.metadataReportConfig.getScopeModel() <span style="color:#719e07">!=</span> applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.metadataReportConfig.setScopeModel(applicationModel); |
| </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:#719e07">if</span> (CollectionUtils.isNotEmpty(<span style="color:#719e07">this</span>.registries)) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.registries.forEach(registryConfig <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (registryConfig.getScopeModel() <span style="color:#719e07">!=</span> applicationModel) { |
| </span></span><span style="display:flex;"><span> registryConfig.setScopeModel(applicationModel); |
| </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>最后被调用到的是最顶层父类型<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>(ScopeModel oldScopeModel, ScopeModel newScopeModel) { |
| </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></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></span><span style="display:flex;"><span> <span style="color:#586e75">//服务元数据对象创建</span> |
| </span></span><span style="display:flex;"><span> serviceMetadata <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ServiceMetadata(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//为服务元数据对象</span> |
| </span></span><span style="display:flex;"><span> serviceMetadata.addAttribute(<span style="color:#2aa198">&#34;ORIGIN_CONFIG&#34;</span>, <span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><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></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></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>(String name); |
| </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>(String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> CompletableFuture.completedFuture(sayHello(name)); |
| </span></span><span style="display:flex;"><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-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></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.Logger; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.slf4j.LoggerFactory; |
| </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></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></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.getLogger(DemoServiceImpl.class); |
| </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>(String name) { |
| </span></span><span style="display:flex;"><span> logger.info(<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.getServiceContext().getRemoteAddress()); |
| </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.getServiceContext().getLocalAddress(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> CompletableFuture<span style="color:#719e07">&lt;</span>String<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">sayHelloAsync</span>(String name) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</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><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></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ApplicationConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.MetadataReportConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ProtocolConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.RegistryConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.ServiceConfig; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.config.bootstrap.DubboBootstrap; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">import</span> org.apache.dubbo.demo.DemoService; |
| </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></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>(String<span style="color:#719e07">[]</span> args) <span style="color:#268bd2">throws</span> Exception { |
| </span></span><span style="display:flex;"><span> startWithBootstrap(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">static</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startWithBootstrap</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.setInterface(DemoService.class); |
| </span></span><span style="display:flex;"><span> service.setRef(<span style="color:#719e07">new</span> DemoServiceImpl()); |
| </span></span><span style="display:flex;"><span> DubboBootstrap bootstrap <span style="color:#719e07">=</span> DubboBootstrap.getInstance(); |
| </span></span><span style="display:flex;"><span> bootstrap.application(<span style="color:#719e07">new</span> ApplicationConfig(<span style="color:#2aa198">&#34;dubbo-demo-api-provider&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .registry(<span style="color:#719e07">new</span> RegistryConfig(<span style="color:#2aa198">&#34;zookeeper://127.0.0.1:2181&#34;</span>)) |
| </span></span><span style="display:flex;"><span> .protocol(<span style="color:#719e07">new</span> ProtocolConfig(CommonConstants.DUBBO, <span style="color:#719e07">-</span>1)) |
| </span></span><span style="display:flex;"><span> .service(service) |
| </span></span><span style="display:flex;"><span> .start() |
| </span></span><span style="display:flex;"><span> .await(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><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></channel></rss> |