<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Java on Apache Dubbo</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/tags/java/</link><description>Recent content in Java on Apache Dubbo</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sat, 15 Nov 2025 11:38:51 +0800</lastBuildDate><atom:link href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/tags/java/index.xml" rel="self" type="application/rss+xml"/><item><title>如果从接口级服务发现平滑迁移到应用级服务发现</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2024/05/13/%E5%A6%82%E6%9E%9C%E4%BB%8E%E6%8E%A5%E5%8F%A3%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%B9%B3%E6%BB%91%E8%BF%81%E7%A7%BB%E5%88%B0%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</link><pubDate>Mon, 13 May 2024 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2024/05/13/%E5%A6%82%E6%9E%9C%E4%BB%8E%E6%8E%A5%E5%8F%A3%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%B9%B3%E6%BB%91%E8%BF%81%E7%A7%BB%E5%88%B0%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</guid><description>&lt;p>总体上来说，在地址注册与发现环节，&lt;code>3.x&lt;/code> 是完全兼容 &lt;code>2.x&lt;/code> 版本的，这意味着，用户可以选择将集群内任意数量的应用或机器升级到 &lt;code>3.x&lt;/code>，同时在这个过程中保持与 &lt;code>2.x&lt;/code> 版本的互操作性。&lt;/p>
&lt;blockquote>
&lt;p>如关心迁移背后工作原理，请参考 &lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/overview/mannual/java-sdk/upgrades-and-compatibility/service-discovery/service-discovery-rule">迁移规则详情与工作原理&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h2 id="1-快速升级步骤">1 快速升级步骤&lt;/h2>
&lt;p>简单的修改 pom.xml 到最新版本就可以完成升级，如果要迁移到应用级地址，只需要调整开关控制 3.x 版本的默认行为。&lt;/p>
&lt;ol>
&lt;li>升级 Provider 应用到最新 3.x 版本依赖，配置双注册开关&lt;code>dubbo.application.register-mode=all&lt;/code>（建议通过全局配置中心设置，默认已自动开启），完成应用发布。&lt;/li>
&lt;li>升级 Consumer 应用到最新 3.x 版本依赖，配置双订阅开关&lt;code>dubbo.application.service-discovery.migration=APPLICATION_FIRST&lt;/code>（建议通过全局配置中心设置，默认已自动开启），完成应用发布。&lt;/li>
&lt;li>在确认 Provider 的上有 Consumer 全部完成应用级地址迁移后，Provider 切到应用级地址单注册。完成升级&lt;/li>
&lt;/ol>
&lt;p>以下是关于迁移流程的详细描述。&lt;/p>
&lt;h2 id="2-provider-端升级过程详解">2 Provider 端升级过程详解&lt;/h2>
&lt;p>在不改变任何 Dubbo 配置的情况下，可以将一个应用或实例升级到 3.x 版本，升级后的 Dubbo 实例会默保保证与 2.x 版本的兼容性，即会正常注册 2.x 格式的地址到注册中心，因此升级后的实例仍会对整个集群仍保持可见状态。&lt;/p>
&lt;p>同时新的地址发现模型（注册应用级别的地址）也将会自动注册。&lt;/p>
&lt;p>&lt;img alt="//imgs/v3/migration/provider-registration.png" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/v3/migration/provider-registration.png">&lt;/p>
&lt;ol>
&lt;li>全局开关&lt;/li>
&lt;/ol>
&lt;p>应用配置（可以通过配置文件或者 -D 指定）&lt;code>dubbo.application.register-mode&lt;/code> 为 instance（只注册应用级）、all（接口级+应用级均注册）开启全局的注册开关，配置此开关后，默认会向所有的注册中心中注册应用级的地址，供消费端服务发现使用。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 双注册
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.register-mode=all
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 仅应用级注册
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.register-mode=instance
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过 -D 参数，可以指定 provider 启动时的注册行为&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>-Ddubbo.application.register-mode=all
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 可选值 interface、instance、all，默认是 all，即接口级地址、应用级地址都注册
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>另外，可以在配置中心修改全局默认行为，来控制所有 3.x 实例注册行为。其中，全局性开关的优先级低于 -D 参数。&lt;/p></description></item><item><title>应用级服务发现地址迁移规则说明</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2024/05/13/%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%9C%B0%E5%9D%80%E8%BF%81%E7%A7%BB%E8%A7%84%E5%88%99%E8%AF%B4%E6%98%8E/</link><pubDate>Mon, 13 May 2024 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2024/05/13/%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%9C%B0%E5%9D%80%E8%BF%81%E7%A7%BB%E8%A7%84%E5%88%99%E8%AF%B4%E6%98%8E/</guid><description>&lt;h2 id="状态模型">状态模型&lt;/h2>
&lt;p>在 Dubbo 3 之前地址注册模型是以接口级粒度注册到注册中心的，而 Dubbo 3 全新的应用级注册模型注册到注册中心的粒度是应用级的。从注册中心的实现上来说是几乎不一样的，这导致了对于从接口级注册模型获取到的 invokers 是无法与从应用级注册模型获取到的 invokers 进行合并的。为了帮助用户从接口级往应用级迁移，Dubbo 3 设计了 Migration 机制，基于三个状态的切换实现实际调用中地址模型的切换。&lt;/p>
&lt;p>&lt;img alt="//imgs/v3/migration/migration-1.png" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/v3/migration/migration-1.png">&lt;/p>
&lt;p>当前共存在三种状态，FORCE_INTERFACE（强制接口级），APPLICATION_FIRST（应用级优先）、FORCE_APPLICATION（强制应用级）。&lt;/p>
&lt;p>FORCE_INTERFACE：只启用兼容模式下接口级服务发现的注册中心逻辑，调用流量 100% 走原有流程
APPLICATION_FIRST：开启接口级、应用级双订阅，运行时根据阈值和灰度流量比例动态决定调用流量走向
FORCE_APPLICATION：只启用新模式下应用级服务发现的注册中心逻辑，调用流量 100% 走应用级订阅的地址&lt;/p>
&lt;h2 id="规则体说明">规则体说明&lt;/h2>
&lt;p>规则采用 yaml 格式配置，具体配置下参考如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">key&lt;/span>: 消费者应用名（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">step&lt;/span>: 状态名（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">threshold&lt;/span>: 决策阈值（默认1.0）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">proportion&lt;/span>: 灰度比例（默认100）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">delay&lt;/span>: 延迟决策时间（默认0）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">force&lt;/span>: 强制切换（默认 false）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">interfaces&lt;/span>: 接口粒度配置（可选）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">serviceKey: 接口名（接口 + &lt;/span>: + 版本号）（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">threshold&lt;/span>: 决策阈值
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">proportion&lt;/span>: 灰度比例
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">delay&lt;/span>: 延迟决策时间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: 强制切换
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: 状态名（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">serviceKey: 接口名（接口 + &lt;/span>: + 版本号）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: 状态名
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">applications&lt;/span>: 应用粒度配置（可选）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">serviceKey&lt;/span>: 应用名（消费的上游应用名）（必填）
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">threshold&lt;/span>: 决策阈值
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">proportion&lt;/span>: 灰度比例
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">delay&lt;/span>: 延迟决策时间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: 强制切换
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: 状态名（必填）
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>key: 消费者应用名&lt;/li>
&lt;li>step: 状态名（FORCE_INTERFACE、APPLICATION_FIRST、FORCE_APPLICATION）&lt;/li>
&lt;li>threshold: 决策阈值（浮点数，具体含义参考后文）&lt;/li>
&lt;li>proportion: 灰度比例（0～100，决定调用次数比例）&lt;/li>
&lt;li>delay: 延迟决策时间（延迟决策的时间，实际等待时间为 1～2 倍 delay 时间，取决于注册中心第一次通知的时间，对于目前 Dubbo 的注册中心实现次配置项保留 0 即可）&lt;/li>
&lt;li>force: 强制切换（对于 FORCE_INTERFACE、FORCE_APPLICATION 是否不考虑决策直接切换，可能导致无地址调用失败问题）&lt;/li>
&lt;li>interfaces: 接口粒度配置&lt;/li>
&lt;/ul>
&lt;p>参考配置示例如下：&lt;/p></description></item><item><title>应用级服务发现迁移示例</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2024/05/13/%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E8%BF%81%E7%A7%BB%E7%A4%BA%E4%BE%8B/</link><pubDate>Mon, 13 May 2024 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2024/05/13/%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E8%BF%81%E7%A7%BB%E7%A4%BA%E4%BE%8B/</guid><description>&lt;p>应用级服务发现为应用间服务发现的协议，因此使用应用级服务发现需要消费端和服务端均升级到 Dubbo 3.0 版本并开启新特性（默认开启）才能在链路中使用应用级服务发现，真正发挥应用级服务发现的优点。&lt;/p>
&lt;h2 id="开启方式">开启方式&lt;/h2>
&lt;h3 id="服务端">服务端&lt;/h3>
&lt;p>应用升级到 Dubbo 3.0 后，服务端自动开启接口级 + 应用级双注册功能，默认无需开发者修改任何配置&lt;/p>
&lt;h3 id="消费端">消费端&lt;/h3>
&lt;p>应用升级到 Dubbo 3.0 后，消费端自动始接口级 + 应用级双订阅功能，默认无需开发者修改任何配置。建议在服务端都升级到 Dubbo 3.0 并开启应用级注册以后通过规则配置消费端关闭接口级订阅，释放对应的内存空间。&lt;/p>
&lt;h2 id="详细说明">详细说明&lt;/h2>
&lt;h3 id="服务端配置">服务端配置&lt;/h3>
&lt;ol>
&lt;li>全局开关&lt;/li>
&lt;/ol>
&lt;p>应用配置（可以通过配置文件或者 -D 指定）&lt;code>dubbo.application.register-mode&lt;/code> 为 instance（只注册应用级）、all（接口级+应用级均注册）开启全局的注册开关，配置此开关后，默认会向所有的注册中心中注册应用级的地址，供消费端服务发现使用。&lt;/p>
&lt;blockquote>
&lt;p>&lt;a href="https://github.com/apache/dubbo-samples/blob/master/2-advanced/dubbo-samples-service-discovery/dubbo-servicediscovery-migration/dubbo-servicediscovery-migration-provider1/src/main/resources/application.yml">参考示例&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 双注册
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> register-mode: all
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 仅应用级注册
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registry:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> register-mode: instance
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>注册中心地址参数配置&lt;/li>
&lt;/ol>
&lt;p>注册中心的地址上可以配置 &lt;code>registry-type=service&lt;/code> 来显示指定该注册中心为应用级服务发现的注册中心，带上此配置的注册中心将只进行应用级服务发现。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;nacos://${nacos.address:127.0.0.1}:8848?registry-type=service&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="消费端订阅模式">消费端订阅模式&lt;/h3>
&lt;p>FORCE_INTERFACE：仅接口级订阅，行为和 Dubbo 2.7 及以前版本一致。
APPLICATION_FIRST：接口级 + 应用级多订阅，如果应用级能订阅到地址就使用应用级的订阅，如果订阅不到地址则使用接口级的订阅，以此保证迁移过程中最大的兼容性。（注：由于存在同时进行订阅的行为，此模式下内存占用会有一定的增长，因此在所有服务端都升级到 Dubbo 3.0 以后建议迁移到 FORCE_APPLICATION 模式降低内存占用）
FORCE_APPLICATION：仅应用级订阅，将只采用全新的服务发现模型。&lt;/p>
&lt;h3 id="消费端配置">消费端配置&lt;/h3>
&lt;ol>
&lt;li>默认配置（不需要配置）&lt;/li>
&lt;/ol>
&lt;p>升级到 Dubbo 3.0 后默认行为为接口级+应用级多订阅，如果应用级能订阅到地址就使用应用级的订阅，如果订阅不到地址则使用接口级的订阅，以此保证最大的兼容性。&lt;/p></description></item><item><title>使用 Apache APISIX 代理 Dubbo 服务 (dubbo 协议)</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2024/04/25/%E4%BD%BF%E7%94%A8-apache-apisix-%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1-dubbo-%E5%8D%8F%E8%AE%AE/</link><pubDate>Thu, 25 Apr 2024 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2024/04/25/%E4%BD%BF%E7%94%A8-apache-apisix-%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1-dubbo-%E5%8D%8F%E8%AE%AE/</guid><description>&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">注意&lt;/h4>

 本文仅适用于 dubbo 协议通信场景。如果您是 Dubbo3 用户，建议您使用 triple 协议，可参见 &lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2024/04/22/%E4%BD%BF%E7%94%A8-apache-apisix-%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1triple%E5%8D%8F%E8%AE%AE/">使用 Apache APISIX 代理 Dubbo 服务（triple协议）&lt;/a> 学习具体示例。

&lt;/div>

&lt;p>&lt;a href="https://apisix.apache.org/">Apache APISIX&lt;/a> 是 Apache 软件基金会的顶级开源项目，也是当前最活跃的开源网关项目。作为一个动态、实时、高性能的开源 API 网关，Apache APISIX 提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。&lt;/p>
&lt;p>Apache APISIX 基于开源项目 tengine/mod_dubbo 模块为 Apache Dubbo 服务配备了HTTP 网关能力。通过 dubbo-proxy 插件，可以轻松地将 Dubbo Service 发布为 HTTP 服务。&lt;/p>
&lt;p>&lt;img alt="架构图" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/apisix-plugin/1.png">&lt;/p>
&lt;h2 id="入门篇">入门篇&lt;/h2>
&lt;h3 id="安装-apisix">安装 APISIX&lt;/h3>
&lt;p>本文档使用 Docker 安装 APISIX。确保本地先安装 &lt;a href="https://www.docker.com/">Docker&lt;/a> 和 &lt;a href="https://docs.docker.com/compose/">Docker Compose&lt;/a>。&lt;/p>
&lt;p>首先，下载 &lt;a href="https://github.com/apache/apisix-docker">apisix-docker&lt;/a> 仓库。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>$ git clone https://github.com/apache/apisix-docker.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">cd&lt;/span> apisix-docker/example
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由于本示例要接入到 Nacos 注册中心，因此 &lt;code>apisix-docker/example&lt;/code> 目录下安装用的 &lt;code>docker-compose.yaml&lt;/code>，添加如下内容：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">nacos&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">image&lt;/span>: nacos/nacos-server:v2.1.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">container_name&lt;/span>: nacos-standalone
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">environment&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - PREFER_HOST_MODE=hostname
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - MODE=standalone
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#2aa198">&amp;#34;8848:8848&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#2aa198">&amp;#34;9848:9848&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">networks&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">apisix&lt;/span>:
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 config.yaml 文件中增加 nacos 注册中心配置：&lt;/p></description></item><item><title>使用 Resilience4j 断路器、限流器、重试、隔离机制保护 Dubbo 应用</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/12/14/%E4%BD%BF%E7%94%A8-resilience4j-%E6%96%AD%E8%B7%AF%E5%99%A8%E9%99%90%E6%B5%81%E5%99%A8%E9%87%8D%E8%AF%95%E9%9A%94%E7%A6%BB%E6%9C%BA%E5%88%B6%E4%BF%9D%E6%8A%A4-dubbo-%E5%BA%94%E7%94%A8/</link><pubDate>Thu, 14 Dec 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/12/14/%E4%BD%BF%E7%94%A8-resilience4j-%E6%96%AD%E8%B7%AF%E5%99%A8%E9%99%90%E6%B5%81%E5%99%A8%E9%87%8D%E8%AF%95%E9%9A%94%E7%A6%BB%E6%9C%BA%E5%88%B6%E4%BF%9D%E6%8A%A4-dubbo-%E5%BA%94%E7%94%A8/</guid><description>&lt;p>Resilience4j 提供了一组高阶函数（装饰器），包括断路器，限流器，重试，隔离，可以对任何的函数式接口，lambda表达式，或方法的引用进行增强，并且这些装饰器可以进行叠加。这样做的好处是，你可以根据需要选择特定的装饰器进行组合。&lt;/p>
&lt;p>关于 Resilience4j 与 Dubbo 集成的使用示例请参见 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-resilience4j">dubbo-samples-resilience4j&lt;/a>&lt;/p></description></item><item><title>使用 Hystrix 对 Dubbo 服务进行熔断限流保护</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/12/14/%E4%BD%BF%E7%94%A8-hystrix-%E5%AF%B9-dubbo-%E6%9C%8D%E5%8A%A1%E8%BF%9B%E8%A1%8C%E7%86%94%E6%96%AD%E9%99%90%E6%B5%81%E4%BF%9D%E6%8A%A4/</link><pubDate>Thu, 14 Dec 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/12/14/%E4%BD%BF%E7%94%A8-hystrix-%E5%AF%B9-dubbo-%E6%9C%8D%E5%8A%A1%E8%BF%9B%E8%A1%8C%E7%86%94%E6%96%AD%E9%99%90%E6%B5%81%E4%BF%9D%E6%8A%A4/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点，从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离，请求缓存和请求打包，以及监控和配置等功能。&lt;/p>
&lt;p>本文介绍在spring应用里，怎么把 Dubbo 和 Hystrix 结合起来使用。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/Netflix/Hystrix">https://github.com/Netflix/Hystrix&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/apache/dubbo">https://github.com/apache/dubbo&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="spring-boot应用">Spring Boot应用&lt;/h2>
&lt;p>Demo 地址： &lt;a href="https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-boot-hystrix">https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-boot-hystrix&lt;/a>&lt;/p>
&lt;h3 id="生成dubbo集成spring-boot的应用">生成dubbo集成spring boot的应用&lt;/h3>
&lt;p>对于不熟悉dubbo 集成spring boot应用的同学，可以在这里直接生成dubbo + spring boot的工程： &lt;a href="http://start.dubbo.apache.org/bootstrap.html/">http://start.dubbo.apache.org/bootstrap.html/&lt;/a>&lt;/p>
&lt;h3 id="配置spring-cloud-starter-netflix-hystrix">配置spring-cloud-starter-netflix-hystrix&lt;/h3>
&lt;p>spring boot官方提供了对hystrix的集成，直接在pom.xml里加入依赖：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.cloud&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-cloud-starter-netflix-hystrix&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>1.4.4.RELEASE&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后在Application类上增加&lt;code>@EnableHystrix&lt;/code>来启用hystrix starter：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SpringBootApplication&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableHystrix&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderApplication&lt;/span> {
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="配置provider端">配置Provider端&lt;/h3>
&lt;p>在Dubbo的Provider上增加&lt;code>@HystrixCommand&lt;/code>配置，这样子调用就会经过Hystrix代理。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>(version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">HelloServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> HelloService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixCommand&lt;/span>(commandProperties &lt;span style="color:#719e07">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixProperty&lt;/span>(name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;circuitBreaker.requestVolumeThreshold&amp;#34;&lt;/span>, value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;10&amp;#34;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixProperty&lt;/span>(name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;execution.isolation.thread.timeoutInMilliseconds&amp;#34;&lt;/span>, value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;2000&amp;#34;&lt;/span>) })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// System.out.println(&amp;#34;async provider received: &amp;#34; + name);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// return &amp;#34;annotation: hello, &amp;#34; + name;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException(&lt;span style="color:#2aa198">&amp;#34;Exception to show hystrix enabled.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="配置consumer端">配置Consumer端&lt;/h3>
&lt;p>对于Consumer端，则可以增加一层method调用，并在method上配置&lt;code>@HystrixCommand&lt;/code>。当调用出错时，会走到&lt;code>fallbackMethod = &amp;quot;reliable&amp;quot;&lt;/code>的调用里。&lt;/p></description></item><item><title>走向 Native 化：Spring&amp;Dubbo AOT 技术示例与原理讲解</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/06/28/%E8%B5%B0%E5%90%91-native-%E5%8C%96springdubbo-aot-%E6%8A%80%E6%9C%AF%E7%A4%BA%E4%BE%8B%E4%B8%8E%E5%8E%9F%E7%90%86%E8%AE%B2%E8%A7%A3/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/06/28/%E8%B5%B0%E5%90%91-native-%E5%8C%96springdubbo-aot-%E6%8A%80%E6%9C%AF%E7%A4%BA%E4%BE%8B%E4%B8%8E%E5%8E%9F%E7%90%86%E8%AE%B2%E8%A7%A3/</guid><description>&lt;p>Java 应用在云计算时代面临“冷启动”慢、内存占用高、预热时间长等问题，无法很好的适应 Serverless 等云上部署模式，GraalVM 通过静态编译、打包等技术在很大程度上解决了这些问题，同时针对 GraalVM 的一些使用限制，Spring 和 Dubbo 等主流框架也都提供了相应的 AOT 解决方案。&lt;/p>
&lt;p>本文我们将详细分析 Java 应用在云时代面临的挑战，GraalVM Native Image 是如何解决这些问题，GraalVM 的基本概念与工作原理，最后我们通过一个 Spring6 + Dubbo3 的微服务应用示例演示了如何将一个普通微服务应用进行静态化打包。&lt;/p>
&lt;p>本文主要分为以下四个部分展开&lt;/p>
&lt;ol>
&lt;li>首先我们会先看一下在云计算快速发展的当下，云上应用应该具备的特点，Java 应用在云上所面临的挑战有哪些。&lt;/li>
&lt;li>其次，我会介绍一下 GraalVM，什么是 Native Image，如何通过 GraalVM 对 Java 应用进行静态化打出 Native Image 可执行的二进制程序。&lt;/li>
&lt;li>第三部分，我们知道 GraalVM 的使用是有一定限制的，比如 Java 的反射等动态特性是不被支持的，因此我们需要提供特殊的 Metadata 配置来绕过这些限制，在这一部分我们会讲解如何加入引入 AOT Processing 来实现自动化的 Metadata 配置，包括 Spring6 框架中 AOT 处理、Dubbo3 框架的 AOT 处理等。&lt;/li>
&lt;li>最后，我们将通过一个 Spring6+Dubbo3 的应用示例，来演示如何将这么一个 Java 应用进行静态化打包。&lt;/li>
&lt;/ol>
&lt;h2 id="java-应用在云时代所面临的挑战">Java 应用在云时代所面临的挑战&lt;/h2>
&lt;p>首先，我们先看一下云计算时代的应用特点，以及 Java 在云时代所面临的挑战。从各个统计机构给出的数据来看，Java 语言仍然是当今最受开发者欢迎的编程语言之一，仅次于一些脚本开发语言。使用 Java 语言可以非常高效的开发业务应用，丰富的生态使得 Java 具有非常高的开发和运行效率，有无数的应用基于 Java 语言开发。&lt;/p></description></item><item><title>引言</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</guid><description>&lt;h2 id="引言">引言&lt;/h2>
&lt;p>服务指标统计体系是 Dubbo 可观测能力的重要组成部分。
dubbo-metrics 指标模块旨在将 dubbo 内部零散的 Metrics 相关类综合到一个单独的模块中，提供一套更加完善、全面、可拓展、解耦合的指标采样-统计-导出解决方案。&lt;/p>
&lt;p>dubbo-metrics 模块包括：&lt;/p>
&lt;ul>
&lt;li>dubbo-metrics-api 公用接口包&lt;/li>
&lt;li>dubbo-metrics-prometheus 普罗米修斯适配包&lt;/li>
&lt;li>dubbo-metrics-metadata 元数据中心指标监控包&lt;/li>
&lt;li>dubbo-metrics-registry 注册中心指标监控包&lt;/li>
&lt;li>dubbo-metrics-config-center 配置中心指标监控包&lt;/li>
&lt;li>dubbo-metrics-default 接口默认实现包，提供dubbo内部核心指标的监控功能&lt;/li>
&lt;/ul>
&lt;p>在设计上，dubbo-metrics 深入应用事件驱动编程思想，总体体现出下图的事件处理链路：&lt;/p>
&lt;p>&lt;img alt="metrics-event-struct" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/metrics-source-blog/metrics-event-struct.png">&lt;/p>
&lt;p>在拓展上，dubbo-metrics 抽象了一套指标导出接口与抽象实现，可实现兼容多种指标统计监控中心，默认提供了普罗米修斯实现。&lt;/p></description></item><item><title>1-指标样本的收集与存储</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</guid><description>&lt;h2 id="一指标样本的收集与存储">一、指标样本的收集与存储&lt;/h2>
&lt;h3 id="指标样本收集">指标样本收集&lt;/h3>
&lt;p>&lt;strong>指标收集器（Collector）是指标对外导出的入口&lt;/strong>。最终导出到指标统计中心的指标采样实际均直接来源于各个指标采样器。因此，我们将从各个收集器实现开始，分析 dubbo-metrics 模块是如何工作的。&lt;/p>
&lt;p>指标收集操作定义在 MetricsCollector （指标采集器，SPI）接口中，可以通过它的实现收集某一类的指标样本（MetricSample）。它主要有以下实现，对应着不同类型的指标：&lt;/p>
&lt;ul>
&lt;li>ConfigCenterMetricsCollector &lt;strong>配置中心操作相关指标收集器&lt;/strong> ，收集配置信息的变化次数&lt;/li>
&lt;li>MetadataMetricsCollector &lt;strong>元数据中心操作相关指标收集器&lt;/strong>，收集提供者、消费者对元数据中心操作（推送数据、拉取数据）情况的计数、耗时统计。&lt;/li>
&lt;li>RegistryMetricsCollector &lt;strong>注册中心相关操作指标收集器&lt;/strong>，收集应用级、接口级服务注册成功、失败、耗时的相关计数。&lt;/li>
&lt;li>DefaultMetricsCollector &lt;strong>默认指标收集器&lt;/strong>，内置多种采样器来完成不同类型的内部指标采样。&lt;/li>
&lt;li>HistogramMetricsCollector &lt;strong>直方图指标收集器&lt;/strong>，利用 micrometer API 处理直方图类型的指标，它的实现较为特殊。&lt;/li>
&lt;/ul>
&lt;p>配置中心 、元数据、服务注册及默认指标收集器均实现自混合指标收集器（CombMetricsCollector）。混合指标收集器实现了 ApplicationMetricsCollector 、ServiceMetricsCollector 、MethodMetricsCollector 三个接口（定义按应用名收集、按应用名-服务名收集和按应用-方法名收集指标的操作），因此它们可以进行应用、服务和方法三个层面的指标收集工作。&lt;/p>
&lt;p>默认指标收集器的特点是通过内部的指标采样器（MetricsSampler）完成指标事件的处理操作，而不是其它收集器的指标监听器（MetricsListener）&lt;/p>
&lt;p>直方图指标收集器则负责收集直方图类型的指标。它利用直方图度量寄存器（HistogramMetricRegister）借助 micrometer API 完成直方图样本的采集。直方图类型包括百分位数、服务水平目标、最小预期值、最大预期值、统计数据分布有效期等。&lt;/p>
&lt;p>&lt;strong>Collector的继承关系：&lt;/strong>&lt;/p>
&lt;p>&lt;img alt="collectors" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/metrics-source-blog/collectors.png">&lt;/p>
&lt;p>可以看出，每个指标收集器都具有来自 MetricsListener 的监听指标事件的能力。为什么指标收集器本身需要监听指标事件？ 在后文中，我们将讨论指标收集器是如何利用内置的子转发器（SubDispatcher）转发指标事件，并完成计数处理的。&lt;/p>
&lt;h3 id="指标样本存储">指标样本存储&lt;/h3>
&lt;p>前文中，我们了解了指标收集的入口是指标收集器（Collector）。那么各个收集器从哪里收集指标样本？&lt;/p>
&lt;p>&lt;strong>对于配置中心、元数据中心、 注册中心的指标收集器：&lt;/strong>&lt;/p>
&lt;p>它们分别负责采集三大中心模块的指标，均继承于&lt;strong>混合数据收集器（CombMetricsCollector）&lt;/strong>，而混合数据收集器中实现了 export 方法 。&lt;/p>
&lt;p>混合数据收集器内部有一个&lt;strong>基本数据聚合器（BaseStatComposite）&lt;/strong>，它实现了 MetricsExport 接口，该接口定义了指标导出操作，混合数据收集器则利用它的 export 方法导出指标。&lt;/p>
&lt;p>基本数据聚合器是一个抽象类，内有三个属性：ApplicationStatComposite 、ServiceStatComposite 和 RtStatComposite 。它们的作用：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ApplicationStatComposite 应用数据聚合器&lt;/strong>，应用程序级别相关事件的计数 ，根据指标Key（ MetricsKey ）和应用名统计指标，提供计数递增操作&lt;/li>
&lt;li>&lt;strong>ServiceStatComposite 服务数据聚合器&lt;/strong>，服务级别相关事件的计数，根据指标Key、应用名和服务名统计指标，提供计数递增操作&lt;/li>
&lt;li>&lt;strong>MethodStatComposite 方法数据聚合器&lt;/strong>，方法级别相关事件的计数，存储各方法RPC调用相关计数。&lt;/li>
&lt;li>&lt;strong>RtStatComposite，Rt（Response Time，响应时间）数据聚合器&lt;/strong>，包括应用级别和服务级别。根据应用名、服务名、注册的指标名及相应相应时间统计指标，提供添加操作。&lt;/li>
&lt;/ul>
&lt;p>对于以上四个聚合器，他们的职责就是存储某一类型的采样样本。&lt;/p>
&lt;p>&lt;strong>基本数据聚合器 （BaseStatComposite）&lt;/strong> 对这三个子聚合器的操作进行了简单整合，统一提供给外界。&lt;strong>而混合指标收集器（CombMetricsCollector）&lt;/strong> 也基本保留了内部基本数据聚合器的所有操作，将其封装为 &lt;code>increment&lt;/code>、&lt;code>setNum&lt;/code>、&lt;code>addRt &lt;/code>三个方法（及它们的重载，分别收集应用级数据和服务级数据）向上提供。外部组件可以直接调用这些收集器完成指标更新操作。&lt;/p></description></item><item><title>2-指标收集器的指标采集流程</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</guid><description>&lt;h2 id="二指标收集器的指标采集流程">二、指标收集器的指标采集流程&lt;/h2>
&lt;p>在前文中，我们了解了指标收集器（Collector）最终收集的数据只有三个来源：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>实现自混合指标收集器（CombMetricsCollector） 的元数据指标收集器（MetadataMetricsCollector）和注册中心指标收集器（RegistryMetricsCollector），它们的样本均存储在内置的基本数据聚合器中。具体来说，是基本数据聚合器下的四个子数据聚合器中：&lt;/p>
&lt;p>&lt;img alt="composite-struct" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/metrics-source-blog/composite-struct.png">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>DefaultMetricsCollector 默认指标收集器&lt;/strong>，它的样本不仅来自于指标事件，还来自其下&lt;strong>采样器（Sampler）&lt;/strong> 中，用于Dubbo核心模块的采样。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>HistogramMetricsCollector 直方图指标收集器&lt;/strong>，由于采样数据的特殊性，它的样本直接以 Map 存储在内部。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>接下来，我们需要明确它们存储的指标是如何添加进去的。&lt;/p>
&lt;h3 id="1服务治理模块的指标采集流程">1，服务治理模块的指标采集流程&lt;/h3>
&lt;p>通过之前的分析，我们知道服务治理模块的指标采集器均实现自混合指标收集器（CombMetricsCollector）。它对基本数据聚合器（BaseStatComposite） 的大部分方法做了封装。基本数据聚合器又封装了四个负责存储不同类型指标采样的子聚合器。&lt;/p>
&lt;p>这四个子聚合器包括：&lt;/p>
&lt;ul>
&lt;li>ApplicationStatComposite&lt;/li>
&lt;li>ServiceStatComposite&lt;/li>
&lt;li>MethodStatComposite&lt;/li>
&lt;li>RtStatComposite&lt;/li>
&lt;/ul>
&lt;p>实际上，&lt;strong>元数据、注册中心指标收集器&lt;/strong>更新、添加指标的操作都是通过混合指标收集器暴露的方法进行。而具体的，是通过 &lt;code>setNum&lt;/code>、&lt;code>increment&lt;/code>、&lt;code>addRt&lt;/code> 这三个方法（及它们的重载）进行操作。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//CombMetricsCollector&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> BaseStatComposite stats;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setNum&lt;/span>(MetricsKey metricsKey, String applicationName, String serviceKey, &lt;span style="color:#dc322f">int&lt;/span> num) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.stats.setServiceKey(metricsKey, applicationName, serviceKey, num);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">increment&lt;/span>(String applicationName, MetricsKey metricsKey) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.stats.incrementApp(metricsKey, applicationName, SELF_INCREMENT_SIZE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">increment&lt;/span>(String applicationName, String serviceKey, MetricsKey metricsKey, &lt;span style="color:#dc322f">int&lt;/span> size) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.stats.incrementServiceKey(metricsKey, applicationName, serviceKey, size);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addRt&lt;/span>(String applicationName, String registryOpType, Long responseTime) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stats.calcApplicationRt(applicationName, registryOpType, responseTime);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addRt&lt;/span>(String applicationName, String serviceKey, String registryOpType, Long responseTime) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stats.calcServiceKeyRt(applicationName, serviceKey, registryOpType, responseTime);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由于几个方法实际上的调用链路类似，我们选择从其中的 setNum 方法开始分析。&lt;/p></description></item><item><title>3-指标监听注册梳理</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</guid><description>&lt;h2 id="三指标监听注册梳理">三、指标监听注册梳理&lt;/h2>
&lt;p>在前一章中，我们了解了不同收集器中的指标样本是如何被监听器添加进去的。接下来，我们将归纳指标监听器 的创建位置，及它们对应统计的指标。&lt;/p>
&lt;p>通过之前的分析，我们已经知道指标 注册事件多播器（RegistryMetricsEventMulticaster）中定义了并绑定了服务注册相关的指标。这种绑定操作同样存在于其它几个简单指标事件多播器（SimpleMetricsEventMulticaster）的几个实现中。&lt;/p>
&lt;h3 id="转发器注册">转发器注册&lt;/h3>
&lt;p>&lt;strong>RegistrySubDispatcher （服务注册指标转发器）注册了服务注册相关指标：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>应用级实例注册成功/失败/总数计数 （APPLICATION_REGISTER_&amp;hellip;)&lt;/li>
&lt;li>应用级服务接口订阅成功/失败/总数计数 （APPLICATION_SUBSCRIBE_&amp;hellip;)&lt;/li>
&lt;li>服务级注册成功/失败/总数计数 （SERVICE_REGISTER_&amp;hellip;）&lt;/li>
&lt;li>特殊的 APPLICATION_NOTIFY_FINISH 和 APPLICATION_DIRECTORY_POST （应用服务目录变化次数）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>MetadataSubDispatcher（元数据指标转发器）注册应用元数据相关指标&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>应用推送元数据相关计数 （APPLICATION_PUSH_&amp;hellip;)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>应用订阅元数据相关计数 （APPLICAITON_SUBSCRIBE_&amp;hellip;）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>服务订阅元数据相关计数 （SERVICE_SUBSCRIBE_&amp;hellip;）&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>ConfigCenterSubDispatcher (配置中心指标转发器) 注册配置中心配置更新次数指标&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>配置中心推送新配置次数 （CONFIGCENTER_METRIC_TOTAL）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>DefaultSubDispatcher (默认转发器) 注册核心RPC调用次数指标&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>请求次数 （METRIC_REQUESTS）&lt;/li>
&lt;li>请求成功次数（METRIC_REQUESTS_SUCCEED）&lt;/li>
&lt;li>请求失败次数（METRIC_REQUEST_BUSINESS_FAILED）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>MetricsDispatcher&lt;/strong>&lt;/p>
&lt;p>MetricsDispatcher 较为特殊，它负责 ApplicationModel 下所有 MetricsCollector（前文中提到的指标收集器） 的初始化注册工作，并将它们添加到自己的监听器列表中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MetricsDispatcher&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> SimpleMetricsEventMulticaster {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@SuppressWarnings&lt;/span>({&lt;span style="color:#2aa198">&amp;#34;rawtypes&amp;#34;&lt;/span>})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">MetricsDispatcher&lt;/span>(ApplicationModel applicationModel) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ScopeBeanFactory beanFactory &lt;span style="color:#719e07">=&lt;/span> applicationModel.getBeanFactory();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCollector&lt;span style="color:#719e07">&amp;gt;&lt;/span> extensionLoader &lt;span style="color:#719e07">=&lt;/span> applicationModel.getExtensionLoader(MetricsCollector.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (extensionLoader &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCollector&lt;span style="color:#719e07">&amp;gt;&lt;/span> customizeCollectors &lt;span style="color:#719e07">=&lt;/span> extensionLoader
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .getActivateExtensions();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (MetricsCollector customizeCollector : customizeCollectors) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> beanFactory.registerBean(customizeCollector);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> customizeCollectors.forEach(&lt;span style="color:#719e07">this&lt;/span>::addListener);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>需要注意，以上几个实现均继承自 SimpleMetricsEventMulticaster，因此它们都具有注册监听、转发事件的能力。它们将自己注册到对应领域的指标 Collector 中，并在收到指标事件时转发到自己注册的监听器中。&lt;/p></description></item><item><title>4-指标转换与导出</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</guid><description>&lt;h2 id="四指标转换与导出">四、指标转换与导出&lt;/h2>
&lt;p>本章主要梳理指标收集完成后，向外部收集器导出的流程。&lt;/p>
&lt;p>通过之前的分析，我们知道不同类型指标的收集分别由各个 Collector 实现进行。它们底层的 MetricsCollector 接口定义了指标导出的操作。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SPI&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">MetricsCollector&lt;/span>&lt;span style="color:#719e07">&amp;lt;&lt;/span>E &lt;span style="color:#268bd2">extends&lt;/span> TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> MetricsLifeListener&lt;span style="color:#719e07">&amp;lt;&lt;/span>E&lt;span style="color:#719e07">&amp;gt;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isCollectEnabled&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Collect metrics as {@link MetricSample}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return List of MetricSample
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricSample&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">collect&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>而指标报告器 (MetricsReporter) 的实现会定时调用Collector 的 &lt;code>collect&lt;/code> 方法，更新并导出指标数据。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">MetricsReporter&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新统计数据，定时调用collect()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refreshData&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取指标数据&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">getResponse&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取带指标名的指标样本（单个指标）&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> String &lt;span style="color:#268bd2">getResponseWithName&lt;/span>(String metricsName) { &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>; }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>指标报告器有两个实现：DefaultMetricsReporter 和 PrometheusMetricsReporter，它们都实现自 AbstractMetricsRepoter，并使用它的指标刷新逻辑 (&lt;code>refreshData&lt;/code>方法)。&lt;/p></description></item><item><title>Dubbo 连接异构微服务体系 - 多协议&amp;多注册中心</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/01/05/dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB-%E5%A4%9A%E5%8D%8F%E8%AE%AE%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</link><pubDate>Thu, 05 Jan 2023 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2023/01/05/dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB-%E5%A4%9A%E5%8D%8F%E8%AE%AE%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</guid><description>&lt;p>从编程开发的角度来说，Dubbo 首先是一款 RPC 服务框架，它最大的优势在于提供了面向接口代理的服务编程模型，对开发者屏蔽了底层的远程通信细节。同时 Dubbo 也是一款服务治理框架，它为分布式部署的微服务提供了服务发现、流量调度等服务治理解决方案。&lt;/p>
&lt;p>在这篇文章中，我们将以以上基础能力为背景，尝试突破 Dubbo 体系自身，探索如何利用 Dubbo 对多协议、多服务发现模型的支持，来实现异构微服务体系间的互联互通。在实际业务场景中，这可以用来解决异构技术体系共存场景下的通信问题，帮助公司实现在异构技术体系间作平滑迁移，解决大规模跨区域、多集群部署场景的地址发现及流量调度等问题。&lt;/p>
&lt;h2 id="面向接口代理的透明服务开发框架">面向接口代理的透明服务开发框架&lt;/h2>
&lt;p>我们还是从 &lt;strong>Dubbo 是一个微服务开发框架&lt;/strong> 这个大家熟知的概念开始。就像 Spring 是开发 Java 应用的基础框架一样，我们经常会选用 Dubbo 作为开发微服务业的基础框架。 Dubbo 框架的最大优势我认为就在其面向接口的编程模型，使得开发远程服务调用就像开发本地服务一样（以 Java 语言为例）：&lt;/p>
&lt;ol>
&lt;li>服务定义&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">GreetingsService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>消费方调用服务&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 和调用本地服务一样，完全透明。&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Reference&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> GreetingService greetingService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doSayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greetingService.sayHi(&lt;span style="color:#2aa198">&amp;#34;Hello world!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下图是 Dubbo 的基本工作原理图，服务提供者与服务消费者之间通过注册中心协调地址，通过约定的协议实现数据交换。&lt;/p>
&lt;p>&lt;img alt="Dubbo basic work flow" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/2023/01/protocols/img.png">&lt;/p>
&lt;h2 id="同构异构微服务体系面临的问题">同构/异构微服务体系面临的问题&lt;/h2>
&lt;p>关于 Dubbo 协议本身及其服务治理相关功能细节并不是本文的重点，我们今天将从一个更高的层次，来看看公司内部构建微服务体系所面的挑战，以及 Dubbo 能为架构选型和迁移等提供哪些解决思路。&lt;/p>
&lt;p>一个公司内部的微服务可能都是基于某一个相同的服务框架开发的，比如说 Dubbo，对于这样的架构，我们称之为是&lt;strong>同构的微服务体系&lt;/strong>；而有些公司的微服务可能是使用多个不同的服务框架所建设，我们称之为&lt;strong>异构的微服务体系&lt;/strong>，多个不同技术栈微服务体系的共存在大型组织内还是非常普遍的，造成这种局面可能有很多原因。比如，可能是遗留系统带来的，也可能是公司正在做技术栈迁移，或者就是不同业务部门为了满足各自特殊需求而做的独立选型（这也意味着异构微服务体系的长期共存）。&lt;/p>
&lt;p>&lt;strong>1. 异构微服务体系共存&lt;/strong>&lt;/p>
&lt;p>我们很容易想到的一个挑战是：**不同的体系间通常是使用不同的 RPC 通信协议、部署独立的注册中心集群，面对这种多协议、多注册中心集群的场景，要如何实现相互之间透明的地址发现和透明的 RPC 调用？**如果我们什么都不做，那么每个微服务体系就只能感知到自己体系内的服务状态，流量也在各自的体系内封闭。而要做到从体系 A 平滑的迁移到体系 B，或者想长期的保持公司内部多个体系的共存，则解决不同体系间的互联互通，实现流量的透明调度将是非常重要的环节。&lt;/p></description></item><item><title>Dubbo 3 之 Triple 流控反压原理解析</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/12/28/dubbo-3-%E4%B9%8B-triple-%E6%B5%81%E6%8E%A7%E5%8F%8D%E5%8E%8B%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</link><pubDate>Wed, 28 Dec 2022 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/12/28/dubbo-3-%E4%B9%8B-triple-%E6%B5%81%E6%8E%A7%E5%8F%8D%E5%8E%8B%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</guid><description>&lt;p>Triple 是 Dubbo 3 提出的基于 HTTP2 的开放协议，
旨在解决 Dubbo 2 私有协议带来的互通性问题。
Triple 基于 HTTP/2 定制自己的流控，支持通过特定的异常通知客户端业务层服务端负载高情况，
保护了服务端被大流量击垮，提高系统高可用能力。&lt;/p>
&lt;h2 id="一流控反压现状">一、流控反压现状&lt;/h2>
&lt;p>客户端和服务器端在接收数据的时候有一个缓冲区来临时存储数据，
但是缓冲区的大小是有限制的，所以有可能会出现缓冲区溢出的情况，
HTTP 通过流控保护数据溢出丢失风险。&lt;/p>
&lt;h3 id="1http1-流控">1、HTTP/1 流控&lt;/h3>
&lt;p>在 HTTP/1.1 中，流量的控制依赖的是底层TCP协议，在客户端和服务器端建立连接的时候，
会使用系统默认的设置来建立缓冲区。在数据进行通信的时候，会告诉对方它的接收窗口的大小，
这个接收窗口就是缓冲区中剩余的可用空间。如果接收窗口大小为零，则说明接收方缓冲区已满，
则发送方将不再发送数据，直到客户端清除其内部缓冲区，然后请求恢复数据传输。&lt;/p>
&lt;h3 id="2http2-流控">2、HTTP/2 流控&lt;/h3>
&lt;p>HTTP/2 使用了多路复用机制，一个TCP连接可以有多个 HTTP/2 连接，
故在 HTTP/2 中，有更加精细的流控制机制，允许服务端实现自己数据流和连接级的流控制。
服务端与客户端第一次连接时，会通过发送 &lt;code>HTTP/2 SettingsFrame&lt;/code>设置初始化的流控窗口大小，
用于 &lt;code>Stream&lt;/code> 级别流控，默认为 65,535 字节。
定好流控窗口后，每次客户端发送数据就会减少流控窗口的大小，
服务端收到数据后会发送窗口更新包（&lt;code>WINDOW_UPDATE frame&lt;/code>）通知客户端更新窗口。
客户端收到窗口更新包后就会增加对应值的流控窗口，从而达到动态控制的目的。&lt;/p>
&lt;p>&lt;img alt="1.png" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/2022/12/28/triple/1.png">&lt;/p>
&lt;h2 id="二triple流控反压">二、Triple流控反压&lt;/h2>
&lt;p>Netty 基于 HTTP/2 实现了基础的流控，当服务端负载过高，客户端发送窗口为 0 时，
新增请求就无法被发送出去，会在缓存到客户端待发送请求队列中，缓存数据过大，
就会造成客户端内存溢出，影响业务程序。&lt;/p>
&lt;p>Triple 基于 Netty 实现了 HTTP/2 协议，通过 &lt;code>HTTP/2 FlowController&lt;/code>接口统一封装，
在实现分为进站（inbound）和出站（outbound）两个维度的实现。
Triple 在 inbound 流量上使用了 Netty 的默认流控实现，
在 outbound 上实现了自己流控，基于服务端负载，
将服务端流量压力透传到客户端业务层，实现客户端的业务反压，暂停业务继续发送请求，
保护服务端不被大流量击垮。&lt;/p></description></item><item><title>Triple 协议支持 Java 异常回传的设计与实现</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/12/19/triple-%E5%8D%8F%E8%AE%AE%E6%94%AF%E6%8C%81-java-%E5%BC%82%E5%B8%B8%E5%9B%9E%E4%BC%A0%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</link><pubDate>Mon, 19 Dec 2022 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/12/19/triple-%E5%8D%8F%E8%AE%AE%E6%94%AF%E6%8C%81-java-%E5%BC%82%E5%B8%B8%E5%9B%9E%E4%BC%A0%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>在一些业务场景, 往往需要自定义异常来满足特定的业务, 主流用法是在catch里抛出异常, 例如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">deal&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//doSomething &lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span>(IGreeterException e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> e;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>或者通过ExceptionBuilder，把相关的异常对象返回给consumer：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>provider.send(&lt;span style="color:#719e07">new&lt;/span> ExceptionBuilders.IGreeterExceptionBuilder()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .setDescription(&amp;#39;异常描述信息&amp;#39;); 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在抛出异常后, 通过捕获和instanceof来判断特定的异常, 然后做相应的业务处理，例如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greeterProxy.echo(REQUEST_MSG);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>} &lt;span style="color:#719e07">catch&lt;/span> (IGreeterException e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//做相应的处理&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 Dubbo 2.x 版本，可以通过上述方法来捕获 Provider 端的异常。
而随着云原生时代的到来， Dubbo 也开启了 3.0 的里程碑。&lt;/p>
&lt;p>Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生，
在 3.0 的许多特性中，很重要的一个改动就是支持&lt;strong>新的一代Rpc协议Triple&lt;/strong>。&lt;/p>
&lt;p>Triple 协议基于 HTTP 2.0 进行构建，对网关的穿透性强，&lt;strong>兼容 gRPC&lt;/strong>，
提供 Request Response、Request Streaming、Response Streaming、
Bi-directional Streaming 等通信模型；
从 Triple 协议开始，Dubbo 还支持基于 IDL 的服务定义。&lt;/p></description></item><item><title>Proxyless Mesh在Dubbo中的实践</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</link><pubDate>Mon, 05 Sep 2022 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>随着 Dubbo 3.1 的 release，Dubbo 在云原生的路上又迈出了重要的一步。在这个版本中添加了 Proxyless Mesh 的新特性，Dubbo Proxyless Mesh 直接实现 xDS 协议解析，
实现 Dubbo 与 Control Plane 的直接通信，进而实现控制面对流量管控、服务治理、可观测性、安全等的统一管控，规避 Sidecar 模式带来的性能损耗与部署架构复杂性。&lt;/p>
&lt;h2 id="什么是service-mesh">什么是Service Mesh&lt;/h2>
&lt;p>Service Mesh 又译作 “服务网格”，作为服务间通信的基础设施层。Buoyant 公司的 CEO Willian Morgan 在他的这篇文章 &lt;a href="https://linkerd.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/">WHAT’S A Service Mesh? AND WHY DO I NEED ONE? &lt;/a>
中解释了什么是 Service Mesh，为什么云原生应用需要 Service Mesh。&lt;/p>
&lt;p>&lt;strong>下面是 Willian Morgan 对 Service Mesh 的解释。&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>A Service Mesh is a dedicated infrastructure layer for handling service-to-service communication. 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>It’s responsible for the reliable delivery of requests through the complex topology of services 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>that comprise a modern, cloud native application. In practice, the Service Mesh is typically implemented 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>as an array of lightweight network proxies that are deployed alongside application code, without the 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application needing to be aware.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>翻译成中文&lt;/strong>&lt;/p></description></item><item><title>22-Dubbo3消费者自动感应决策应用级服务发现原理</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="22-dubbo3消费者自动感应决策应用级服务发现原理">22-Dubbo3消费者自动感应决策应用级服务发现原理&lt;/h1>
&lt;h2 id="221-简介">22.1 简介&lt;/h2>
&lt;p>这里要说的内容对Dubbo2迁移到Dubbo3的应用比较有帮助，消费者应用级服务发现做了一些自动决策的逻辑来决定当前消费者是应用级发现还是接口级服务发现，这里与前面说的提供者双注册的原理是对等的，提供者默认同时进行应用级注册和接口级注册，消费者对提供者注册的数据来决定使用应用级发现或者接口级发现。这些都是默认的行为，当然对于消费者来说还可以自定义其他的迁移规则，具体的需要我们详细来看逻辑。&lt;/p>
&lt;p>如果说对于迁移过程比较感兴趣可以直接去官网看文档相对来说还是比较清晰:&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docs/migration/migration-service-discovery/">https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/&lt;/a>&lt;/p>
&lt;p>这里再借官网的图来用用，迁移过程主要如下所示：
第一个图是提供者双注册的图：
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/v3/migration/provider-registration.png">&lt;/p>
&lt;p>第二个图是消费者订阅决策的图：
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/v3/migration/consumer-subscription.png">&lt;/p>
&lt;p>第三个图就是精确到消费者订阅的代码层的逻辑了，消费者服务间调用通过一个Invoker类型对象来进行对象，如下图所示消费者代理对象通过创建一个迁移容错的调用器对象来对应用级或者接口级订阅进行适配如下所示
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/v3/migration/migration-cluster-invoker.png">&lt;/p>
&lt;p>第二个图和第三个图是重点要关注的这一个文章的内容主要就是说这里的逻辑。&lt;/p>
&lt;p>关于代码位置如果不知道是如何调用到这一块逻辑的可以查看博文&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/21-dubbo-xiao-fei-zhe-yin-yong-fu-wu-de-ru-kou/">《21-Dubbo3消费者引用服务入口》&lt;/a>&lt;/p>
&lt;p>这里直接将代码位置定位到：RegistryProtocol类型的interceptInvoker方法中：
如下所示：&lt;/p>
&lt;p>RegistryProtocol类型的interceptInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">interceptInvoker&lt;/span>(ClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker, URL url, URL consumerUrl) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//目前存在的扩展类型为RegistryProtocolListener监听器的实现类型MigrationRuleListener &lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryProtocolListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> listeners &lt;span style="color:#719e07">=&lt;/span> findRegistryProtocolListeners(url);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isEmpty(listeners)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (RegistryProtocolListener listener : listeners) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listener.onRefer(&lt;span style="color:#719e07">this&lt;/span>, invoker, consumerUrl, url);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该方法尝试加载所有RegistryProtocolListener定义，这些定义通过与定义的交互来控制调用器的行为，然后使用这些侦听器更改MigrationInvoker的状态和行为。当前可用的监听器是MigrationRuleListener，用于通过动态变化的规则控制迁移行为。&lt;/p>
&lt;h2 id="222-migrationrulelistener-类型的onrefer方法">22.2 MigrationRuleListener 类型的onRefer方法&lt;/h2>
&lt;p>直接来看代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onRefer&lt;/span>(RegistryProtocol registryProtocol, ClusterInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker, URL consumerUrl, URL registryURL) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个对应invoker对象的MigrationRuleHandler类型对象 然后将其存放在缓存Map&amp;lt;MigrationInvoker, MigrationRuleHandler&amp;gt;类型对象handles中&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MigrationRuleHandler&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> migrationRuleHandler &lt;span style="color:#719e07">=&lt;/span> handlers.computeIfAbsent((MigrationInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span>) invoker, _key &lt;span style="color:#719e07">-&amp;gt;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ((MigrationInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span>) invoker).setMigrationRuleListener(&lt;span style="color:#719e07">this&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MigrationRuleHandler&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>((MigrationInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span>) invoker, consumerUrl);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> });
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//迁移规则执行 rule是封装了迁移的配置规则的信息对应类型MigrationRule类型，在初始化对象的时候进行了配置初始化&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> migrationRuleHandler.doMigrate(rule);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于这个igrationRule的文可以直接看官方的文档比较详细：&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/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">地址迁移规则说明&lt;/a>&lt;/p></description></item><item><title>21-Dubbo3消费者引用服务入口</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="21-dubbo3消费者引用服务入口">21-Dubbo3消费者引用服务入口&lt;/h1>
&lt;h2 id="211-简介">21.1 简介&lt;/h2>
&lt;p>前面我们通过Demo说了一个服务引用配置的创建。另外也在前面的文章说了服务提供者的启动完整过程，不过在说服务提供者启动的过程中并未提到服务消费者是如何发现服务，如果调用服务的，这里先就不再说关于服务消费者启动的一个细节了，直接来看前面未提到的服务消费者是如何引用到服务提供者提供的服务的。
先来回顾下样例代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ConsumerApplication&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>(String&lt;span style="color:#719e07">[]&lt;/span> args) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> runWithBootstrap();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">runWithBootstrap&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference.setInterface(DemoService.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference.setGeneric(&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference.setProtocol(&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap.getInstance();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig(&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-consumer&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig.setQosEnable(&lt;span style="color:#cb4b16">false&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig.setQosPort(&lt;span style="color:#719e07">-&lt;/span>1);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap.application(applicationConfig)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .registry(&lt;span style="color:#719e07">new&lt;/span> RegistryConfig(&lt;span style="color:#2aa198">&amp;#34;zookeeper://8.131.79.126:2181&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .protocol(&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig(CommonConstants.DUBBO, &lt;span style="color:#719e07">-&lt;/span>1))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .reference(reference)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DemoService demoService &lt;span style="color:#719e07">=&lt;/span> bootstrap.getCache().get(reference);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String message &lt;span style="color:#719e07">=&lt;/span> demoService.sayHello(&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(message);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// generic invoke&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> GenericService genericService &lt;span style="color:#719e07">=&lt;/span> (GenericService) demoService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object genericInvokeResult &lt;span style="color:#719e07">=&lt;/span> genericService.$invoke(&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>, &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span>{String.class.getName()},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[]&lt;/span>{&lt;span style="color:#2aa198">&amp;#34;dubbo generic invoke&amp;#34;&lt;/span>});
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(genericInvokeResult);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这段代码我们前面详细说了服务引用的配置ReferenceConfig和Dubbo启动器启动应用的过程DubboBootstrap，后面我们直接定位到消费者引用服务的代码位置来看。&lt;/p></description></item><item><title>20-Dubbo3服务引用配置ReferenceConfig</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="20-dubbo3服务引用配置referenceconfig">20-Dubbo3服务引用配置ReferenceConfig&lt;/h1>
&lt;h2 id="201-简介">20.1 简介&lt;/h2>
&lt;p>前面简单介绍了一下消费者的例子，消费者创建的第一步就是先进行消费者信息的配置对应类型为ReferenceConfig，这里详细来看ReferenceConfig包含哪些信息？先简单了解下消费者配置的类型关系如下图所示：引用配置与服务配置类型都是通过继承接口配置来扩展的，在分析生产者的时候详细介绍过服务相关的配置，这里来详细看消费者引用者的相关配置信息.
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/source-blog/20-refe.png">&lt;/p>
&lt;p>前面例子说了消费者配置对象的创建主要是通过如下代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个配置类型的对象创建过程并没有太多的逻辑这里主要来说下各种配置信息：
服务消费者引用服务配置。对应的配置类： &lt;code>org.apache.dubbo.config.ReferenceConfig&lt;/code>&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">属性&lt;/th>
 &lt;th style="text-align: left">对应URL参数&lt;/th>
 &lt;th style="text-align: left">类型&lt;/th>
 &lt;th style="text-align: left">是否必填&lt;/th>
 &lt;th style="text-align: left">缺省值&lt;/th>
 &lt;th style="text-align: left">作用&lt;/th>
 &lt;th style="text-align: left">描述&lt;/th>
 &lt;th style="text-align: left">兼容性&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">id&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">&lt;strong>必填&lt;/strong>&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">配置关联&lt;/td>
 &lt;td style="text-align: left">服务引用BeanId&lt;/td>
 &lt;td style="text-align: left">1.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">interface&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">class&lt;/td>
 &lt;td style="text-align: left">&lt;strong>必填&lt;/strong>&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务发现&lt;/td>
 &lt;td style="text-align: left">服务接口名&lt;/td>
 &lt;td style="text-align: left">1.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">version&lt;/td>
 &lt;td style="text-align: left">version&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务发现&lt;/td>
 &lt;td style="text-align: left">服务版本，与服务提供者的版本一致&lt;/td>
 &lt;td style="text-align: left">1.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">group&lt;/td>
 &lt;td style="text-align: left">group&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务发现&lt;/td>
 &lt;td style="text-align: left">服务分组，当一个接口有多个实现，可以用分组区分，必需和服务提供方一致&lt;/td>
 &lt;td style="text-align: left">1.0.7以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">timeout&lt;/td>
 &lt;td style="text-align: left">timeout&lt;/td>
 &lt;td style="text-align: left">long&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">缺省使用&lt;code>&amp;lt;dubbo:consumer&amp;gt;&lt;/code>的timeout&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">服务方法调用超时时间(毫秒)&lt;/td>
 &lt;td style="text-align: left">1.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">retries&lt;/td>
 &lt;td style="text-align: left">retries&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">缺省使用&lt;code>&amp;lt;dubbo:consumer&amp;gt;&lt;/code>的retries&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">远程服务调用重试次数，不包括第一次调用，不需要重试请设为0&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">connections&lt;/td>
 &lt;td style="text-align: left">connections&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">缺省使用&lt;code>&amp;lt;dubbo:consumer&amp;gt;&lt;/code>的connections&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">对每个提供者的最大连接数，rmi、http、hessian等短连接协议表示限制连接数，dubbo等长连接协表示建立的长连接个数&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">loadbalance&lt;/td>
 &lt;td style="text-align: left">loadbalance&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">缺省使用&lt;code>&amp;lt;dubbo:consumer&amp;gt;&lt;/code>的loadbalance&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">负载均衡策略，可选值：random,roundrobin,leastactive，分别表示：随机，轮询，最少活跃调用&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">async&lt;/td>
 &lt;td style="text-align: left">async&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">缺省使用&lt;code>&amp;lt;dubbo:consumer&amp;gt;&lt;/code>的async&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">是否异步执行，不可靠异步，只是忽略返回值，不阻塞执行线程&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">generic&lt;/td>
 &lt;td style="text-align: left">generic&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">缺省使用&lt;code>&amp;lt;dubbo:consumer&amp;gt;&lt;/code>的generic&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">是否缺省泛化接口，如果为泛化接口，将返回GenericService&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">check&lt;/td>
 &lt;td style="text-align: left">check&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">缺省使用&lt;code>&amp;lt;dubbo:consumer&amp;gt;&lt;/code>的check&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">启动时检查提供者是否存在，true报错，false忽略&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">url&lt;/td>
 &lt;td style="text-align: left">url&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">点对点直连服务提供者地址，将绕过注册中心&lt;/td>
 &lt;td style="text-align: left">1.0.6以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">stub&lt;/td>
 &lt;td style="text-align: left">stub&lt;/td>
 &lt;td style="text-align: left">class/boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">服务接口客户端本地代理类名，用于在客户端执行本地逻辑，如本地缓存等，该本地代理类的构造函数必须允许传入远程代理对象，构造函数如：public XxxServiceLocal(XxxService xxxService)&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">mock&lt;/td>
 &lt;td style="text-align: left">mock&lt;/td>
 &lt;td style="text-align: left">class/boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">服务接口调用失败Mock实现类名，该Mock类必须有一个无参构造函数，与Local的区别在于，Local总是被执行，而Mock只在出现非业务异常(比如超时，网络异常等)时执行，Local在远程调用之前执行，Mock在远程调用后执行。&lt;/td>
 &lt;td style="text-align: left">Dubbo1.0.13及其以上版本支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">cache&lt;/td>
 &lt;td style="text-align: left">cache&lt;/td>
 &lt;td style="text-align: left">string/boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">以调用参数为key，缓存返回结果，可选：lru, threadlocal, jcache等&lt;/td>
 &lt;td style="text-align: left">Dubbo2.1.0及其以上版本支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">validation&lt;/td>
 &lt;td style="text-align: left">validation&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">是否启用JSR303标准注解验证，如果启用，将对方法参数上的注解进行校验&lt;/td>
 &lt;td style="text-align: left">Dubbo2.1.0及其以上版本支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">proxy&lt;/td>
 &lt;td style="text-align: left">proxy&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">javassist&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">选择动态代理实现策略，可选：javassist, jdk&lt;/td>
 &lt;td style="text-align: left">2.0.2以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">client&lt;/td>
 &lt;td style="text-align: left">client&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">客户端传输类型设置，如Dubbo协议的netty或mina。&lt;/td>
 &lt;td style="text-align: left">Dubbo2.0.0以上版本支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">registry&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">缺省将从所有注册中心获服务列表后合并结果&lt;/td>
 &lt;td style="text-align: left">配置关联&lt;/td>
 &lt;td style="text-align: left">从指定注册中心注册获取服务列表，在多个注册中心时使用，值为&lt;code>&amp;lt;dubbo:registry&amp;gt;&lt;/code>的id属性，多个注册中心ID用逗号分隔&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">owner&lt;/td>
 &lt;td style="text-align: left">owner&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">调用服务负责人，用于服务治理，请填写负责人公司邮箱前缀&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">actives&lt;/td>
 &lt;td style="text-align: left">actives&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">0&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">每服务消费者每服务每方法最大并发调用数&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">cluster&lt;/td>
 &lt;td style="text-align: left">cluster&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">failover&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">集群方式，可选：failover/failfast/failsafe/failback/forking&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">filter&lt;/td>
 &lt;td style="text-align: left">reference.filter&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">default&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">服务消费方远程调用过程拦截器名称，多个名称用逗号分隔&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">listener&lt;/td>
 &lt;td style="text-align: left">invoker.listener&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">default&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">服务消费方引用服务监听器名称，多个名称用逗号分隔&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">layer&lt;/td>
 &lt;td style="text-align: left">layer&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">服务调用者所在的分层。如：biz、dao、intl:web、china:acton。&lt;/td>
 &lt;td style="text-align: left">2.0.7以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">init&lt;/td>
 &lt;td style="text-align: left">init&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">false&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">是否在afterPropertiesSet()时饥饿初始化引用，否则等到有人注入或引用该实例时再初始化。&lt;/td>
 &lt;td style="text-align: left">2.0.10以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">protocol&lt;/td>
 &lt;td style="text-align: left">protocol&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">只调用指定协议的服务提供方，其它协议忽略。&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>原文地址：&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/20-dubbo3-fu-wu-yin-yong-pei-zhi-referenceconfig/">20-Dubbo3服务引用配置ReferenceConfig&lt;/a>&lt;/p></description></item><item><title>19 重新来过从一个服务消费者的Demo说起</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="19-重新来过从一个服务消费者的demo说起">19 重新来过从一个服务消费者的Demo说起&lt;/h1>
&lt;p>为了更方便了解原理,我们先来编写一个Demo,从例子中来看源码实现:，前面说了提供者现在已经有服务注册上去了，那接下来我们编写一个消费者的例子来进行服务发现与服务RPC调用。&lt;/p>
&lt;h2 id="191-启动zookeeper">19.1 启动Zookeeper&lt;/h2>
&lt;p>为了Demo可以正常启动,需要我们先在本地启动一个Zookeeper如下图所示:
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/source-blog/19-zk.png">&lt;/p>
&lt;h2 id="192-服务消费者">19.2 服务消费者&lt;/h2>
&lt;p>接下来给大家贴一下示例源码,这个源码来源于Dubbo源码目录的	dubbo-demo/dubbo-demo-api 目录下面的dubbo-demo-api-consumer子项目,这里我做了删减,方便看核心代码:
首先我们定义一个服务接口如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 同步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 用于异步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture.completedFuture(sayHello(name));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>服务实现类如下:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.RpcContext;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.Logger;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.LoggerFactory;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Logger logger &lt;span style="color:#719e07">=&lt;/span> LoggerFactory.getLogger(DemoServiceImpl.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.info(&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, request from consumer: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext.getServiceContext().getRemoteAddress());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext.getServiceContext().getLocalAddress();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="193-启用服务消费者">19.3 启用服务消费者&lt;/h2>
&lt;p>有了服务接口之后我们来启用服务,启用服务的源码如下:
这里如果要启动消费者,主要要修改QOS端口这里我已经配置可以直接复用&lt;/p></description></item><item><title>18-Dubbo3元数据服务MetadataService的导出</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="18-dubbo3元数据服务metadataservice的导出">18-Dubbo3元数据服务MetadataService的导出&lt;/h1>
&lt;h2 id="181-简介">18.1 简介&lt;/h2>
&lt;p>MetadataService
此服务用于公开Dubbo进程内的元数据信息。典型用途包括：&lt;/p>
&lt;ul>
&lt;li>使用者查询提供者的元数据信息，以列出接口和每个接口的配置&lt;/li>
&lt;li>控制台（dubbo admin）查询特定进程的元数据，或聚合所有进程的数据。在Dubbo2.x的时候，所有的服务数据都是以接口的形式注册在注册中心.&lt;/li>
&lt;/ul>
&lt;p>Dubbo3将部分数据抽象为元数据的形式来将数据存放在元数据中心，然后元数据由服务提供者提供给消费者而不是再由注册中心进行推送，如下图所示：&lt;/p>
&lt;p>&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/source-blog/18-metadata.png">&lt;/p>
&lt;p>&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/source-blog/18-metadata3.png">
引入 MetadataService 元数据服务服务的好处
• 由中心化推送转向点对点拉取（Consumer - Proroder）
• 易于扩展更多的参数
• 更多的数据量
• 对外暴露更多的治理数据&lt;/p>
&lt;h2 id="182-metadataservice的导出过程">18.2 MetadataService的导出过程&lt;/h2>
&lt;p>了解元数据的到处过程，这个就要继续前面博客往后的代码了前面博客说了一个服务发布之后的服务信息的双注册数据，这里继续看下导出服务之后的代码：
先来简单回顾下模块发布的启动生命周期方法：&lt;/p>
&lt;p>DefaultModuleDeployer类型的start方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> Future &lt;span style="color:#268bd2">start&lt;/span>() &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onModuleStarting();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// initialize&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationDeployer.initialize();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialize();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// export services&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportServices();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// prepare application instance&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// exclude internal module to avoid wait itself&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (moduleModel &lt;span style="color:#719e07">!=&lt;/span> moduleModel.getApplicationModel().getInternalModule()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationDeployer.prepareInternalModule();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// refer services&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referServices();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if no async export/refer services, just set started&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (asyncExportingFutures.isEmpty() &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> asyncReferringFutures.isEmpty()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onModuleStarted();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ....
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> startFuture;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面的博客我们已经说了服务提供者导出服务的方法如下：&lt;/p></description></item><item><title>17-Dubbo服务提供者的双注册原理</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="17-dubbo服务提供者的双注册原理">17-Dubbo服务提供者的双注册原理&lt;/h1>
&lt;h2 id="171-简介">17.1 简介&lt;/h2>
&lt;p>上个博客&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/15-dubbo-de-san-da-zhong-xin-zhi-yuan-shu-ju-zhong-xin-yuan-ma-jie-xi/">《15-Dubbo的三大中心之元数据中心源码解析》&lt;/a>导出服务端的时候多次提到了元数据中心，注册信息的注册。
Dubbo3出来时间不太长，对于现在的用户来说大部分使用的仍旧是Dubbo2.x，
Dubbo3 比较有特色也是会直接使用到的功能就是&lt;strong>应用级服务发现&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>应用级服务发现
&lt;em>从服务/接口粒度到应用粒度的升级，使得 Dubbo 在集群可伸缩性、连接异构微服务体系上更具优势。应用粒度能以更低的资源消耗支持超百万实例规模集群程； 实现与 Spring Cloud、Kubernetes Service 等异构微服务体系的互联互通。&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>对于直接使用Dubbo3的用户还好，可以仅仅开启应用级注册，但是对于Dubbo2.x的用户升级到Dubbo3的用户来说前期都是要开启双注册来慢慢迁移的，既注册传统的接口信息到注册中心，又注册应用信息到注册中心，同时注册应用与接口关系的元数据信息。
关于双注册与服务迁移的过程的使用可以参考官网：
&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docs/migration/migration-service-discovery/">应用级地址发现迁移指南&lt;/a>&lt;/p>
&lt;p>关于官网提供者双注册的图我这里贴一下，方便了解：
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/v3/migration/provider-registration.png">&lt;/p>
&lt;h2 id="172-双注册配置的读取">17.2 双注册配置的读取&lt;/h2>
&lt;h3 id="1721-注册中心地址作为元数据中心">17.2.1 注册中心地址作为元数据中心&lt;/h3>
&lt;p>这个配置的解析过程在前面的博客介绍元数据中心的时候很详细的说了相关链接：&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/15-dubbo-de-san-da-zhong-xin-zhi-yuan-shu-ju-zhong-xin-yuan-ma-jie-xi/">15-Dubbo的三大中心之元数据中心源码解析&lt;/a>&lt;/p>
&lt;p>对应代码位于：DefaultApplicationDeployer类型的startMetadataCenter()方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startMetadataCenter&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#586e75">//如果未配置元数据中心的地址等配置则使用注册中心的地址等配置做为元数据中心的配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> useRegistryAsMetadataCenterIfNecessary();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...省略掉其他代码防止受到干扰&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>具体逻辑是这个方法：
useRegistryAsMetadataCenterIfNecessary&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">useRegistryAsMetadataCenterIfNecessary&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#586e75">//配置缓存中查询元数据配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Collection&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetadataReportConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> metadataConfigs &lt;span style="color:#719e07">=&lt;/span> configManager.getMetadataConfigs();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...省略掉空判断&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#586e75">//查询是否有注册中心设置了默认配置isDefault 设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> defaultRegistries &lt;span style="color:#719e07">=&lt;/span> configManager.getDefaultRegistries();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (defaultRegistries.size() &lt;span style="color:#719e07">&amp;gt;&lt;/span> 0) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//多注册中心遍历&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> defaultRegistries
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .stream()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//筛选符合条件的注册中心 (筛选逻辑就是查看是否有对应协议的扩展支持)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .filter(&lt;span style="color:#719e07">this&lt;/span>::isUsedRegistryAsMetadataCenter)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//注册中心配置映射为元数据中心 映射就是获取需要的配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .map(&lt;span style="color:#719e07">this&lt;/span>::registryAsMetadataCenter)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将元数据中心配置存储在配置缓存中方便后续使用&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .forEach(metadataReportConfig &lt;span style="color:#719e07">-&amp;gt;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...省略掉具体的逻辑&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> });
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于元数据中心地址的获取，主要经过如下逻辑：&lt;/p></description></item><item><title>16-模块发布器发布服务全过程</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="16-模块发布器发布服务全过程">16-模块发布器发布服务全过程&lt;/h1>
&lt;h2 id="161-简介">16.1 简介&lt;/h2>
&lt;p>Dubbo做为服务治理框架,比较核心的就是服务相关的概念,这里我先贴个找到的关于Dubbo工作原理的架构图:
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/source-blog/16-deploy.png">
如果按完整服务启动与订阅的顺序我们可以归结为以下6点:&lt;/p>
&lt;ul>
&lt;li>导出服务(提供者)
&lt;ul>
&lt;li>服务提供方通过指定端口对外暴露服务&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>注册服务(提供者)
&lt;ul>
&lt;li>提供方向注册中心注册自己的信息&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>(服务发现)-订阅服务(消费者)
&lt;ul>
&lt;li>服务调用方通过注册中心订阅自己感兴趣的服务&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>(服务发现)-服务推送(消费者)
&lt;ul>
&lt;li>注册中心向调用方推送地址列表&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>调用服务(消费者调用提供者)
&lt;ul>
&lt;li>调用方选择一个地址发起RPC调用&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>监控服务
&lt;ul>
&lt;li>服务提供方和调用方的统计数据由监控模块收集展示&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>上面的完整的服务启动订阅与调用流程不仅仅适用于Dubbo 同样也适用于其他服务治理与发现的模型, 一般服务发现与服务调用的思路就是这样的,我们将以上内容扩展,暴漏服务可以使用http,tcp,udp等各种协议,注册服务可以注册到Redis,Dns,Etcd,Zookeeper等注册中心中,订阅服务可以主动去注册中心查询服务列表,服务发现可以让注册中心将服务数据动态推送给消费者.Dubbo其实就是基于这种简单的服务模型来扩展出各种功能的支持,来满足服务治理的各种场景,了解了这里可能各位同学就想着自行开发一个简单的微服务框架了。&lt;/p>
&lt;p>回到主题,从以上的服务完整发布调用流程可以看到,所有的功能都是由导出服务(提供者)开始的,只有提供者先提供了服务才可以有真正的服务让消费者调用。&lt;/p>
&lt;p>之前的博客内容 链接:&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/">&amp;laquo;12-全局视野来看Dubbo3.0.8的服务启动生命周期&amp;raquo;&lt;/a> 我们了解了 DefaultModuleDeployer模块器启动的流程,其中在start代码的模版方法中开始了导出服务的功能,这里我们来详细看下服务发布的全过程:&lt;/p>
&lt;p>入口代码: DefaultModuleDeployer的发布服务方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportServices&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 		&lt;span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (ServiceConfigBase sc : configManager.getServices()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportServiceInternal(sc);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="162-导出服务的入口">16.2 导出服务的入口&lt;/h2>
&lt;p>入口代码: DefaultModuleDeployer的发布服务方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportServices&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 		&lt;span style="color:#586e75">//从配置管缓存中查询缓存的所有的服务配置然后逐个服务发布&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (ServiceConfigBase sc : configManager.getServices()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportServiceInternal(sc);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>主要流程为遍历初始化的服务配置列表然后逐个服务开始到处
内部导出服务代码:
exportServiceInternal方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportServiceInternal&lt;/span>(ServiceConfigBase sc) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> serviceConfig &lt;span style="color:#719e07">=&lt;/span> (ServiceConfig&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span>) sc;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务配置刷新 配置优先级覆盖&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>serviceConfig.isRefreshed()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig.refresh();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务已经导出过了就直接返回&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (sc.isExported()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//是否异步方式导出 全局配置或者服务级其中一个配置了异步则异步处理&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (exportAsync &lt;span style="color:#719e07">||&lt;/span> sc.shouldExportAsync()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//异步其实就是使用线程来导出服务serviceExportExecutor&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExecutorService executor &lt;span style="color:#719e07">=&lt;/span> executorRepository.getServiceExportExecutor();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>Void&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> CompletableFuture.runAsync(() &lt;span style="color:#719e07">-&amp;gt;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>sc.isExported()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sc.export();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportedServices.add(sc);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (Throwable t) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.error(getIdentifier() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; export async catch error : &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t.getMessage(), t);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }, executor);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncExportingFutures.add(future);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//同步导出服务&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>sc.isExported()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sc.export();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exportedServices.add(sc);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个逻辑里面做了一些基本的操作,可以直接看注释然后调用ServiceConfig的export的来导出服务,继续往后看服务配置的导出服务方法。&lt;/p></description></item><item><title>15-Dubbo的三大中心之元数据中心源码解析</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="15-dubbo的三大中心之元数据中心源码解析">15-Dubbo的三大中心之元数据中心源码解析&lt;/h1>
&lt;h2 id="151-简介">15.1 简介&lt;/h2>
&lt;p>关于元数据中心的概念对于大部分用户来说是比较陌生的,配置中心的话我们还好理解,对于元数据中心是什么,我们来看下我从官网拷贝过来的一段文字:&lt;/p>
&lt;p>元数据中心在2.7.x版本开始支持，随着应用级别的服务注册和服务发现在Dubbo中落地，&lt;strong>元数据中心也变的越来越重要&lt;/strong>。在以下几种情况下会需要部署元数据中心：&lt;/p>
&lt;ul>
&lt;li>对于一个原先采用老版本Dubbo搭建的应用服务，在迁移到Dubbo 3时，Dubbo 3 会需要一个&lt;strong>元数据中心来维护RPC服务与应用的映射关系（即接口与应用的映射关系）&lt;/strong>，因为如果采用了&lt;strong>应用级别的服务发现和服务注册&lt;/strong>，在注册中心中将&lt;strong>采用“应用 —— 实例列表”结构&lt;/strong>的数据组织形式，&lt;strong>不再是以往的“接口 —— 实例列表”结构的数据组织形式&lt;/strong>，而以往用接口级别的服务注册和服务发现的应用服务在&lt;strong>迁移到应用级别&lt;/strong>时，&lt;strong>得不到接口与应用之间的对应关系&lt;/strong>，从而无法从注册中心得到实例列表信息，所以&lt;strong>Dubbo为了兼容这种场景，在Provider端启动时，会往元数据中心存储接口与应用的映射关系&lt;/strong>。&lt;/li>
&lt;li>为了让&lt;strong>注册中心更加聚焦与地址的发现和推送能力&lt;/strong>，&lt;strong>减轻注册中心的负担&lt;/strong>，元数据中心承载了所有的服务元数据、大量接口/方法级别配置信息等，无论是接口粒度还是应用粒度的服务发现和注册，元数据中心都起到了重要的作用。&lt;/li>
&lt;li>如果有以上两种需求，都可以选择部署元数据中心，并通过Dubbo的配置来集成该元数据中心。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>元数据中心并不依赖于注册中心和配置中心&lt;/strong>，用户可以自由选择是否集成和部署元数据中心，如下图所示：&lt;/p>
&lt;p>&lt;img alt="/imgs/v3/concepts/centers-metadata.png" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/v3/concepts/centers-metadata.png">&lt;/p>
&lt;p>该图中不配备配置中心，意味着可以不需要全局管理配置的能力。该图中不配备注册中心，意味着可能采用了Dubbo mesh的方案，也可能不需要进行服务注册，仅仅接收直连模式的服务调用。
官网参考文章地址:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docs/concepts/registry-configcenter-metadata/">部署架构（注册中心 配置中心 元数据中心&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docs/references/metadata/">元数据参考手册&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>综上所述可以用几句话概括下:&lt;/p>
&lt;ul>
&lt;li>元数据中心来维护RPC服务与应用的映射关系（即接口与应用的映射关系）来兼容接口与应用之间的对应关系&lt;/li>
&lt;li>让注册中心更加聚焦与地址的发现和推送能力&lt;/li>
&lt;/ul>
&lt;p>元数据中心的启动是在DefaultApplicationDeployer中的初始化方法 initialize() 中:如下所示&lt;/p>
&lt;p>这里只看下 startMetadataCenter();方法即可&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (initialized) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> (startLock) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (initialized) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register shutdown hook&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registerShutdownHook();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startConfigCenter();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadApplicationConfigs();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initModuleDeployers();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// @since 2.7.8&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startMetadataCenter();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialized &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (logger.isInfoEnabled()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.info(getIdentifier() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; has been initialized!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="152-深入探究元数据中心的启动过程">15.2 深入探究元数据中心的启动过程&lt;/h2>
&lt;h3 id="1521-启动元数据中心的代码全貌">15.2.1 启动元数据中心的代码全貌&lt;/h3>
&lt;p>关于元数据中心我们看下 startMetadataCenter()方法来大致了解下整个流程&lt;/p></description></item><item><title>14-Dubbo配置加载全解析</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="14-dubbo配置加载全解析">14-Dubbo配置加载全解析&lt;/h1>
&lt;h2 id="141-回到启动器的初始化过程">14.1 回到启动器的初始化过程&lt;/h2>
&lt;p>在应用程序启动的时候会调用发布器的启动方法 ,然后调用初始化方法,在发布器DefaultApplicationDeployer中的初始化方法initialize() 如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initialize&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (initialized) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Ensure that the initialization is completed when concurrent calls&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> (startLock) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (initialized) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// register shutdown hook&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registerShutdownHook();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startConfigCenter();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadApplicationConfigs();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initModuleDeployers();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// @since 2.7.8&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startMetadataCenter();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialized &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (logger.isInfoEnabled()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.info(getIdentifier() &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; has been initialized!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>初始化过程中会先启动配置中心配置信息处理,然后 调用加载初始化应用程序配置方法loadApplicationConfigs();进行配置加载
关于配置的官方文档链接为 &lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docs/references/configuration/overview/">配置概述&lt;/a>&lt;/p></description></item><item><title>13-Dubbo的三大中心之配置中心</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="13-dubbo的三大中心之配置中心">13-Dubbo的三大中心之配置中心&lt;/h1>
&lt;h2 id="131-配置中心简介">13.1 配置中心简介&lt;/h2>
&lt;p>百度了一段不错的文字来介绍配置中心，我看了下肯定比我写的好多了，那我就直接拷贝过来一起看：&lt;/p>
&lt;p>&lt;em>对于传统的单体应用而言，常使用配置文件来管理所有配置，比如SpringBoot的application.yml文件，但是在微服务架构中全部手动修改的话很麻烦而且不易维护。微服务的配置管理一般有以下需求：&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>&lt;strong>集中配置管理&lt;/strong>，一个微服务架构中可能有成百上千个微服务，所以集中配置管理是很重要的。&lt;/em>&lt;/li>
&lt;li>&lt;em>&lt;strong>不同环境不同配置&lt;/strong>，比如数据源配置在不同环境（开发，生产，测试）中是不同的。&lt;/em>&lt;/li>
&lt;li>&lt;em>&lt;strong>运行期间可动态调整&lt;/strong>。例如，可根据各个微服务的负载情况，动态调整数据源连接池大小等。&lt;/em>&lt;/li>
&lt;li>&lt;em>&lt;strong>配置修改后可自动更新&lt;/strong>。如配置内容发生变化，微服务可以自动更新配置。&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>综上所述对于微服务架构而言，一套统一的，通用的管理配置机制是不可缺少的主要组成部分。常见的做法就是通过配置服务器进行管理。&lt;/p>
&lt;p>不过对于来看这个文章的小伙伴应该大部分对配置中心都会比较了解，分布式配置中心实现简单一点就是借助Zookeeper来协助存储，变更推送，不过为了实现各种不同的业务需求，市面上已经有很多很可靠的配置中心可用了，比如我从其他地方拷贝过来的图（虽然不是最新的但是可以供大家参考下）：&lt;/p>
&lt;p>&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/source-blog/register.png">&lt;/p>
&lt;p>每个配置中心都有自己的实现，如果对配置中心感兴趣的小伙伴可以自行去对应开源项目官网查看，我们这里来看Dubbo对配置中心的支持&lt;/p>
&lt;p>&lt;em>&lt;strong>多配置中心：&lt;/strong> Dubbo支持多配置中心，来 &lt;strong>保证其中一个配置中心集群出现不可用时能够切换到另一个配置中心集群&lt;/strong> ，保证能够正常从配置中心获取全局的配置、路由规则等信息。这也能够满足配置中心在部署上适应各类高可用的部署架构模式。-来自官网&lt;/em>&lt;/p>
&lt;p>做中间件可能考虑更多的的不仅仅是性能，还要过多的考虑高可用，高可用怎么做呢，其实就是失效转移，主备切换，降级，降级再降级这些理论的运用，多多考虑某一个服务挂了怎么办，Dubbo的多配置中心支持增加了复杂性，不过降低了服务不可用的风险，有一定的人手的公司还是值得做的。&lt;/p>
&lt;p>关于Dubbo的配置中心这里我来贴个官网的图:
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/v3/concepts/centers-config.png">
关于官网的介绍可以自行去官网看详细内容: &lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docs/concepts/registry-configcenter-metadata/">部署架构(注册中心、配置中心、元数据中心&lt;/a>&lt;/p>
&lt;h2 id="132-启动配置中心">13.2 启动配置中心&lt;/h2>
&lt;p>在上一个博客中说到了&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/">《12-全局视野来看Dubbo3.0.8的服务启动生命周期》&lt;/a>Dubbo应用的启动过程DefaultApplicationDeployer的initialize()方法的全生命周期，在初始化方法中通过调用startConfigCenter();方法来启动配置中心的加载。后面就来详细看下：&lt;/p>
&lt;p>DefaultApplicationDeployer类型的startConfigCenter()代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startConfigCenter&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load application config&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载应用程序配置 （配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置，也可能是多应用，多注册中心这样的配置）&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configManager.loadConfigsOfTypeFromProps(ApplicationConfig.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// try set model name&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (StringUtils.isBlank(applicationModel.getModelName())) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//设置一下模块名字和模块描述（我们再Debug里面经常会看到这个描述信息 toString直接返回了Dubbo为我们改造的对象信息）&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationModel.setModelName(applicationModel.tryGetApplicationName());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// load config centers&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//加载配置中心配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置，也可能是多应用，多注册中心这样的配置）&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configManager.loadConfigsOfTypeFromProps(ConfigCenterConfig.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#586e75">//出于兼容性目的，如果没有明确指定配置中心，并且registryConfig的UseAConfigCenter为null或true，请使用registry作为默认配置中心&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> useRegistryAsConfigCenterIfNecessary();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// check Config Center&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置管理器中获取配置中心&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Collection&lt;span style="color:#719e07">&amp;lt;&lt;/span>ConfigCenterConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> configCenters &lt;span style="color:#719e07">=&lt;/span> configManager.getConfigCenters();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置中心配置不为空则刷新配置中心配置将其放入配置管理器中&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//下面开始刷新配置中心配置,如果配置中心配置为空则执行空刷新&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isEmpty(configCenters)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置中心不存在的配置刷新&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConfigCenterConfig configCenterConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConfigCenterConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configCenterConfig.setScopeModel(applicationModel);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configCenterConfig.refresh();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//验证配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (configCenterConfig.isValid()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置合法则将配置放入配置管理器中&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configManager.addConfigCenter(configCenterConfig);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configCenters &lt;span style="color:#719e07">=&lt;/span> configManager.getConfigCenters();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//一个或者多个配置中心配置存在的情况下的配置刷新&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (ConfigCenterConfig configCenterConfig : configCenters) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configCenterConfig.refresh();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//验证配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#586e75">//配置中心配置不为空则将配置中心配置添加到environment中&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isNotEmpty(configCenters)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//多配置中心本地动态配置对象创建CompositeDynamicConfiguration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CompositeDynamicConfiguration compositeDynamicConfiguration &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CompositeDynamicConfiguration();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取配置中心的相关配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (ConfigCenterConfig configCenter : configCenters) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Pass config from ConfigCenterBean to environment&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将配置中心的外部化配置,更新到环境里面&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> environment.updateExternalConfigMap(configCenter.getExternalConfiguration());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将配置中心的应用配置,添加到环境里面&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> environment.updateAppExternalConfigMap(configCenter.getAppExternalConfiguration());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Fetch config from remote config center&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从配置中心拉取配置添加到组合配置中&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将配置中心中的动态配置信息 设置到environment的动态配置属性中&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> environment.setDynamicConfiguration(compositeDynamicConfiguration);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1321-配置管理器加载配置">13.2.1 配置管理器加载配置&lt;/h3>
&lt;p>前面我们看到了配置管理器会从系统属性中加载配置这里我们来详细看下，配置往往是我们使用者比较关注的内容，&lt;/p></description></item><item><title>12 全局视野来看Dubbo3的服务启动生命周期</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="12-全局视野来看dubbo3的服务启动生命周期">12 全局视野来看Dubbo3的服务启动生命周期&lt;/h1>
&lt;h2 id="121-启动方法简介">12.1 启动方法简介&lt;/h2>
&lt;p>在说启动方法之前先把视野拉回第一章&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">《1-从一个服务提供者的Demo说起》&lt;/a>我们的Demo代码,下面只贴一下核心代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">Application&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>(String&lt;span style="color:#719e07">[]&lt;/span> args) &lt;span style="color:#268bd2">throws&lt;/span> Exception {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startWithBootstrap();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startWithBootstrap&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service.setInterface(DemoService.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service.setRef(&lt;span style="color:#719e07">new&lt;/span> DemoServiceImpl());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这一个篇章主要说这里:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap.getInstance();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化应用配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap.application(&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig(&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化注册中心配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .registry(&lt;span style="color:#719e07">new&lt;/span> RegistryConfig(&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化协议配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .protocol(&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig(CommonConstants.DUBBO, &lt;span style="color:#719e07">-&lt;/span>1))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化服务配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .service(service)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//启动&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .await();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面我们介绍了Dubbo启动器DubboBootstrap类型对象的创建,又介绍了为DubboBootstrap启动器初始化各种配置信息,这一个博客就开始到了分析启动方法的位置了,Dubbo启动器借助Deployer发布器来启动和发布服务,发布器的启动过程包含了启动配置中心,加载配置,启动元数据中心,启动服务等操作都是比较重要又比较复杂的过程,这里我们先来看下启动过程的生命周期来为后面的内容做好铺垫。&lt;/p>
&lt;h2 id="122-启动器启动方法的调用逻辑start">12.2 启动器启动方法的调用逻辑start()&lt;/h2>
&lt;p>这里我们就直接来看DubboBootstrap的start()方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">start&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 		&lt;span style="color:#586e75">//调用重载的方法进行启动参数代表是否等待启动结束&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.start(&lt;span style="color:#cb4b16">true&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们再来看重载的start方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">start&lt;/span>(&lt;span style="color:#dc322f">boolean&lt;/span> wait) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#586e75">//这个发布器是在ApplicationModel对象创建之后初始化的时候进行初始化的具体类型为DefaultApplicationDeployer&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Future future &lt;span style="color:#719e07">=&lt;/span> applicationDeployer.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (wait) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//等待异步启动的结果&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> future.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (Exception e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//启动失败则抛出一个异常&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException(&lt;span style="color:#2aa198">&amp;#34;await dubbo application start finish failure&amp;#34;&lt;/span>, e);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="123-应用程序发布器defaultapplicationdeployer的启动方法">12.3 应用程序发布器DefaultApplicationDeployer的启动方法&lt;/h2>
&lt;p>发布器是帮助我们发布服务和引用服务的,在Dubbo3中不论是服务提供者还是服务消费者如果想要启动服务都需要走这个启动方法的逻辑,所以务必重视&lt;/p></description></item><item><title>11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="11-dubbo启动器dubbobootstrap添加协议配置信息protocolconfig">11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig&lt;/h1>
&lt;h2 id="111-简介">11.1 简介&lt;/h2>
&lt;p>先贴个代码用来参考:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap.getInstance();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap.application(&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig(&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .registry(&lt;span style="color:#719e07">new&lt;/span> RegistryConfig(&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .protocol(&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig(CommonConstants.DUBBO, &lt;span style="color:#719e07">-&lt;/span>1))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .service(service)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .await();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上个博客我们说了 RegistryConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码协议配置信息:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>.protocol(&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig(CommonConstants.DUBBO, &lt;span style="color:#719e07">-&lt;/span>1))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="112--协议的配置相关">11.2 协议的配置相关&lt;/h2>
&lt;p>下面的配置来源于官网&lt;/p>
&lt;p>服务提供者协议配置。对应的配置类： org.apache.dubbo.config.ProtocolConfig。同时，如果需要支持多协议，可以声明多个 &lt;code>&amp;lt;dubbo:protocol&amp;gt;&lt;/code> 标签，并在 &lt;code>&amp;lt;dubbo:service&amp;gt;&lt;/code> 中通过 protocol 属性指定使用的协议。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">属性&lt;/th>
 &lt;th style="text-align: left">对应URL参数&lt;/th>
 &lt;th style="text-align: left">类型&lt;/th>
 &lt;th style="text-align: left">是否必填&lt;/th>
 &lt;th style="text-align: left">缺省值&lt;/th>
 &lt;th style="text-align: left">作用&lt;/th>
 &lt;th style="text-align: left">描述&lt;/th>
 &lt;th style="text-align: left">兼容性&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">id&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo&lt;/td>
 &lt;td style="text-align: left">配置关联&lt;/td>
 &lt;td style="text-align: left">协议BeanId，可以在&amp;lt;dubbo:service protocol=&amp;quot;&amp;quot;&amp;gt;中引用此ID，如果ID不填，缺省和name属性值一样，重复则在name后加序号。&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">name&lt;/td>
 &lt;td style="text-align: left">&lt;protocol>&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">&lt;strong>必填&lt;/strong>&lt;/td>
 &lt;td style="text-align: left">dubbo&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">协议名称&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">port&lt;/td>
 &lt;td style="text-align: left">&lt;port>&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo协议缺省端口为20880，rmi协议缺省端口为1099，http和hessian协议缺省端口为80；如果&lt;strong>没有&lt;/strong>配置port，则自动采用默认端口，如果配置为**-1**，则会分配一个没有被占用的端口。Dubbo 2.4.0+，分配的端口在协议缺省端口的基础上增长，确保端口段可控。&lt;/td>
 &lt;td style="text-align: left">服务发现&lt;/td>
 &lt;td style="text-align: left">服务端口&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">host&lt;/td>
 &lt;td style="text-align: left">&lt;host>&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">自动查找本机IP&lt;/td>
 &lt;td style="text-align: left">服务发现&lt;/td>
 &lt;td style="text-align: left">-服务主机名，多网卡选择或指定VIP及域名时使用，为空则自动查找本机IP，-建议不要配置，让Dubbo自动获取本机IP&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">threadpool&lt;/td>
 &lt;td style="text-align: left">threadpool&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">fixed&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">线程池类型，可选：fixed/cached&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">threads&lt;/td>
 &lt;td style="text-align: left">threads&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">200&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">服务线程池大小(固定大小)&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">iothreads&lt;/td>
 &lt;td style="text-align: left">threads&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">cpu个数+1&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">io线程池大小(固定大小)&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">accepts&lt;/td>
 &lt;td style="text-align: left">accepts&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">0&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">服务提供方最大可接受连接数&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">payload&lt;/td>
 &lt;td style="text-align: left">payload&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">8388608(=8M)&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">请求及响应数据包大小限制，单位：字节&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">codec&lt;/td>
 &lt;td style="text-align: left">codec&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">协议编码方式&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">serialization&lt;/td>
 &lt;td style="text-align: left">serialization&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo协议缺省为hessian2，rmi协议缺省为java，http协议缺省为json&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">协议序列化方式，当协议支持多种序列化方式时使用，比如：dubbo协议的dubbo,hessian2,java,compactedjava，以及http协议的json等&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">accesslog&lt;/td>
 &lt;td style="text-align: left">accesslog&lt;/td>
 &lt;td style="text-align: left">string/boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">设为true，将向logger中输出访问日志，也可填写访问日志文件路径，直接把访问日志输出到指定文件&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">path&lt;/td>
 &lt;td style="text-align: left">&lt;path>&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务发现&lt;/td>
 &lt;td style="text-align: left">提供者上下文路径，为服务path的前缀&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">transporter&lt;/td>
 &lt;td style="text-align: left">transporter&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo协议缺省为netty&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">协议的服务端和客户端实现类型，比如：dubbo协议的mina,netty等，可以分拆为server和client配置&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">server&lt;/td>
 &lt;td style="text-align: left">server&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo协议缺省为netty，http协议缺省为servlet&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">协议的服务器端实现类型，比如：dubbo协议的mina,netty等，http协议的jetty,servlet等&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">client&lt;/td>
 &lt;td style="text-align: left">client&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo协议缺省为netty&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">协议的客户端实现类型，比如：dubbo协议的mina,netty等&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">dispatcher&lt;/td>
 &lt;td style="text-align: left">dispatcher&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo协议缺省为all&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">协议的消息派发方式，用于指定线程模型，比如：dubbo协议的all, direct, message, execution, connection等&lt;/td>
 &lt;td style="text-align: left">2.1.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">queues&lt;/td>
 &lt;td style="text-align: left">queues&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">0&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">线程池队列大小，当线程池满时，排队等待执行的队列大小，建议不要设置，当线程池满时应立即失败，重试其它服务提供机器，而不是排队，除非有特殊需求。&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">charset&lt;/td>
 &lt;td style="text-align: left">charset&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">UTF-8&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">序列化编码&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">buffer&lt;/td>
 &lt;td style="text-align: left">buffer&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">8192&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">网络读写缓冲区大小&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">heartbeat&lt;/td>
 &lt;td style="text-align: left">heartbeat&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">0&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">心跳间隔，对于长连接，当物理层断开时，比如拔网线，TCP的FIN消息来不及发送，对方收不到断开事件，此时需要心跳来帮助检查连接是否已断开&lt;/td>
 &lt;td style="text-align: left">2.0.10以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">telnet&lt;/td>
 &lt;td style="text-align: left">telnet&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">所支持的telnet命令，多个命令用逗号分隔&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">register&lt;/td>
 &lt;td style="text-align: left">register&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">true&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">该协议的服务是否注册到注册中心&lt;/td>
 &lt;td style="text-align: left">2.0.8以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">contextpath&lt;/td>
 &lt;td style="text-align: left">contextpath&lt;/td>
 &lt;td style="text-align: left">String&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">缺省为空串&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">2.0.6以上版本&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:&lt;/p></description></item><item><title>10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="10-dubbo启动器dubbobootstrap添加注册中心配置信息registryconfig">10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig&lt;/h1>
&lt;h2 id="101-简介">10.1 简介&lt;/h2>
&lt;p>先贴个代码用来参考:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap.getInstance();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap.application(&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig(&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .registry(&lt;span style="color:#719e07">new&lt;/span> RegistryConfig(&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .protocol(&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig(CommonConstants.DUBBO, &lt;span style="color:#719e07">-&lt;/span>1))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .service(service)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .await();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上个博客我们说了启动器ApplicationConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码注册中心配置信息:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>registry(&lt;span style="color:#719e07">new&lt;/span> RegistryConfig(&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="102--注册中心的配置相关">10.2 注册中心的配置相关&lt;/h2>
&lt;p>下面的配置来源于官网&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">属性&lt;/th>
 &lt;th style="text-align: left">对应URL参数&lt;/th>
 &lt;th style="text-align: left">类型&lt;/th>
 &lt;th style="text-align: left">是否必填&lt;/th>
 &lt;th style="text-align: left">缺省值&lt;/th>
 &lt;th style="text-align: left">作用&lt;/th>
 &lt;th style="text-align: left">描述&lt;/th>
 &lt;th style="text-align: left">兼容性&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">id&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">配置关联&lt;/td>
 &lt;td style="text-align: left">注册中心引用BeanId，可以在&amp;lt;dubbo:service registry=&amp;quot;&amp;quot;&amp;gt;或&amp;lt;dubbo:reference registry=&amp;quot;&amp;quot;&amp;gt;中引用此ID&lt;/td>
 &lt;td style="text-align: left">1.0.16以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">address&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;host:port&amp;gt; &lt;/code>&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">&lt;strong>必填&lt;/strong>&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务发现&lt;/td>
 &lt;td style="text-align: left">注册中心服务器地址，如果地址没有端口缺省为9090，同一集群内的多个地址用逗号分隔，如：ip:port,ip:port，不同集群的注册中心，请配置多个&lt;code>&amp;lt;dubbo:registry&amp;gt;&lt;/code>标签&lt;/td>
 &lt;td style="text-align: left">1.0.16以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">protocol&lt;/td>
 &lt;td style="text-align: left">&lt;protocol>&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo&lt;/td>
 &lt;td style="text-align: left">服务发现&lt;/td>
 &lt;td style="text-align: left">注册中心地址协议，支持&lt;code>dubbo&lt;/code>, &lt;code>multicast&lt;/code>, &lt;code>zookeeper&lt;/code>, &lt;code>redis&lt;/code>, &lt;code>consul(2.7.1)&lt;/code>, &lt;code>sofa(2.7.2)&lt;/code>, &lt;code>etcd(2.7.2)&lt;/code>, &lt;code>nacos(2.7.2)&lt;/code>等协议&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">port&lt;/td>
 &lt;td style="text-align: left">&lt;port>&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">9090&lt;/td>
 &lt;td style="text-align: left">服务发现&lt;/td>
 &lt;td style="text-align: left">注册中心缺省端口，当address没有带端口时使用此端口做为缺省值&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">username&lt;/td>
 &lt;td style="text-align: left">&lt;username>&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">登录注册中心用户名，如果注册中心不需要验证可不填&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">password&lt;/td>
 &lt;td style="text-align: left">&lt;password>&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">登录注册中心密码，如果注册中心不需要验证可不填&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">transport&lt;/td>
 &lt;td style="text-align: left">registry.transporter&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">netty&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">网络传输方式，可选mina,netty&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">timeout&lt;/td>
 &lt;td style="text-align: left">registry.timeout&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">5000&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">注册中心请求超时时间(毫秒)&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">session&lt;/td>
 &lt;td style="text-align: left">registry.session&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">60000&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">注册中心会话超时时间(毫秒)，用于检测提供者非正常断线后的脏数据，比如用心跳检测的实现，此时间就是心跳间隔，不同注册中心实现不一样。&lt;/td>
 &lt;td style="text-align: left">2.1.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">file&lt;/td>
 &lt;td style="text-align: left">registry.file&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">使用文件缓存注册中心地址列表及服务提供者列表，应用重启时将基于此文件恢复，注意：两个注册中心不能使用同一文件存储&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">wait&lt;/td>
 &lt;td style="text-align: left">registry.wait&lt;/td>
 &lt;td style="text-align: left">int&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">0&lt;/td>
 &lt;td style="text-align: left">性能调优&lt;/td>
 &lt;td style="text-align: left">停止时等待通知完成时间(毫秒)&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">check&lt;/td>
 &lt;td style="text-align: left">check&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">true&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">注册中心不存在时，是否报错&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">register&lt;/td>
 &lt;td style="text-align: left">register&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">true&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">是否向此注册中心注册服务，如果设为false，将只订阅，不注册&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">subscribe&lt;/td>
 &lt;td style="text-align: left">subscribe&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">true&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">是否向此注册中心订阅服务，如果设为false，将只注册，不订阅&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">dynamic&lt;/td>
 &lt;td style="text-align: left">dynamic&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">true&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">服务是否动态注册，如果设为false，注册后将显示为disable状态，需人工启用，并且服务提供者停止时，也不会自动取消注册，需人工禁用。&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">group&lt;/td>
 &lt;td style="text-align: left">group&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">dubbo&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">服务注册分组，跨组的服务不会相互影响，也无法相互调用，适用于环境隔离。&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">simplified&lt;/td>
 &lt;td style="text-align: left">simplified&lt;/td>
 &lt;td style="text-align: left">boolean&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">false&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">注册到注册中心的URL是否采用精简模式的（与低版本兼容）&lt;/td>
 &lt;td style="text-align: left">2.7.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">extra-keys&lt;/td>
 &lt;td style="text-align: left">extraKeys&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">在simplified=true时，extraKeys允许你在默认参数外将额外的key放到URL中，格式：“interface,key1,key2”。&lt;/td>
 &lt;td style="text-align: left">2.7.0以上版本&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:&lt;/p></description></item><item><title>9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="9-dubbo启动器dubbobootstrap添加应用程序的配置信息applicationconfig">9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig&lt;/h1>
&lt;h2 id="91-简介">9.1 简介&lt;/h2>
&lt;p>先贴个代码用来参考:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap.getInstance();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap.application(&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig(&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .registry(&lt;span style="color:#719e07">new&lt;/span> RegistryConfig(&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .protocol(&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig(CommonConstants.DUBBO, &lt;span style="color:#719e07">-&lt;/span>1))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .service(service)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .await();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上个博客我们说了启动器对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>bootstrap.application(&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig(&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="92-应用程序applicationconfig的配置信息">9.2 应用程序ApplicationConfig的配置信息&lt;/h2>
&lt;p>ApplicationConfig的构造器比较简单就是为他的成员变量name赋值来标识这个应用程序的名字
下面我们直接参考下官网的配置表格:&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">属性&lt;/th>
 &lt;th style="text-align: left">对应URL参数&lt;/th>
 &lt;th style="text-align: left">类型&lt;/th>
 &lt;th style="text-align: left">是否必填&lt;/th>
 &lt;th style="text-align: left">缺省值&lt;/th>
 &lt;th style="text-align: left">作用&lt;/th>
 &lt;th style="text-align: left">描述&lt;/th>
 &lt;th style="text-align: left">兼容性&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">name&lt;/td>
 &lt;td style="text-align: left">application&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">&lt;strong>必填&lt;/strong>&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">当前应用名称，用于注册中心计算应用间依赖关系，注意：消费者和提供者应用名不要一样，此参数不是匹配条件，你当前项目叫什么名字就填什么，和提供者消费者角色无关，比如：kylin应用调用了morgan应用的服务，则kylin项目配成kylin，morgan项目配成morgan，可能kylin也提供其它服务给别人使用，但kylin项目永远配成kylin，这样注册中心将显示kylin依赖于morgan&lt;/td>
 &lt;td style="text-align: left">1.0.16以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">version&lt;/td>
 &lt;td style="text-align: left">application.version&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">当前应用的版本&lt;/td>
 &lt;td style="text-align: left">2.2.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">owner&lt;/td>
 &lt;td style="text-align: left">owner&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">应用负责人，用于服务治理，请填写负责人公司邮箱前缀&lt;/td>
 &lt;td style="text-align: left">2.0.5以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">organization&lt;/td>
 &lt;td style="text-align: left">organization&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">组织名称(BU或部门)，用于注册中心区分服务来源，此配置项建议不要使用autoconfig，直接写死在配置中，比如china,intl,itu,crm,asc,dw,aliexpress等&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">architecture&lt;/td>
 &lt;td style="text-align: left">architecture&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">用于服务分层对应的架构。如，intl、china。不同的架构使用不同的分层。&lt;/td>
 &lt;td style="text-align: left">2.0.7以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">environment&lt;/td>
 &lt;td style="text-align: left">environment&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">应用环境，如：develop/test/product，不同环境使用不同的缺省值，以及作为只用于开发测试功能的限制条件&lt;/td>
 &lt;td style="text-align: left">2.0.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">compiler&lt;/td>
 &lt;td style="text-align: left">compiler&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">javassist&lt;/td>
 &lt;td style="text-align: left">性能优化&lt;/td>
 &lt;td style="text-align: left">Java字节码编译器，用于动态类的生成，可选：jdk或javassist&lt;/td>
 &lt;td style="text-align: left">2.1.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">logger&lt;/td>
 &lt;td style="text-align: left">logger&lt;/td>
 &lt;td style="text-align: left">string&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">slf4j&lt;/td>
 &lt;td style="text-align: left">性能优化&lt;/td>
 &lt;td style="text-align: left">日志输出方式，可选：slf4j,jcl,log4j,log4j2,jdk&lt;/td>
 &lt;td style="text-align: left">2.2.0以上版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">metadata-type&lt;/td>
 &lt;td style="text-align: left">metadata-type&lt;/td>
 &lt;td style="text-align: left">String&lt;/td>
 &lt;td style="text-align: left">可选&lt;/td>
 &lt;td style="text-align: left">local&lt;/td>
 &lt;td style="text-align: left">服务治理&lt;/td>
 &lt;td style="text-align: left">metadata 传递方式，是以 Provider 视角而言的，Consumer 侧配置无效，可选值有： remote - Provider 把 metadata 放到远端注册中心，Consumer 从注册中心获取 local - Provider 把 metadata 放在本地，Consumer 从 Provider 处直接获取&lt;/td>
 &lt;td style="text-align: left">2.7.6以上版本&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>官网的配置很详细了上面有一些属性是值得注意的比如这个name,compiler,logger,metadata-type 我们可能要多看下默认值是什么,方便我们在使用过程中遇到问题的排查&lt;/p></description></item><item><title>8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="8-dubbo启动器dubbobootstrap借助双重校验锁的单例模式进行对象的初始化">8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化&lt;/h1>
&lt;h2 id="81-启动器简介">8.1 启动器简介&lt;/h2>
&lt;p>在说启动器之前先把视野拉回第一章&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/">《1-从一个服务提供者的Demo说起》&lt;/a>我们的Demo代码,下面只贴一下核心代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">Application&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>(String&lt;span style="color:#719e07">[]&lt;/span> args) &lt;span style="color:#268bd2">throws&lt;/span> Exception {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startWithBootstrap();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startWithBootstrap&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service.setInterface(DemoService.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service.setRef(&lt;span style="color:#719e07">new&lt;/span> DemoServiceImpl());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这一个篇章主要说这里:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap.getInstance();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap.application(&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig(&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .registry(&lt;span style="color:#719e07">new&lt;/span> RegistryConfig(&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .protocol(&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig(CommonConstants.DUBBO, &lt;span style="color:#719e07">-&lt;/span>1))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .service(service)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .await();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo3 往云原生的方向走自然要针对云原生应用的应用启动,应用运行,应用发布等信息做一些建模,这个DubboBootstrap就是用来启动Dubbo服务的.类似于Netty的Bootstrap类型和ServerBootstrap启动器&lt;/p>
&lt;h2 id="82-双重校验锁的单例模式创建启动器对象的">8.2 双重校验锁的单例模式创建启动器对象的&lt;/h2>
&lt;p>Dubbo的bootstrap类为啥要用单例模式:&lt;/p>
&lt;p>通过调用静态方法getInstance()获取单例实例。之所以设计为单例，是因为Dubbo中的一些类（如ExtensionLoader）只为每个进程设计一个实例。&lt;/p>
&lt;p>下面就来直接看代码吧,代码胜千言:
对象的调用代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap.getInstance(); 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DubboBootstrap获取对象的getInstance()方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> DubboBootstrap &lt;span style="color:#268bd2">getInstance&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 		&lt;span style="color:#586e75">//双重校验锁第一次判断空&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//为空都进行排队&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> (DubboBootstrap.class) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//双重校验锁第二次判断空 上面为空的都排队了这里得判断下&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//调用重载方法获取对象&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap.getInstance(ApplicationModel.defaultModel());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DubboBootstrap获取对象重载的getInstance(ApplicationModel applicationModel)方法:&lt;/p></description></item><item><title>7-Dubbo的SPI扩展机制之自动激活扩展Activate源码解析</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="7-dubbo的spi扩展机制之自动激活扩展activate源码解析">7-Dubbo的SPI扩展机制之自动激活扩展Activate源码解析&lt;/h1>
&lt;h2 id="71-activate扩展的说明">7.1 Activate扩展的说明&lt;/h2>
&lt;p>此注解对于使用给定条件自动激活某些扩展非常有用，例如：@Activate可用于在有多个实现时加载某些筛选器扩展。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>group()&lt;/strong> 指定组条件。框架SPI定义了有效的组值。&lt;/li>
&lt;li>&lt;strong>value()&lt;/strong> 指定URL条件中的参数键。&lt;/li>
&lt;/ul>
&lt;p>SPI提供程序可以调用ExtensionLoader。getActivateExtension(URL、String、String)方法以查找具有给定条件的所有已激活扩展。&lt;/p>
&lt;p>比如后面我们会说到的&lt;strong>过滤器扩展对象&lt;/strong>的获取,如下通过调用&lt;strong>getActivateExtension方法的&lt;/strong>代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Filter&lt;span style="color:#719e07">&amp;gt;&lt;/span> filters;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filters &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil.getExtensionLoader(Filter.class, moduleModels.get(0)).getActivateExtension(url, key, group);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="72-获取自动激活扩展的源码">7.2 获取自动激活扩展的源码&lt;/h2>
&lt;p>前面我们看了激活扩展是通过调用getActivateExtension方法来获取对象的,那接下来就来看下这个方法做了什么操作:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">* @param url 服务的url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">* @param key 用于获取扩展点名称的url参数键 比如监听器:exporter.listener,过滤器:params-filter,telnet处理器:telnet
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">*/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getActivateExtension&lt;/span>(URL url, String key) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getActivateExtension(url, key, &lt;span style="color:#cb4b16">null&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续调用重载的方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param url 服务的url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param key 用于获取扩展点名称的url参数键 比如监听器:exporter.listener,过滤器:params-filter,telnet处理器:telnet
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param group group 用于筛选的分组,比如过滤器中使用此参数来区分消费者使用这个过滤器还是提供者使用这个过滤器他们的group参数分表为consumer,provider
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return 已激活的扩展列表。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getActivateExtension&lt;/span>(URL url, String key, String group) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 		&lt;span style="color:#586e75">//从参数中获取url指定的值&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String value &lt;span style="color:#719e07">=&lt;/span> url.getParameter(key);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用下个重载的方法&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getActivateExtension(url, StringUtils.isEmpty(value) &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> : COMMA_SPLIT_PATTERN.split(value), group);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面的重载方法都是用来转换参数的,下面这个方法才是真正的逻辑&lt;/p></description></item><item><title>06-Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="6-dubbo的spi扩展机制之普通扩展对象的创建与wrapper机制的源码解析">6 Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析&lt;/h1>
&lt;h2 id="61-普通扩展对象的加载与创建">6.1 普通扩展对象的加载与创建&lt;/h2>
&lt;p>这里我们要分析的是ExtensionLoader类型的getExtension(String name)方法, 有了前面自适应扩展的铺垫,这里就更容易来看了getExtension是根据扩展名字获取具体扩展的通用方法,我们来根据某个类型来获取扩展的时候就是走的这里,比如在这个博客开头的介绍:&lt;/p>
&lt;ul>
&lt;li>ApplicationModel中获取配置管理器对象&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> configManager &lt;span style="color:#719e07">=&lt;/span> (ConfigManager) &lt;span style="color:#719e07">this&lt;/span>.getExtensionLoader(ApplicationExt.class)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .getExtension(ConfigManager.NAME);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="611-getextension方法源码">6.1.1 getExtension方法源码&lt;/h3>
&lt;p>先来看下getExtension方法的源码,根据扩展名字查询扩展对象&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">public&lt;/span> T &lt;span style="color:#268bd2">getExtension&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#586e75">//这里并不能看到什么,只多传了个参数wrap为true调用另外一个重载的方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> T extension &lt;span style="color:#719e07">=&lt;/span> getExtension(name, &lt;span style="color:#b58900">true&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (extension &lt;span style="color:#719e07">==&lt;/span> null) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException(&lt;span style="color:#2aa198">&amp;#34;Not find extension: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extension;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">getExtension&lt;/span>(String name, &lt;span style="color:#dc322f">boolean&lt;/span> wrap) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#586e75">//检查扩展加载器是否已被销毁&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> checkDestroyed();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (StringUtils.isEmpty(name)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException(&lt;span style="color:#2aa198">&amp;#34;Extension name == null&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//扩展名字为true则加载默认扩展&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>.equals(name)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getDefaultExtension();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//非wrap类型则将缓存的扩展名字key加上_origin后缀&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//wrap是aop机制 俗称切面,这个origin在aop里面可以称为切点,下面的wrap扩展可以称为增强通知的类型,普通扩展和wrap扩展的扩展名字是一样的&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String cacheKey &lt;span style="color:#719e07">=&lt;/span> name;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>wrap) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cacheKey &lt;span style="color:#719e07">+=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;_origin&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从cachedInstances缓存中查询&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> Holder&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> holder &lt;span style="color:#719e07">=&lt;/span> getOrCreateHolder(cacheKey);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object instance &lt;span style="color:#719e07">=&lt;/span> holder.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//缓存中不存在则创建扩展对象 双重校验锁&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> (holder) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//双重校验锁的方式&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> holder.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//创建扩展对象&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> createExtension(name, wrap);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> holder.set(instance);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> (T) instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们先来看一下默认扩展的加载代码:&lt;/p></description></item><item><title>05-自适应扩展对象的创建getAdaptiveExtension方法</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h2 id="5-自适应扩展对象的创建getadaptiveextension方法">5 自适应扩展对象的创建getAdaptiveExtension方法&lt;/h2>
&lt;p>自适应扩展又称为动态扩展,可以在运行时生成扩展对象&lt;/p>
&lt;p>ExtensionLoader中的getAdaptiveExtension()方法,这个方法也是我们看到的第一个获取扩展对象的方法. ,这个方法可以帮助我们通过SPI机制从扩展文件中找到需要的扩展类型并创建它的对象,
&lt;strong>自适应扩展:&lt;strong>如果对设计模式比较了解的可能会联想到&lt;/strong>适配器模式&lt;/strong>,自适应扩展其实就是适配器模式的思路,自适应扩展有两种策略:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>一种是我们自己实现自适应扩展:然后使用@Adaptive修饰这个时候适配器的逻辑由我们自己实现,当扩展加载器去查找具体的扩展的时候可以通过找到我们这个对应的适配器扩展,然后适配器扩展帮忙去查询真正的扩展,这个比如我们下面要举的扩展注入器的例子,具体扩展通过扩展注入器适配器,注入器适配器来查询具体的注入器扩展实现来帮忙查找扩展。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>还有一种方式是我们未实现这个自适应扩展,Dubbo在运行时通过字节码动态代理的方式在运行时生成一个适配器,使用这个适配器映射到具体的扩展. 第二种情况往往用在比如 Protocol、Cluster、LoadBalance 等。有时，有些拓展并不想在框架启动阶段被加载，而是希望在拓展方法被调用时，根据运行时参数进行加载。(如果还不了解可以考虑看下@Adaptive注解加载方法上面的时候扩展是如何加载的)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">getAdaptiveExtension&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 		&lt;span style="color:#586e75">//检查当前扩展加载器是否已经被销毁&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> checkDestroyed();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从自适应扩展缓存中查询扩展对象如果存在就直接返回,这个自适应扩展类型只会有一个扩展实现类型如果是多个的话根据是否可以覆盖参数决定扩展实现类是否可以相互覆盖&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object instance &lt;span style="color:#719e07">=&lt;/span> cachedAdaptiveInstance.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个if判断不太优雅 容易多层嵌套,上面instance不为空就可以直接返回了&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//创建异常则抛出异常直接返回(多线程场景下可能第一个线程异常了第二个线程进来之后走到这里)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (createAdaptiveInstanceError &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException(&lt;span style="color:#2aa198">&amp;#34;Failed to create adaptive instance: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createAdaptiveInstanceError.toString(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createAdaptiveInstanceError);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#586e75">//加锁排队 (单例模式创建对象的思想 双重校验锁)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> (cachedAdaptiveInstance) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//加锁的时候对象都是空的,进来之后先判断下防止重复创建&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> cachedAdaptiveInstance.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//只有第一个进来锁的对象为空开始创建扩展对象&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 	&lt;span style="color:#586e75">//根据SPI机制获取类型,创建对象&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> createAdaptiveExtension();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//存入缓存&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cachedAdaptiveInstance.set(instance);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (Throwable t) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createAdaptiveInstanceError &lt;span style="color:#719e07">=&lt;/span> t;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException(&lt;span style="color:#2aa198">&amp;#34;Failed to create adaptive instance: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t.toString(), t);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> (T) instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面使用单例思想来调用创建自适应扩展对象的方法,下面就让我们深入探究下创建自适应扩展对象的整个过程createAdaptiveExtension();方法:&lt;/p></description></item><item><title>04-Dubbo的扩展机制</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="4-dubbo的扩展机制">4-Dubbo的扩展机制&lt;/h1>
&lt;h2 id="41-回顾我们前面使用到扩展场景">4.1 回顾我们前面使用到扩展场景&lt;/h2>
&lt;p>在上一章中我们初始化应用模型对象的时候,了解到有几个地方用到了扩展机制来创建对象,这一章我们会详细来讲一下这个扩展对象的加载过程,这里我们先来回顾下哪些地方用到了扩展机制:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 使用扩展机制获取TypeBuilder&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>TypeBuilder&lt;span style="color:#719e07">&amp;gt;&lt;/span> tbs &lt;span style="color:#719e07">=&lt;/span> model.getExtensionLoader(TypeBuilder.class).getSupportedExtensionInstances();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//获取域模型初始化器ScopeModelInitializer扩展对象,执行初始化方法&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializerExtensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>.getExtensionLoader(ScopeModelInitializer.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ScopeModelInitializer&lt;span style="color:#719e07">&amp;gt;&lt;/span> initializers &lt;span style="color:#719e07">=&lt;/span> initializerExtensionLoader.getSupportedExtensionInstances();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// OrderedPropertiesConfiguration 中获取有序配置提供器对象&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>OrderedPropertiesProvider&lt;span style="color:#719e07">&amp;gt;&lt;/span> propertiesProviderExtensionLoader &lt;span style="color:#719e07">=&lt;/span> moduleModel.getExtensionLoader(OrderedPropertiesProvider.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ApplicationModel中获取配置管理器对象&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> configManager &lt;span style="color:#719e07">=&lt;/span> (ConfigManager) &lt;span style="color:#719e07">this&lt;/span>.getExtensionLoader(ApplicationExt.class)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .getExtension(ConfigManager.NAME);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ModuleModel中获取模块扩展对象&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>ModuleExt&lt;span style="color:#719e07">&amp;gt;&lt;/span> exts &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>.getExtensionLoader(ModuleExt.class).getSupportedExtensionInstances();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ApplicationModel中获Environment对象&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>environment &lt;span style="color:#719e07">=&lt;/span> (Environment) &lt;span style="color:#719e07">this&lt;/span>.getExtensionLoader(ApplicationExt.class)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .getExtension(Environment.NAME);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ApplicationModel中获取应用初始化监听器ApplicationInitListener扩展对象&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ApplicationInitListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> extensionLoader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">this&lt;/span>.getExtensionLoader(ApplicationInitListener.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> listenerNames &lt;span style="color:#719e07">=&lt;/span> extensionLoader.getSupportedExtensions();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ScopeModel中创建扩展访问器:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">this&lt;/span>.extensionDirector &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ExtensionDirector(parent &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> parent.getExtensionDirector() : &lt;span style="color:#cb4b16">null&lt;/span>, scope, &lt;span style="color:#719e07">this&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>有了以上的应用场景我们可以来看下扩展机制了&lt;/p>
&lt;h2 id="42-为什么要用到扩展机制">4.2 为什么要用到扩展机制?&lt;/h2>
&lt;p>为什么要用到扩展这个想必每个编程人员都比较了解,一个好的程序是要遵循一定的设计规范比如设计模式中的&lt;strong>开闭原则&lt;/strong> 英文全称是 Open Closed Principle，简写为 OCP,对扩展开放、对修改关闭:&lt;/p>
&lt;p>&lt;strong>对扩展开放：&lt;/strong> 指的是我们系统中的模块、类、方法对它们的提供者（开发者）应该是开放的，提供者可以对系统进行扩展（新增）新的功能。&lt;/p></description></item><item><title>03-框架,应用程序,模块领域模型Model对象的初始化</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="3-框架应用程序模块领域模型model对象的初始化">3-框架,应用程序,模块领域模型Model对象的初始化&lt;/h1>
&lt;p>在上一章中我们详细看了服务配置ServiceConfig类型的初始化，不过我们跳过了AbstractMethodConfig的构造器中创建模块模型对象的过程，那这一章我们就来看下模块模型对象的初始化过程:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AbstractMethodConfig&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>(ApplicationModel.defaultModel().getDefaultModule());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>那为什么会在Dubbo3的新版本中加入这个域模型呢&lt;/strong>，主要有如下原因
之前dubbo都是只有一个作用域的，通过静态类 属性共享
增加域模型是为了:&lt;/p>
&lt;ol>
&lt;li>让Dubbo支持多应用的部署，这块一些大企业有诉求&lt;/li>
&lt;li>从架构设计上，解决静态属性资源共享、清理的问题&lt;/li>
&lt;li>分层模型将应用的管理和服务的管理分开&lt;/li>
&lt;/ol>
&lt;p>可能比较抽象，可以具体点来看。Dubbo3中在启动时候需要启动配置中心、元数据中心，这个配置中心和元数据中心可以归应用模型来管理。Dubbo作为RPC框架又需要启动服务和引用服务，服务级别的管理就交给了这个模块模型来管理。分层次的管理方便我们理解和处理逻辑，父子级别的模型又方便了数据传递。&lt;/p>
&lt;p>了解过JVM类加载机制的同学应该就比较清楚JVM类加载过程中的数据访问模型。子类加载器先交给父类加载器查找，找不到再从子类加载器中查找。Dubbo的分层模型类似这样一种机制，这一章先来简单了解下，后面用到时候具体细说。&lt;/p>
&lt;h2 id="31-模型对象的关系">3.1 模型对象的关系&lt;/h2>
&lt;p>为了不增加复杂性，我们这里仅仅列出模型对象类型类型之间的继承关系如下所示:
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/source-blog/3-model.png">&lt;/p>
&lt;center>图3.1 模型对象的继承关系&lt;/center>
&lt;p>模型对象一共有4个，公共的属性和操作放在了域模型类型中，下面我们来详细说下这几个模型类型:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>ExtensionAccessor&lt;/strong> 扩展的统一访问器&lt;/p>
&lt;ul>
&lt;li>用于获取扩展加载管理器ExtensionDirector对象&lt;/li>
&lt;li>&lt;strong>获取扩展对象ExtensionLoader&lt;/strong>&lt;/li>
&lt;li>根据扩展名字&lt;strong>获取具体扩展对象&lt;/strong>&lt;/li>
&lt;li>获取自适应扩展对象&lt;/li>
&lt;li>获取默认扩展对象&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ScopeModel&lt;/strong> 模型对象的公共抽象父类型&lt;/p>
&lt;ul>
&lt;li>内部id用于表示模型树的层次结构&lt;/li>
&lt;li>公共模型名称，可以被用户设置&lt;/li>
&lt;li>描述信息&lt;/li>
&lt;li>类加载器管理&lt;/li>
&lt;li>父模型管理parent&lt;/li>
&lt;li>当前模型的所属域ExtensionScope有:&lt;strong>FRAMEWORK(框架)&lt;/strong>，&lt;strong>APPLICATION(应用)&lt;/strong>，&lt;strong>MODULE(模块)&lt;/strong>，&lt;strong>SELF(自给自足&lt;/strong>，为每个作用域创建一个实例，用于特殊的SPI扩展，如ExtensionInjector)&lt;/li>
&lt;li>具体的扩展加载程序管理器对象的管理:&lt;strong>ExtensionDirector&lt;/strong>&lt;/li>
&lt;li>域Bean工厂管理，一个内部共享的Bean工厂&lt;strong>ScopeBeanFactory&lt;/strong>&lt;/li>
&lt;li>等等&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>FrameworkModel&lt;/strong> dubbo框架模型，可与多个应用程序共享&lt;/p>
&lt;ul>
&lt;li>FrameworkModel实例对象集合，allInstances&lt;/li>
&lt;li>所有ApplicationModel实例对象集合，applicationModels&lt;/li>
&lt;li>发布的ApplicationModel实例对象集合pubApplicationModels&lt;/li>
&lt;li>框架的服务存储库&lt;strong>FrameworkServiceRepository&lt;/strong>类型对象(数据存储在内存中)&lt;/li>
&lt;li>内部的应用程序模型对象internalApplicationModel&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ApplicationModel&lt;/strong> 表示正在使用Dubbo的应用程序，并存储基本&lt;strong>元数据信息&lt;/strong>，以便在RPC调用过程中使用。
ApplicationModel包括许多关于&lt;strong>发布服务&lt;/strong>的ProviderModel和许多关于订阅服务的Consumer Model。&lt;/p>
&lt;ul>
&lt;li>ExtensionLoader、DubboBootstrap和这个类目前被设计为单例或静态（本身完全静态或使用一些静态字段）。因此，从它们返回的实例属于流程范围。如果想在一个进程中支持多个dubbo服务器，可能需要重构这三个类。&lt;/li>
&lt;li>&lt;strong>所有ModuleModel实例&lt;/strong>对象集合moduleModels&lt;/li>
&lt;li>&lt;strong>发布的ModuleModel实例&lt;/strong>对象集合pubModuleModels&lt;/li>
&lt;li>&lt;strong>环境信息Environment实例&lt;/strong>对象environment&lt;/li>
&lt;li>&lt;strong>配置管理ConfigManager实例&lt;/strong>对象configManager&lt;/li>
&lt;li>&lt;strong>服务存储库ServiceRepository实例&lt;/strong>对象serviceRepository&lt;/li>
&lt;li>&lt;strong>应用程序部署器ApplicationDeployer实例&lt;/strong>对象deployer&lt;/li>
&lt;li>&lt;strong>所属框架FrameworkModel实例&lt;/strong>对象frameworkModel&lt;/li>
&lt;li>&lt;strong>内部的模块模型ModuleModel实例&lt;/strong>对象internalModule&lt;/li>
&lt;li>&lt;strong>默认的模块模型ModuleModel实例&lt;/strong>对象defaultModule&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ModuleModel&lt;/strong> 服务模块的模型&lt;/p>
&lt;ul>
&lt;li>&lt;strong>所属应用程序模型ApplicationModel实例&lt;/strong>对象applicationModel&lt;/li>
&lt;li>&lt;strong>模块环境信息ModuleEnvironment实例&lt;/strong>对象moduleEnvironment&lt;/li>
&lt;li>&lt;strong>模块服务存储库ModuleServiceRepository实例&lt;/strong>对象serviceRepository&lt;/li>
&lt;li>&lt;strong>模块的服务配置管理ModuleConfigManager实例&lt;/strong>对象moduleConfigManager&lt;/li>
&lt;li>&lt;strong>模块部署器ModuleDeployer实例&lt;/strong>对象deployer用于导出和引用服务&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>了解了这几个模型对象的关系我们可以了解到这几个模型对象的管理层级从框架到应用程序，然后到模块的管理(FrameworkModel-&amp;gt;ApplicationModel-&amp;gt;ModuleModel)，他们主要用来针对框架，应用程序，模块的&lt;strong>存储&lt;/strong>，&lt;strong>发布管理，&lt;/strong>，&lt;strong>配置管理&lt;/strong>&lt;/p>
&lt;p>看来Dubbo3 针对应用服务治理与运维这一块也是在努力尝试.&lt;/p></description></item><item><title>02-启动服务前服务配置ServiceConfig类型是如何初始化的?</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="2-启动服务前服务配置serviceconfig类型是如何初始化的">2-启动服务前服务配置ServiceConfig类型是如何初始化的?&lt;/h1>
&lt;h2 id="21-示例源码回顾">2.1 示例源码回顾:&lt;/h2>
&lt;p>为了方便我们理解记忆,这里先来回顾下上一章我们说的示例代码,如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">Application&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>(String&lt;span style="color:#719e07">[]&lt;/span> args) &lt;span style="color:#268bd2">throws&lt;/span> Exception {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startWithBootstrap();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startWithBootstrap&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service.setInterface(DemoService.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> service.setRef(&lt;span style="color:#719e07">new&lt;/span> DemoServiceImpl());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap.getInstance();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap.application(&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig(&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .registry(&lt;span style="color:#719e07">new&lt;/span> RegistryConfig(&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .protocol(&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig(CommonConstants.DUBBO, &lt;span style="color:#719e07">-&lt;/span>1))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .service(service)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .await();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面这几行代码虽然看似简单,仅仅几行的启动,但是完全掌握也得下一翻大功夫,接下来我们重点看启动代码中的第一行,创建一个服务配置对象:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="22-了解一下服务配置的建模">2.2 了解一下服务配置的建模&lt;/h2>
&lt;p>下面是一个简单的UML继承关系图,当然这个图很是简单的,这里仅仅列出了当前服务提供者的相关服务配置继承关系, 服务提供者独有的配置标注颜色为蓝色,一些可能与服务引用配置所共有的父类型我们用红色背景,当然这里为了简便起见不会提起服务引用相关的配置类型,这里列举了如下服务提供者类型,他们各司其职:
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/source-blog/2-ServiceConfig.png">&lt;/p>
&lt;center>图2.1 服务引用类继承关系UML&lt;/center>
&lt;ul>
&lt;li>AbstractConfig
&lt;ul>
&lt;li>&lt;strong>抽象的配置类型&lt;/strong>,也是最顶层的服务配置类型,封装着解析配置的实用方法和公共方法,比如服务id的设置,服务标签名字的处理,服务参数的添加,属性的提取等等&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>AbstractMethodConfig
&lt;ul>
&lt;li>&lt;strong>抽象的方法配置&lt;/strong>,同样这个类型也是见名知意,服务方法的相关配置处理,这个类型主要用于对服务方法的一些配置信息建模比如服务方法的调用超时时间,重试次数,最大并发调用数,负载均衡策略,是否异步调用,是否确认异步发送等等配置信息.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>AbstractInterfaceConfig
&lt;ul>
&lt;li>&lt;strong>抽象的接口配置&lt;/strong>,与前面介绍的方法配置类似,这个类型是对服务接口的建模,主要的配置信息有暴漏服务的接口名字,服务接口的版本号,客户/提供方将引用的远程服务分组,&lt;strong>服务元数据&lt;/strong>,服务接口的本地impl类名,服务监控配置,对于生成动态代理的策略，可以选择两种策略：jdk和javassist,容错类型等等配置&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>AbstractServiceConfig
&lt;ul>
&lt;li>&lt;strong>抽象的服务配置&lt;/strong>,这个就与我们的服务提供者有了具体的关系了,主要记录了一些服务提供者的公共配置,如服务版本,服务分组,服务延迟注册毫秒数,是否暴漏服务,服务权重,是否为动态服务,服务协议类型,是否注册等等.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>ServiceConfigBase
&lt;ul>
&lt;li>&lt;strong>服务的基础配置类&lt;/strong>,这个类型仍旧是个抽象的类型提取了一些基础的配置:导出服务的接口类,服务名称,接口实现的引用类型,提供者配置,是否是通用服务GenericService&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>ServiceConfig
&lt;ul>
&lt;li>&lt;strong>服务配置实现类&lt;/strong>, 上面的类型都是抽象类型不能做为具体存在的事物,这个类型是我们出现的第一个服务配置实现类型,服务配置实现类已经从父类型中继承了这么多的属性,这里主要为实现服务提供了一些配置如服务的协议配置,服务的代理工厂JavassistProxyFactory是将生成导出服务代理的ProxyFactory实现，是其默认实现,服务提供者模型,是否导出服务,导出的服务列表,服务监听器等等.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>ServiceBean
&lt;ul>
&lt;li>&lt;strong>服务工厂Bean&lt;/strong>	,这个主要是Spring模块来简化配置的一个服务工厂Bean这里就先不详细介绍Spring相关的配置.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="23-serviceconfig构造器的初始化调用链">2.3 ServiceConfig构造器的初始化调用链&lt;/h2>
&lt;p>有了上面的类型继承关系我们就比较好分析了,接下来我们开始创建服务配置对象如下代码所示:&lt;/p></description></item><item><title>01 从一个服务提供者的Demo说起</title><link>https://deploy-preview-3199--dubbo.netlify.app/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://deploy-preview-3199--dubbo.netlify.app/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>&lt;h1 id="1-从一个服务提供者的demo说起">1 从一个服务提供者的Demo说起&lt;/h1>
&lt;p>为了更方便了解原理,我们先来编写一个Demo,从例子中来看源码实现:&lt;/p>
&lt;h2 id="11-启动zookeeper">1.1 启动Zookeeper&lt;/h2>
&lt;p>为了Demo可以正常启动,需要我们先在本地启动一个Zookeeper如下图所示:
&lt;img alt="在这里插入图片描述" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/source-blog/1-zookeeper.png">&lt;/p>
&lt;h2 id="12-服务提供者">1.2 服务提供者&lt;/h2>
&lt;p>接下来给大家贴一下示例源码,这个源码来源于Dubbo源码目录的	dubbo-demo/dubbo-demo-api 目录下面的dubbo-demo-api-provider子项目,这里我做了删减,方便看核心代码:
首先我们定义一个服务接口如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 同步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 用于异步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture.completedFuture(sayHello(name));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>服务实现类如下:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.RpcContext;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.Logger;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.LoggerFactory;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Logger logger &lt;span style="color:#719e07">=&lt;/span> LoggerFactory.getLogger(DemoServiceImpl.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.info(&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, request from consumer: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext.getServiceContext().getRemoteAddress());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext.getServiceContext().getLocalAddress();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="13-启用服务">1.3 启用服务&lt;/h2>
&lt;p>有了服务接口之后我们来启用服务,启用服务的源码如下:&lt;/p></description></item><item><title>Rest 协议</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/07/26/rest-%E5%8D%8F%E8%AE%AE/</link><pubDate>Tue, 26 Jul 2022 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/07/26/rest-%E5%8D%8F%E8%AE%AE/</guid><description>&lt;h1 id="dubborestprotocol设计文档">Dubbo RestProtocol 设计文档&lt;/h1>
&lt;h2 id="原版本dubborest">原版本dubbo rest&lt;/h2>
&lt;p>consumer&lt;/p>
&lt;p>restClient支持 依赖resteasy 不支持spring mvc &lt;/p>
&lt;p>provider(较重)&lt;/p>
&lt;p>依赖web container   (tomcat,jetty，)servlet 模式，jaxrs netty server&lt;/p>
&lt;h3 id="改版dubborest">改版dubbo rest &lt;/h3>
&lt;p>方向：&lt;/p>
&lt;p>更加轻量，具有dubbo风格的rest，微服务体系互通（Springcloud Alibaba）&lt;/p>
&lt;p>1.注解解析&lt;/p>
&lt;p>2.报文编解码&lt;/p>
&lt;p>3.restClient&lt;/p>
&lt;p>4.restServer(netty)&lt;/p>
&lt;p>支持程度：&lt;/p>
&lt;p>content-type   text json xml form(后续会扩展)&lt;/p>
&lt;p>注解&lt;/p>
&lt;p>param,header,body,pathvariable （spring mvc &amp;amp; resteasy）&lt;/p>
&lt;h2 id="http协议报文">Http 协议报文&lt;/h2>
&lt;pre>&lt;code>POST /test/path? HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-type: application/json


{&amp;quot;name&amp;quot;:&amp;quot;dubbo&amp;quot;,&amp;quot;age&amp;quot;:10,&amp;quot;address&amp;quot;:&amp;quot;hangzhou&amp;quot;}
&lt;/code>&lt;/pre>
&lt;h3 id="dubbohttpheader">dubbo http(header)&lt;/h3>
&lt;pre>&lt;code>// service key header
path: com.demo.TestInterface
group: demo
port: 80
version: 1.0.0

// 保证长连接
Keep-Alive,Connection: keep-alive
Keep-alive: 60

// RPCContext Attachment
userId: 123456
&lt;/code>&lt;/pre>
&lt;h2 id="目前支持粒度">目前支持粒度：&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">数据位置&lt;/th>
 &lt;th style="text-align: left">content-type&lt;/th>
 &lt;th style="text-align: left">spring注解&lt;/th>
 &lt;th style="text-align: left">resteasy注解&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">body&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">ReuqestBody&lt;/td>
 &lt;td style="text-align: left"> 无注解即为body&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">querystring(?test=demo)&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">RequestParam&lt;/td>
 &lt;td style="text-align: left">QueryParam&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">header&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">RequestHeader&lt;/td>
 &lt;td style="text-align: left">PathParam&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">form&lt;/td>
 &lt;td style="text-align: left">application/x-www-form-urlencoded&lt;/td>
 &lt;td style="text-align: left">RequestParam ReuqestBody&lt;/td>
 &lt;td style="text-align: left">FormParam&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">path&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">PathVariable&lt;/td>
 &lt;td style="text-align: left">PathParam&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">method&lt;/td>
 &lt;td style="text-align: left">无要求&lt;/td>
 &lt;td style="text-align: left">PostMapping GetMapping&lt;/td>
 &lt;td style="text-align: left">GET POST&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">url&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">PostMapping GetMapping path属性&lt;/td>
 &lt;td style="text-align: left">Path&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">content-type&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">PostMapping GetMapping consumers属性&lt;/td>
 &lt;td style="text-align: left">Consumers&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">Accept&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;td style="text-align: left">PostMapping GetMapping produces属性&lt;/td>
 &lt;td style="text-align: left">Produces&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="rest注解解析servicerestmetadataresolver">rest注解解析（ServiceRestMetadataResolver）&lt;/h2>
&lt;pre>&lt;code>JAXRSServiceRestMetadataResolver

SpringMvcServiceRestMetadataResolver
&lt;/code>&lt;/pre>
&lt;p>ServiceRestMetadata&lt;/p></description></item><item><title>浅析 Dubbo 3.0 中接口级地址推送性能的优化</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/06/23/%E6%B5%85%E6%9E%90-dubbo-3.0-%E4%B8%AD%E6%8E%A5%E5%8F%A3%E7%BA%A7%E5%9C%B0%E5%9D%80%E6%8E%A8%E9%80%81%E6%80%A7%E8%83%BD%E7%9A%84%E4%BC%98%E5%8C%96/</link><pubDate>Thu, 23 Jun 2022 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/06/23/%E6%B5%85%E6%9E%90-dubbo-3.0-%E4%B8%AD%E6%8E%A5%E5%8F%A3%E7%BA%A7%E5%9C%B0%E5%9D%80%E6%8E%A8%E9%80%81%E6%80%A7%E8%83%BD%E7%9A%84%E4%BC%98%E5%8C%96/</guid><description>&lt;h2 id="url-简介">URL 简介&lt;/h2>
&lt;p>在阐述地址推送性能的具体优化之前，我们有必要先了解一下与之息息相关的内容 &amp;mdash; URL。&lt;/p>
&lt;h3 id="定义">定义&lt;/h3>
&lt;p>在不谈及 dubbo 时，我们大多数人对 URL 这个概念并不会感到陌生。统一资源定位器 (&lt;a href="https://www.ietf.org/rfc/rfc1738.txt">RFC1738&lt;/a>――Uniform Resource Locators (URL))应该是最广为人知的一个 RFC 规范，它的定义也非常简单。&lt;/p>
&lt;blockquote>
&lt;p>因特网上的可用资源可以用简单字符串来表示，该文档就是描述了这种字符串的语法和语 义。而这些字符串则被称为：“统一资源定位器”（URL）&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>一个标准的 URL 格式&lt;/strong>至多可以包含如下的几个部分&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>protocol://username:password@host:port/path?key=value&amp;amp;key=value
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>一些典型 URL&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>http://www.facebook.com/friends?param1=value1&amp;amp;amp;param2=value2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>https://username:password@10.20.130.230:8080/list?version=1.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ftp://username:password@192.168.1.7:21/1/read.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当然，也有一些&lt;strong>不太符合常规的 URL&lt;/strong>，也被归类到了 URL 之中&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>192.168.1.3:20880
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = null, url host = 192.168.1.3, port = 20880, url path = null
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file:///home/user1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = file, url host = null, url path = home/user1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file://home/user1/router.js?type=script&amp;lt;br&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = file, url host = home, url path = user1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file:///D:/1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = file, url host = null, url path = D:/1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file:/D:/1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>同上 file:///D:/1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/home/user1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = null, url host = null, url path = home/user1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>home/user1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = null, url host = home, url path = user1/router.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="dubbo-中的-url">Dubbo 中的 URL&lt;/h3>
&lt;p>在 dubbo 中，也使用了类似的 URL，主要用于在各个扩展点之间传递数据，组成此 URL 对象的具体参数如下:&lt;/p></description></item><item><title>如何通过 Apache ShenYu 网关代理 Dubbo 服务</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/05/04/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-apache-shenyu-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</link><pubDate>Wed, 04 May 2022 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/05/04/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-apache-shenyu-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</guid><description>&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/shenyu-dubbo/ApacheShenYu-Dubbo-zh.png">&lt;/p>
&lt;h2 id="1-介绍">1. 介绍&lt;/h2>
&lt;ul>
&lt;li>Apache ShenYu&lt;/li>
&lt;/ul>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/shenyu-dubbo/shenyu.png">&lt;/p>
&lt;p>&lt;a href="https://shenyu.apache.org/zh/docs/index">Apache ShenYu(Incubating)&lt;/a> 是一个异步的，高性能的，跨语言的，响应式的 &lt;code>API&lt;/code> 网关。兼容各种主流框架体系，支持热插拔，用户可以定制化开发，满足用户各种场景的现状和未来需求，经历过大规模场景的锤炼。&lt;/p>
&lt;p>2021年5月，&lt;code>ShenYu&lt;/code>捐献给 &lt;code>Apache&lt;/code> 软件基金会，Apache 基金会全票通过，顺利进入孵化器。&lt;/p>
&lt;ul>
&lt;li>Apache Dubbo&lt;/li>
&lt;/ul>
&lt;p>&lt;code>Apache Dubbo&lt;/code> 是一款微服务开发框架，它提供了 &lt;code>RPC&lt;/code> 通信 与 微服务治理 两大关键能力。这意味着，使用 &lt;code>Dubbo&lt;/code> 开发的微服务，将具备相互之间的远程发现与通信能力， 同时利用 Dubbo 提供的丰富服务治理能力，可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 &lt;code>Dubbo&lt;/code> 是高度可扩展的，用户几乎可以在任意功能点去定制自己的实现，以改变框架的默认行为来满足自己的业务需求。&lt;/p>
&lt;h2 id="2-dubbo快速开始">2. Dubbo快速开始&lt;/h2>
&lt;p>本小节介绍如何将&lt;code>Dubbo&lt;/code>服务接入到&lt;code>ShenYu&lt;/code>网关，您可以直接在工程下找到本小节的&lt;a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">示例代码&lt;/a> 。&lt;/p>
&lt;h3 id="21-启动shenyu-admin">2.1 启动shenyu-admin&lt;/h3>
&lt;p>&lt;code>shenyu-admin&lt;/code>是&lt;code>Apache ShenYu&lt;/code>后台管理系统， 启动的方式有多种，本文通过 &lt;code>[本地部署](https://shenyu.apache.org/zh/docs/deployment/deployment-local)&lt;/code> 的方式启动。启动成功后，需要在基础配置&lt;code>-&amp;gt;&lt;/code>插件管理中，把&lt;code>dubbo&lt;/code> 插件设置为开启，并设置你的注册地址，请确保注册中心已经开启。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/shenyu-dubbo/dubbo-enable-zh.png">&lt;/p>
&lt;h3 id="22-启动shenyu网关">2.2 启动shenyu网关&lt;/h3>
&lt;p>在这里通过 &lt;a href="https://github.com/apache/incubator-shenyu/tree/master/shenyu-bootstrap">源码&lt;/a> 的方式启动，直接运行&lt;code>shenyu-bootstrap&lt;/code>中的&lt;code>ShenyuBootstrapApplication&lt;/code>。&lt;/p>
&lt;p>在启动前，请确保网关已经引入相关依赖。如果客户端是&lt;code>apache dubbo&lt;/code>，注册中心使用&lt;code>zookeeper&lt;/code>，请参考如下配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> apache shenyu apache dubbo plugin start&lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org.apache.shenyu&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>shenyu&lt;span style="color:#719e07">-&lt;/span>spring&lt;span style="color:#719e07">-&lt;/span>boot&lt;span style="color:#719e07">-&lt;/span>starter&lt;span style="color:#719e07">-&lt;/span>plugin&lt;span style="color:#719e07">-&lt;/span>apache&lt;span style="color:#719e07">-&lt;/span>dubbo&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>${project.version}&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org.apache.dubbo&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>dubbo&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>2.7.5&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> Dubbo zookeeper registry dependency start &lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org.apache.curator&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>curator&lt;span style="color:#719e07">-&lt;/span>client&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>4.0.1&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>exclusions&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>exclusion&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>log4j&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>log4j&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>exclusion&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>exclusions&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org.apache.curator&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>curator&lt;span style="color:#719e07">-&lt;/span>framework&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>4.0.1&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>org.apache.curator&lt;span style="color:#719e07">&amp;lt;/&lt;/span>groupId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>curator&lt;span style="color:#719e07">-&lt;/span>recipes&lt;span style="color:#719e07">&amp;lt;/&lt;/span>artifactId&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>4.0.1&lt;span style="color:#719e07">&amp;lt;/&lt;/span>version&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;/&lt;/span>dependency&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> Dubbo zookeeper registry dependency end &lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;!--&lt;/span> apache dubbo plugin end&lt;span style="color:#719e07">--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="23-启动shenyu-examples-dubbo">2.3 启动shenyu-examples-dubbo&lt;/h3>
&lt;p>以官网提供的例子为例 &lt;a href="https://github.com/apache/shenyu/tree/master/shenyu-examples/shenyu-examples-dubbo">shenyu-examples-dubbo&lt;/a> 。 假如&lt;code>dubbo&lt;/code>服务定义如下：&lt;/p></description></item><item><title>Dubbo3 应用级服务发现</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/06/02/dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</link><pubDate>Wed, 02 Jun 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/06/02/dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</guid><description>&lt;h2 id="1-服务发现service-discovery-概述">1 服务发现（Service Discovery） 概述&lt;/h2>
&lt;p>从 Internet 刚开始兴起，如何动态感知后端服务的地址变化就是一个必须要面对的问题，为此人们定义了 DNS 协议，基于此协议，调用方只需要记住由固定字符串组成的域名，就能轻松完成对后端服务的访问，而不用担心流量最终会访问到哪些机器 IP，因为有代理组件会基于 DNS 地址解析后的地址列表，将流量透明的、均匀的分发到不同的后端机器上。&lt;/p>
&lt;p>在使用微服务构建复杂的分布式系统时，如何感知 backend 服务实例的动态上下线，也是微服务框架最需要关心并解决的问题之一。业界将这个问题称之为 - 微服务的地址发现（Service Discovery），业界比较有代表性的微服务框架如 SpringCloud、Dubbo 等都抽象了强大的动态地址发现能力，并且为了满足微服务业务场景的需求，绝大多数框架的地址发现都是基于自己设计的一套机制来实现，因此在能力、灵活性上都要比传统 DNS 丰富得多。如 SpringCloud 中常用的 Eureka， Dubbo 中常用的 Zookeeper、Nacos 等，这些注册中心实现不止能够传递地址（IP + Port），还包括一些微服务的 Metadata 信息，如实例序列化类型、实例方法列表、各个方法级的定制化配置等。&lt;/p>
&lt;p>下图是微服务中 Service Discovery 的基本工作原理图，微服务体系中的实例大概可分为三种角色：服务提供者（Provider）、服务消费者（Consumer）和注册中心（Registry）。而不同框架实现间最主要的区别就体现在注册中心数据的组织：地址如何组织、以什么粒度组织、除地址外还同步哪些数据？&lt;/p>
&lt;p>&lt;img alt="img1" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/service-discovery-1.png">&lt;/p>
&lt;p>我们今天这篇文章就是围绕这三个角色展开，重点看下 Dubbo 中对于服务发现方案的设计，包括之前老的服务发现方案的优势和缺点，以及 Dubbo 3.0 中正在设计、开发中的全新的&lt;strong>面向应用粒度的地址发现方案&lt;/strong>，我们期待这个新的方案能做到：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>支持几十万/上百万级集群实例的地址发现&lt;/strong>&lt;/li>
&lt;li>&lt;strong>与不同的微服务体系（如 Spring Cloud）实现在地址发现层面的互通&lt;/strong>&lt;/li>
&lt;/ul>
&lt;h2 id="2-dubbo-地址发现机制解析">2 Dubbo 地址发现机制解析&lt;/h2>
&lt;p>我们先以一个 DEMO 应用为例，来快速的看一下 Dubbo “接口粒度”服务发现与“应用粒度”服务发现体现出来的区别。这里我们重点关注 Provider 实例是如何向注册中心注册的，并且，为了体现注册中心数据量变化，我们观察的是两个 Provider 实例的场景。&lt;/p>
&lt;p>&lt;strong>应用 DEMO 提供的服务列表如下：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.GreetingService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;greetingService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们示例注册中心实现采用的是 Zookeeper ，启动 192.168.0.103 和 192.168.0.104 两个实例后，以下是两种模式下注册中心的实际数据&lt;/p></description></item><item><title>Dubbo-Api-Docs -- Apache Dubbo文档展示&amp;测试工具</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2020/12/22/dubbo-api-docs--apache-dubbo%E6%96%87%E6%A1%A3%E5%B1%95%E7%A4%BA%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/</link><pubDate>Tue, 22 Dec 2020 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2020/12/22/dubbo-api-docs--apache-dubbo%E6%96%87%E6%A1%A3%E5%B1%95%E7%A4%BA%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/</guid><description>&lt;h1 id="dubbo-api-docs">Dubbo-Api-Docs&lt;/h1>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>Swagger 是一个规范和完整的前端框架,用于生成,描述,调用和可视化 RESTful 风格的 Web 服务.
Swagger 规范也逐渐发展成为了 OpenAPI 规范.&lt;/p>
&lt;p>Springfox 是一个集成了Swagger,基于 Sring MVC/Spring Webflux 实现的一个 Swagger 描述文件生成框架,通过使用它定义的
一些描述接口的注解自动生成Swagger的描述文件, 使 Swagger 能够展示并调用接口.&lt;/p>
&lt;p>相信很多人都听说和使用过Swagger和Springfox, 这里就不再赘述了.&lt;/p>
&lt;p>Dubbo-Admin中有接口测试功能,但是缺少接口描述的文档,所以该测试功能比较适合接口开发人员用于测试接口.而其他人想要使用该功能就必须
先通过接口开发者编写的文档或者其他方式了解清楚接口信息才能使用该功能测试接口.
Dubbo这边有没有集合文档展示和测试功能,能不用写文档就能把接口直接给调用方,类似Swagger/Springfox的工具呢?
之前做过一些调研,找到一些类似的工具:&lt;/p>
&lt;ul>
&lt;li>有些是基于Springfox做的,直接一个文本域放JSON, 与目前Admin中的测试功能大同小异&lt;/li>
&lt;li>有些是直接基于Swagger的Java版OpenApi规范生成工具做的,能把一些基础数据类型的简单参数作为表单项展示&lt;/li>
&lt;/ul>
&lt;p>它们都有一个共同点: 会把你的提供者变为Web项目. 当然有些提供者是通过web容器加载启动的,甚至也有和web工程在一起的,那就无所谓了.
但也有非web的提供者. 为了文档我得把它变为web项目吗?(还要引入一堆Web框架的依赖?比如Spring MVC)或者说生产环境打包时删除它的引用
和代码里的相关注解? 有没有简单点的方式呢?&lt;/p>
&lt;p>OpenAPI中没有RPC的规范,Swagger是OpenAPI的实现,所以也不支持RPC相关调用.Springfox是通过Swagger实现的 RESTful API的工具,
而RESTful又是基于Web的,Dubbo没法直接使用.我们最终选择了自己实现:&lt;/p>
&lt;ul>
&lt;li>提供一些描述接口信息的简单注解&lt;/li>
&lt;li>在提供者启动时解析注解并缓存解析结果&lt;/li>
&lt;li>在提供者增加几个Dubbo-Api-Docs使用的获取接口信息的接口&lt;/li>
&lt;li>在Dubbo Admin侧通过Dubbo泛化调用实现Http方式调用Dubbo接口的网关&lt;/li>
&lt;li>在Dubbo Admin侧实现接口信息展示和调用接口功能&lt;/li>
&lt;li>下列情况中的参数直接展示为表单项,其他的展示为JSON:
&lt;ul>
&lt;li>方法参数为基础数据类型的&lt;/li>
&lt;li>方法参数为一个Bean,Bena中属性为基础数据类型的&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>很少的第三方依赖,甚至大部分都是你项目里本身就使用的&lt;/li>
&lt;li>可以通过profile决定是否加载, 打包时简单的修改profile就能区分生产和测试,甚至profile你本来就使用了&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>今天,我很高兴的宣布: Dubbo 用户也可以享受类似Swagger的体验了 &amp;ndash; Dubbo-Api-Docs发布了.&lt;/p>
&lt;/blockquote>
&lt;h2 id="简介">简介&lt;/h2>
&lt;p>Dubbo-Api-Docs 是一个展示dubbo接口文档,测试接口的工具.&lt;/p>
&lt;p>使用 Dubbo-Api-Docs 分为两个主要步骤:&lt;/p>
&lt;ol>
&lt;li>在dubbo项目引入Dubbo-Api-Docs 相关jar包,并增加类似Swagger的注解.&lt;/li>
&lt;li>在 Dubbo-Admin 中查看接口描述并测试.&lt;/li>
&lt;/ol>
&lt;p>通过以上两个步骤即可享受类似Swagger的体验, 并且可以在生产环境中关闭Dubbo-Api-Docs的扫描.&lt;/p></description></item><item><title>Dubbo测试验证</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/12/02/dubbo%E6%B5%8B%E8%AF%95%E9%AA%8C%E8%AF%81/</link><pubDate>Mon, 02 Dec 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/12/02/dubbo%E6%B5%8B%E8%AF%95%E9%AA%8C%E8%AF%81/</guid><description>&lt;p>除了线上常规的使用场景以外，我们在日常使用中还需要一些特定的使用方式，比如对正在开发的功能进行验证测试，比如单独调用某台机器的服务，这篇文章就来介绍一下这些场景下的使用方式。&lt;/p>
&lt;h3 id="只订阅">只订阅&lt;/h3>
&lt;p>为方便开发测试，经常会在线下共用一个所有服务可用的注册中心，这时，如果一个正在开发中的服务提供者注册，可能会影响消费者不能正常运行。&lt;/p>
&lt;p>可以让服务提供者开发方，只订阅服务(开发的服务可能依赖其它服务)，而不注册正在开发的服务，通过直连测试正在开发的服务。 &lt;br>
&lt;img alt="subscribe-only" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/subscribe-only.jpg">
禁用注册配置&lt;/p>
&lt;pre>&lt;code>&amp;lt;dubbo:registry address=&amp;quot;10.20.153.10:9090&amp;quot; register=&amp;quot;false&amp;quot; /&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>或者&lt;/p>
&lt;pre>&lt;code>&amp;lt;dubbo:registry address=&amp;quot;10.20.153.10:9090?register=false&amp;quot; /&amp;gt;
&lt;/code>&lt;/pre>
&lt;h3 id="指定ip调用">指定IP调用&lt;/h3>
&lt;p>在开发及测试环境下，经常需要绕过注册中心，只测试指定服务提供者，这时候可能需要点对点直连，点对点直联方式，将以服务接口为单位，忽略注册中心的提供者列表，A 接口配置点对点，不影响 B 接口从注册中心获取列表&lt;br>
&lt;img alt="subscribe-only" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-directly.jpg">&lt;/p>
&lt;p>可以通过以下几种配置来指定IP调用&lt;/p>
&lt;ul>
&lt;li>XML 配置： 如果是线上需求需要点对点，可在 &lt;code>&amp;lt;dubbo:reference&amp;gt;&lt;/code> 中配置 url 指向提供者，将绕过注册中心，多个地址用分号隔开，配置如下：
&lt;code>&amp;lt;dubbo:reference id=&amp;quot;xxxService&amp;quot; interface=&amp;quot;com.alibaba.xxx.XxxService&amp;quot; url=&amp;quot;dubbo://localhost:20890&amp;quot; /&amp;gt;&lt;/code>&lt;/li>
&lt;li>通过-D参数指定： 在 JVM 启动参数中加入-D参数映射服务地址，如：&lt;code>java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890&lt;/code>&lt;/li>
&lt;li>通过文件映射: 如果服务比较多，也可以用文件映射，用 -Ddubbo.resolve.file 指定映射文件路径，此配置优先级高于 &lt;code>&amp;lt;dubbo:reference&amp;gt;&lt;/code> 中的配置，如：
&lt;code>java -Ddubbo.resolve.file=xxx.properties&lt;/code>&lt;br>
然后在映射文件 xxx.properties 中加入配置，其中 key 为服务名，value 为服务提供者 URL：&lt;code>com.alibaba.xxx.XxxService=dubbo://localhost:20890&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="回声测试">回声测试&lt;/h3>
&lt;h4 id="使用方式">使用方式&lt;/h4>
&lt;p>回声测试用于检测服务是否可用，回声测试按照正常请求流程执行，能够测试整个调用是否通畅，可用于监控。&lt;/p>
&lt;p>所有服务自动实现 EchoService 接口，只需将任意服务引用强制转型为 EchoService，即可使用。&lt;/p>
&lt;p>Spring 配置：&lt;/p>
&lt;pre>&lt;code>&amp;lt;dubbo:reference id=&amp;quot;memberService&amp;quot; interface=&amp;quot;com.xxx.MemberService&amp;quot; /&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>// 远程服务引用
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>MemberService memberService = ctx.getBean(&amp;#34;memberService&amp;#34;); 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>EchoService echoService = (EchoService) memberService; // 强制转型为EchoService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 回声测试可用性
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>String status = echoService.$echo(&amp;#34;OK&amp;#34;); 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>assert(status.equals(&amp;#34;OK&amp;#34;));
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现原理">实现原理&lt;/h4>
&lt;p>我们在实现，注册服务的时候，并没有配置EchoService这个接口，为什么可以直接使用呢？原来是Dubbo在生成proxy的时候，已经实现了&lt;code>EchoService这个接口&lt;/code>&lt;/p></description></item><item><title>Dubbo 在 Service Mesh 下的思考和方案</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/11/30/dubbo-%E5%9C%A8-service-mesh-%E4%B8%8B%E7%9A%84%E6%80%9D%E8%80%83%E5%92%8C%E6%96%B9%E6%A1%88/</link><pubDate>Sat, 30 Nov 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/11/30/dubbo-%E5%9C%A8-service-mesh-%E4%B8%8B%E7%9A%84%E6%80%9D%E8%80%83%E5%92%8C%E6%96%B9%E6%A1%88/</guid><description>&lt;h2 id="开头">开头&lt;/h2>
&lt;p>Service Mesh这个“热”词是2016年9月被“造”出来，而今年2018年更是被称为service Mesh的关键之年，各家大公司都希望能在这个思潮下领先一步。今天我也分享阿里中间件在这方面的观点，思考和实践。考虑到有些人没了解过Dubbo(集团内以HSF为主)和Servicemesh，先简单介绍下这两个词。Dubbo应该是国内最受欢迎的远程服务框架，在Github上有超过2w的star数，也是阿里分布式架构互联互通的核心所在。跟Dubbo一样，servicemesh也是面向服务互联互通这一问题域，是云原生技术栈的核心之一；大家可以简单理解service mesh就是云原生组织定义的微服务架构解决理念。Dubbo是实现框架，融入service mesh理念就是我们今天分享的。&lt;/p>
&lt;h2 id="现状和挑战">现状和挑战&lt;/h2>
&lt;p>&lt;img alt="1.png | center | 826x206" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbomesh/1.png">&lt;/p>
&lt;p>当前Dubbo支撑的阿里分布式应用内支撑万级别的应用数，运行在20多万的服务器实例上，每天调用量是万亿级别，这应该是国内最大的分布式应用集群。&lt;/p>
&lt;p>挑战主要来自三方面&lt;/p>
&lt;ul>
&lt;li>首先， 数以万计的应用意味着有以十万级的服务，理顺错综复杂的服务拓扑关系，甚至及时诊断某个异常调用链路，需要考虑海量数据的拉取分析，是非常有挑战的，阿里通过EagleEye鹰眼链路系统提供可观察性和治理能力来解决；&lt;/li>
&lt;li>第二个挑战是机房级别容灾，阿里的机房是分布在天南海北，大家可以想象横跨数千公里的网络延迟会造成服务互通很大的影响，所以在保证一定恢复时间和一定数据容错的情况下做异地多活是有巨大挑战，阿里通过支持异地多活的单元化架构解决。&lt;/li>
&lt;li>第三个挑战是阿里业务众多，尤其像阿里生态中的高德，UC，优酷等所使用的开发语言跟淘系Java是不一样的，比如PHP，C，Nodejs，Dart等，要维护多个版本并保证各版本具有同样的功能是成本比较高的；这个挑战在云原生的新一代理念下更具挑战，毕竟。今天主题跟第三个挑战是息息相关，能解决一定的问题。&lt;/li>
&lt;/ul>
&lt;p>这里讲个大鱼吃小鱼的故事来简单理解下云原生：软件会吃掉这个世界，也就是信息化不可避免，而开源会吃掉软件，最终云原生会吃掉开源。这正代表了云原生理念的颠覆性，从商业软件到开源到云原生，环环相套，以体系化和层次化的方式推荐各个方面的开源方案和标准，这会极大降低企业级架构服务的技术门槛，是企业信息化之路的一大利好，当然也是进化方向。这个故事跟今天的主题&amp;ndash;开发者定义软件未来，是非常契合，也就是说这个趋势至少在企业级软件服务领域正在发生。云原生：Cloud Native is Patterns with A complete and trusted tool kit for modern architectures。&lt;/p>
&lt;p>&lt;span data-type="color" style="color:white">Service Mesh的典型方案&lt;/span>&lt;/p>
&lt;h2 id="service-mesh的典型方案">Service Mesh的典型方案&lt;/h2>
&lt;p>&lt;img alt="2.png | center | 826x206" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbomesh/2.png">&lt;/p>
&lt;p>讲完故事，回到servicemesh。&lt;/p>
&lt;p>传统形态下SDK代表着一个特定语言的库，由应用和微服务框架共处一进程内，在发布升级中共享生命周期。比较典型的代表是Twitter的finagle，Google的stubby/grpc，阿里巴巴的HSF/Dubbo.&lt;/p>
&lt;p>Serviemesh下推荐是右边Sidecar方案，Sidecar方案没有引入新的功能，只是改变了原有功能的位置，以独立的应用来存在，大家可以暂时以nginx来理解其网络代理能力也可以。&lt;/p>
&lt;p>在这张图中希望大家关注两个信息， 1）所有的sidecar形成逻辑网络被称为数据面，是业务服务的链路中是强依赖节点，承载了业务数据互联互通的基础；传统的ops管控服务被称为控制面，这部分跟传统是大同小异。 2）在sidecar形态下，网络会增加两跳，即应用与sidecar之间，他们之间的数据互通也是基于协议规范。后面会详细讲。&lt;/p>
&lt;h2 id="sidecar模式的优劣">Sidecar模式的优劣&lt;/h2>
&lt;p>&lt;img alt="3.png | center | 826x206" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbomesh/3.png">&lt;/p>
&lt;p>接下来从开发和运维两个阶段来分开比较。&lt;/p>
&lt;ul>
&lt;li>多语言支持方面，既然sidecar是独立应用，用最合适的一种语言开发完成即可，就避免了需要针对不同语言的应用场景做不同的版本开发。当前阿里选择基于C语言的Envoy做二次开发来追求最小的footprint和性能，当然也曾经历一些弯路，比如曾经用Java开发过一个sidecar，但最终由于引入JRE体量大和GC带来的抖动等问题证明不可行。有必要强调的是：这里说的是sidecar自身开发现在避免了多语言多版本的问题，而真要支持任意服务自由采用任意语言实现这一理想，是需要站在从业务到数据面再到业务的整个链路上的数据交互做思考。&lt;/li>
&lt;li>性能方面，sidecar情形下由于会增加两跳，这两跳是业务应用与sidecar的两个进程之间的调用，这是本机，即便是经过优化，也是会增加进程切换以及数据转换的开销。经过我们的优化测试，在正常的业务访问下，相比SDK形态下最多增加1毫秒的开销，这在大多数业务情形下是基本无感知无影响。&lt;/li>
&lt;li>再看运维阶段的比较，一般SDK形态的服务框架都是只关心开发的诉求，对于如何运维都是不关心，而软件生命周期中运维是最长的，如何从中间件角度解决更多的运维问题是非常有意义的。阿里的中间件经常需要升级，以库的形式升级时就需要业务方应用重新打包，这个推动业务方变更的方式是比较被动，而且周期很长。&lt;/li>
&lt;li>当以镜像为基本原子单位进行发布部署时，阿里的中间件SDK体量大概是200兆，需要与业务一起打包，这样在业务应用升级时让分发的包就显得笨重，时效性相比sidecar形态就差一截。&lt;/li>
&lt;/ul>
&lt;p>稍微总结下，sidecar具有两个明显优势，一个是多语言开发维护成本低 ，另一个是独立升级，当然代价是需要增加一点点的网络延迟。至此大家是不是觉得Sidecar基本完美？ 别着急，需要大家再思考一个问题：SDK模式下中间件组件会随应用一起发布，拥有完全一致的生命周期；而在sidecar模式下，如何管理sidecar的生命周期？这里可以拿无线耳机来举个例子，无线耳机是独立了，但必须独立电源的驱动，所以充电是要的。是的，在大规模的集群中这个点会带来不小的复杂性。&lt;/p>
&lt;h2 id="关键点">关键点&lt;/h2>
&lt;p>&lt;img alt="4.png | center | 826x206" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbomesh/4.png">&lt;/p>
&lt;p>下面跟大家分享下我们对servicemesh理解的三个关键技术点。分别是sidecar运维，数据面与控制面的集成，协议。&lt;/p>
&lt;ul>
&lt;li>先说sidecar的运维，这是个难点，也是为什么sidecar方案以前没有被广泛应用的重要原因。前面说sidecar与应用现在成为两个不同的进程，要考虑多个事宜，一是要考虑如何把sidecar与应用部署在一起，二是考虑业务进程或sidecar进程一方需要升级重启时如何协同来保证请求的正常处理或转发，即优雅上下线的问题。这些事宜考虑清楚并解决后，算是具备servicemesh的前提条件。当然，kubernetes解决了这块的事情，提供了initiator类似插件的机制来对原子性的pod进行注入sidecar，并通过健康检查机制来保证两个进程的协同。简单地也可以这么理解：先把kubernetes容器调度平台的实施是servicemesh的前提条件。&lt;/li>
&lt;li>数据面中的sidecar的服务治理能力则是其核心竞争力，包括负载均衡策略，路由，安全，权重等等，这些能力是以规则形式通过控制面来统一下发给数据面。在传统微服务框架下数据面和控制面的集成是紧耦合，也就是数据面和控制面是一体的，举例来说用了Dubbo框架，只能选择Dubbo-Ops。而Envoy作为servicemesh思潮的带领者，提出了一整套的API规范，Istio可以实现其xDS接口，阿里巴巴也可以根据自己的架构设计实现类似的服务平台。&lt;/li>
&lt;li>协议 协议 协议， 重要的事说三遍。。。sidecar和Dubbo的内核是网络协议的处理器，而sidecar又是面向多语言场景的，所以自然协议处理能力是要强调的。先说下阿里Dubbo当下向Mesh方向发展时遇到难点。首先我们的服务接口都是通过Java Interface描述，其次涉及的传输模型DTO也是Java POJO定义，最后协议也是私有的。这会导致跨语言比较难，而sidecar形态需要面向多语言，这些问题更是首当其冲。考虑到这里有点稍微偏细节点，希望大家带着如下问题来先思考下：业务应用到sidecar之间的数据交换要考虑什么? Sidecar自身在处理网络字节流时又要考虑什么？是的，首先业务应用最好都不依赖特定协议库，也不依赖特接口定义库；Sidecar自身处理数据时跟nginx很接近，但最好具备协议转换适配的能力，比如把基于HTTP的请求转换为Dubbo请求，就能轻松集成Dubbo遗留系统。&lt;/li>
&lt;/ul>
&lt;h2 id="回看协议">回看协议&lt;/h2>
&lt;p>&lt;img alt="5.png | center | 826x206" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbomesh/5.png">&lt;/p></description></item><item><title>Dubbo服务端异步接口的实现背景和实践</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/11/02/dubbo%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</link><pubDate>Sat, 02 Nov 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/11/02/dubbo%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h2 id="铺垫">铺垫&lt;/h2>
&lt;p>建议先对Dubbo的处理过程中涉及的线程阶段先做个了解，具体可参考&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/11/01/dubbo%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/">Dubbo客户端异步接口的实现背景和使用场景&lt;/a>。&lt;/p>
&lt;h2 id="实现背景">实现背景&lt;/h2>
&lt;p>有必要比较详细点的介绍下服务端的线程策略来加深用户在选择服务端异步的判断依据，同时有必要引出协程这一在服务端异步中常常会用到的“秘密武器”。&lt;/p>
&lt;h3 id="服务端的线程策略">服务端的线程策略&lt;/h3>
&lt;p>Dubbo支持多种NIO框架来做Remoting的协议实现，无论是Netty，Mina或者Grizzly，实现都大同小异，都是基于事件驱动的方式来做网络通道建立，数据流读取的。其中以Grizzly对于&lt;a href="https://javaee.github.io/grizzly/iostrategies.html">线程策略&lt;/a>的介绍为例，通常支持以下四种。Dubbo作为一个RPC框架，默认选择的是第一种策略，原因在于业务服务是CPU密集型还是IO阻塞型，是无法断定的，第一种策略是最保险的策略。当然，对于这几种策略有了了解后，再结合业务场景做针对性的选择是最完美的。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Worker-thread策略&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>最常用最普适的策略，其中IO线程将NIO事件处理委托给工作线程。&lt;/p>
&lt;p>&lt;img alt="workerthread-strategy.png" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubboasyn_server/1.png">&lt;/p>
&lt;p>此策略具有很高的伸缩性。我们可以根据需要更改IO和worker线程池的大小，并且不存在在特定NIO事件处理期间可能发生的，同一Selector各个Channel之间相互干扰的风险。&lt;/p>
&lt;p>缺点是有线程上下文切换的代价。&lt;/p>
&lt;ol start="2">
&lt;li>&lt;strong>Same-thread策略&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>可能是最有效的策略。与第一种不同，同一线程处理当前线程中的NIO事件，避免了昂贵的线程上下文切换。&lt;/p>
&lt;p>&lt;img alt="samethread-strategy.png" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubboasyn_server/2.png">&lt;/p>
&lt;p>这个策略可以调整IO线程池大小，也是具备可伸缩性；缺点也很明显，它要求业务处理中一定不要有阻塞处理，因为它可能会阻止在同一个IO线程上发生的其他NIO事件的处理。&lt;/p>
&lt;ol start="3">
&lt;li>&lt;strong>Dynamic策略&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>如前所述，前两种策略具有明显的优点和缺点。但是，如果策略可以尝试在运行时根据当前条件（负载，收集的统计信息等）巧妙地交换它们，何如？&lt;/p>
&lt;p>&lt;img alt="dynamic-strategy.png" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubboasyn_server/3.png">&lt;/p>
&lt;p>这种策略可能会带来很多好处，能更好地控制资源，前提是不要使条件评估逻辑过载，防止评估判断的复杂性会使这种策略效率低下。
多说一句，希望大家对这个策略多留意一下，它可能是Dubbo服务端异步方式的最佳搭配。我也多扯个淡，这几天关注了些adaptive XX或者predictive XX，这里看到dynamic真是亲切，Dubbo作为产品级生产级的微服务解决方案，是必须既要adaptive，又要predictive，还要dynamic，哈哈。&lt;/p>
&lt;ol start="4">
&lt;li>&lt;strong>Leader-follower策略&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>&lt;img alt="leaderfollower-strategy.png" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubboasyn_server/4.png">&lt;/p>
&lt;p>此策略类似于第一种，但它不是将NIO事件处理传递给worker线程，而是通过将控制传递给Selector给工作线程，并将实际NIO事件处理当前IO线程中。这种策略其实是把worker和IO线程阶段做了混淆，个人不建议。&lt;/p>
&lt;h3 id="协程与线程">协程与线程&lt;/h3>
&lt;p>在CPU资源的管理上，OS和JVM的最小调度单位都是线程，业务应用通过扩展实现的协程包是可以具备独立的运行单位，事实上也是基于线程来做的，核心应该是遇到IO阻塞，或者锁等待时，保存上下文，然后切换到另一个协程。至于说的协程开销低，能更高效的使用CPU，这些考虑到协程库的用户态实现和上下文设计是支持的，但也建议大家结合实际业务场景做性能测试。&lt;/p>
&lt;p>&lt;strong>在默认的Dubbo线程策略中，是有worker线程池来执行业务逻辑，但也常常会发生ThreadPool Full的问题，为了尽快释放worker线程，在业务服务的实现中会另起线程。代价是再次增加线程上下文切换，同时需要考虑链路级别的数据传送(比如tracing信息)和流控的出口控制等等。当然，如果Dubbo能够切换到Same-thread策略，再配合协程库的支持，服务端异步是一种值得推荐的使用方式。&lt;/strong>&lt;/p>
&lt;h2 id="示例">示例&lt;/h2>
&lt;p>通过示例来体验下Dubbo服务端异步接口。Demo代码请访问github之&lt;a href="https://github.com/dubbo/dubbo-samples/tree/master/2-advanced/dubbo-samples-notify">https://github.com/dubbo/dubbo-samples/tree/master/2-advanced/dubbo-samples-notify&lt;/a>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AsyncServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> AsyncService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Main sayHello() method start.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> AsyncContext asyncContext &lt;span style="color:#719e07">=&lt;/span> RpcContext.startAsync();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Thread(() &lt;span style="color:#719e07">-&amp;gt;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext.signalContextSwitch();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Attachment from consumer: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext.getContext().getAttachment(&lt;span style="color:#2aa198">&amp;#34;consumer-key1&amp;#34;&lt;/span>));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34; -- Async start.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread.sleep(500);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">catch&lt;/span> (InterruptedException e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e.printStackTrace();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext.write(&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34; -- Async end.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }).start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Main sayHello() method end.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hello, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="实践建议">实践建议&lt;/h2>
&lt;ul>
&lt;li>不要迷信服务端异步。&lt;/li>
&lt;li>服务端异步在Event-Driven或者Reactive面前基本是伪命题.&lt;span data-type="color" style="color:rgb(36, 41, 46)">&lt;span data-type="background" style="background-color:rgb(255, 255, 255)">补充下原因：服务端异步初衷是说Dubbo的服务端业务线程数（默认是200个）不够，但其实在event-driven模式下，200个肯定不需要那么多，只需要cpu核数那样就可以。只要业务实现是非阻塞的纯异步方式的业务逻辑处理，用再多的线程数都是浪费资源。&lt;/span>&lt;/span>&lt;/li>
&lt;li>要用服务端异步，建议服务端的线程策略采用same thread模式+协程包。&lt;/li>
&lt;/ul>
&lt;h2 id="小结">小结&lt;/h2>
&lt;p>Dubbo在支持业务应用时，会碰到千奇百怪的需求场景，服务端异步为用户提供了一种解决ThreadPool Full的方案。当发生ThreadPool Full的情况下，如果当前系统瓶颈是CPU，不建议用这种方案；如果系统Load不高，调高worker的线程数目，或者采用服务端异步，都是可以考虑的。&lt;/p></description></item><item><title>Dubbo客户端异步接口的实现背景和实践</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/11/01/dubbo%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</link><pubDate>Fri, 01 Nov 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/11/01/dubbo%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9E%E7%8E%B0%E8%83%8C%E6%99%AF%E5%92%8C%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h2 id="铺垫">铺垫&lt;/h2>
&lt;p>&lt;img alt="image | left" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubboasyn_client/1.png">&lt;/p>
&lt;p>先简单介绍下一次完整的Dubbo调用所经历的线程阶段。几个信息这里罗列下&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Biz&lt;del>代表业务线程，即便是业务逻辑处理所处的线程，Biz&lt;/del>线程池可能是业务自己创建维护，大多数的可能是系统框架自身管理的（比如web型的业务系统跑在Tomcat容器下，Biz&lt;del>线程就是Tomcat维护）；IO&lt;/del>代表网络数据处理线程，是IO框架（比如Netty，Grizzly）创建维护，Dubbo Remoting所默认Netty实现是NioEventloopLoopGroup；另外按照Channel与IO线程的绑定关系，也可以直接把IO~看成一个可接受事件消息的Channel。像Biz和IO这样的异步处理阶段在JDK8中有个很精确地抽象描述，叫CompletionStage。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>大家知道，线程与线程之间做数据通信的方式是共享变量，Biz和IO两个stage之间的数据通信是Queue，具体到Dubbo实现，在客户端一侧的实现（即上图中用1所标注的步骤）中Biz是通过向EventLoop的LinkedBlockingQueue放置一个Task，而EventLoop有对应的Thread会不停的迭代Queue来执行Task中所包含的信息，具体代码可以看SingleThreadEventExecutor（顺便提下，Netty中默认是用无上限的LinkedBlockingQueue，在Biz的速率高于网络速率情况下，似乎好像有Memory Leak的风险）。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>如上图所示，标准的一次RPC调用经过了图中所示的1,2,3,4的四次消息(事件)传递，分别是客户端业务线程到IO线程的请求发出，服务端IO线程到业务逻辑线程的__请求接受，__服务端处理完成后由业务逻辑线程到IO线程的响应写出，客户端收到结果后从IO线程到业务逻辑的响应处理。除了1与4之间一般需要维护响应和请求的映射对应关系，四次的事件处理都是完全独立的，所以一次RPC调用天然是异步的，而同步是基于异步而来。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="客户端异步">客户端异步&lt;/h2>
&lt;h3 id="实现背景">实现背景&lt;/h3>
&lt;p>在Java语言（其他语言不清楚）下一次本地接口的调用可以透明地通过代理机制转为远程RPC的调用，大多数业务方也比较喜欢这种与本地接口类似的编程方式做远程服务集成，所以虽然RPC内部天然是异步的，但使用Dubbo的用户使用最广泛的还是同步，而异步反而成为小众的使用场景。同步的优点是编程模型更加符合业务方的“传统”习惯，代价是在图中的1代表的请求发出事件后需要阻塞当前的Biz&lt;del>线程，一直等到4代表的响应处理后才能唤醒。在这个短则微秒级别，长则秒级的1,2,3,4处理过程中都要阻塞Biz&lt;/del>线程，就会消耗线程资源，增加系统资源的开销。&lt;/p>
&lt;p>所以，客户端异步的出发点是节省线程资源开销，代价是需要了解下异步的使用方式:)。在同步方式下API接口的返回类型是代表着某个业务类，而当异步情况下，响应返回与请求发出是完全独立的两个事件，需要API接口的返回类型变为上述中说的CompletionStage才是最贴合的，这是Dubbo在异步上支持的必然异步。回到最近的Dubbo发布版，是不改变接口的情况下，需要在服务创建时注册一个回调接口来处理响应返回事件。&lt;/p>
&lt;p>下面以示例来说。&lt;/p>
&lt;h3 id="示例">示例&lt;/h3>
&lt;p>事件通知的示例代码请参考：&lt;a href="https://github.com/dubbo/dubbo-samples/tree/master/2-advanced/dubbo-samples-notify">https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-notify&lt;/a>&lt;/p>
&lt;p>事件通知允许 Consumer 端在调用之前、调用正常返回之后或调用出现异常时，触发 &lt;code>oninvoke&lt;/code>、&lt;code>onreturn&lt;/code>、&lt;code>onthrow&lt;/code> 三个事件。&lt;/p>
&lt;p>可以通过在配置 Consumer 时，指定事件需要通知的方法，如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoCallback&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.notify.impl.NotifyImpl&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> check=&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.notify.api.DemoService&amp;#34;&lt;/span> version=&lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;cn&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span> onreturn=&lt;span style="color:#2aa198">&amp;#34;demoCallback.onreturn&amp;#34;&lt;/span> onthrow=&lt;span style="color:#2aa198">&amp;#34;demoCallback.onthrow&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中，NotifyImpl 的代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">NotifyImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Notify{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>Integer, String&lt;span style="color:#719e07">&amp;gt;&lt;/span> ret &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>Integer, String&lt;span style="color:#719e07">&amp;gt;&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onreturn&lt;/span>(String name, &lt;span style="color:#dc322f">int&lt;/span> id) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret.put(id, name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;onreturn: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onthrow&lt;/span>(Throwable ex, String name, &lt;span style="color:#dc322f">int&lt;/span> id) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;onthrow: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里要强调一点，自定义 Notify 接口中的三个方法的参数规则如下：&lt;/p></description></item><item><title>Dubbo 在跨语言和协议穿透性方向上的探索：支持 HTTP/2 gRPC 和 Protobuf</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/10/28/dubbo-%E5%9C%A8%E8%B7%A8%E8%AF%AD%E8%A8%80%E5%92%8C%E5%8D%8F%E8%AE%AE%E7%A9%BF%E9%80%8F%E6%80%A7%E6%96%B9%E5%90%91%E4%B8%8A%E7%9A%84%E6%8E%A2%E7%B4%A2%E6%94%AF%E6%8C%81-http/2-grpc-%E5%92%8C-protobuf/</link><pubDate>Mon, 28 Oct 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/10/28/dubbo-%E5%9C%A8%E8%B7%A8%E8%AF%AD%E8%A8%80%E5%92%8C%E5%8D%8F%E8%AE%AE%E7%A9%BF%E9%80%8F%E6%80%A7%E6%96%B9%E5%90%91%E4%B8%8A%E7%9A%84%E6%8E%A2%E7%B4%A2%E6%94%AF%E6%8C%81-http/2-grpc-%E5%92%8C-protobuf/</guid><description>&lt;p>本文总体上可分为基础产品简介、Dubbo 对 gRPC (HTTP/2) 和 Protobuf 的支持及示例演示三部分，在简介部分介绍了 Dubbo、HTTP/2、gRPC、Protobuf 的基本概念和特点；第二部分介绍了 Dubbo 为何要支持 gRPC (HTTP/2) 和 Protobuf，以及这种支持为 gRPC 和 Dubbo 开发带来的好处与不同；第三部分通过两个实例分别演示了 Dubbo gRPC 和 Dubbo Protobuf 的使用方式。&lt;/p>
&lt;h2 id="基本介绍">基本介绍&lt;/h2>
&lt;h3 id="dubbo-协议">Dubbo 协议&lt;/h3>
&lt;p>从协议层面展开，以下是当前 2.7 版本支持的 Dubbo 协议&lt;/p>
&lt;p>&lt;img alt="image-20191029103919557" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/grpc/dubbo-ptotocol.png">&lt;/p>
&lt;p>众所周知，Dubbo 协议是直接定义在 TCP 传输层协议之上，由于 TCP 高可靠全双工的特点，为 Dubbo 协议的定义提供了最大的灵活性，但同时也正是因为这样的灵活性，RPC 协议普遍都是定制化的私有协议，Dubbo 同样也面临这个问题。在这里我们着重讲一下 Dubbo 在协议通用性方面值得改进的地方，关于协议详细解析请参见&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/10/05/dubbo-%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3/">官网博客&lt;/a>&lt;/p>
&lt;ul>
&lt;li>Dubbo 协议体 Body 中有一个可扩展的 attachments 部分，这给 RPC 方法之外额外传递附加属性提供了可能，是一个很好的设计。但是类似的 Header 部分，却缺少类似的可扩展 attachments，这点可参考 HTTP 定义的 Ascii Header 设计，将 Body Attachments 和 Header Attachments 做职责划分。&lt;/li>
&lt;li>Body 协议体中的一些 RPC 请求定位符如 Service Name、Method Name、Version 等，可以提到 Header 中，和具体的序列化协议解耦，以更好的被网络基础设施识别或用于流量管控。&lt;/li>
&lt;li>扩展性不够好，欠缺协议升级方面的设计，如 Header 头中没有预留的状态标识位，或者像 HTTP 有专为协议升级或协商设计的特殊 packet。&lt;/li>
&lt;li>在 Java 版本的代码实现上，不够精简和通用。如在链路传输中，存在一些语言绑定的内容；消息体中存在冗余内容，如 Service Name 在 Body 和 Attachments 中都存在。&lt;/li>
&lt;/ul>
&lt;h3 id="http1">HTTP/1&lt;/h3>
&lt;p>相比于直接构建与 TPC 传输层的私有 RPC 协议，构建于 HTTP 之上的远程调用解决方案会有更好的通用性，如WebServices 或 REST 架构，使用 HTTP + JSON 可以说是一个事实标准的解决方案。&lt;/p></description></item><item><title>本地存根和本地伪装</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/10/22/%E6%9C%AC%E5%9C%B0%E5%AD%98%E6%A0%B9%E5%92%8C%E6%9C%AC%E5%9C%B0%E4%BC%AA%E8%A3%85/</link><pubDate>Tue, 22 Oct 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/10/22/%E6%9C%AC%E5%9C%B0%E5%AD%98%E6%A0%B9%E5%92%8C%E6%9C%AC%E5%9C%B0%E4%BC%AA%E8%A3%85/</guid><description>&lt;h2 id="基本概念">基本概念&lt;/h2>
&lt;p>典型的 RPC 调用客户端是依赖并且只依赖接口编程来进行远程调用的。在真正发起远程调用之前，用户往往需要做一些预处理的工作，比如提前校验参数。在拿到返回调用结果之后，用户可能需要缓存结果，或者是在调用失败的时候构造容错数据，而不是简单的抛出异常。&lt;/p>
&lt;p>这个时候，用户可以编写出类似以下的代码来处理上面提出的这些场景：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">try&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> preProcess();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> service.invoke(...);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>} &lt;span style="color:#719e07">catch&lt;/span> (Throwable e) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> mockValue;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>} &lt;span style="color:#719e07">finally&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> postProcess();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>类似的，用户也可以通过面向切面编程 &lt;em>AOP&lt;/em> 的高级技巧来解决上面的诉求，比如通过 &lt;em>Spring AOP&lt;/em> 的方式可以通过类似下面的这段配置来完成。使用 &lt;em>AOP&lt;/em> 的技巧相比上面的代码来说，避免了容错处理等与业务无关的代码对业务代码的侵入，使得业务处理主逻辑更简洁。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demo-service-stub&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoServiceStub&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demo-service-mock&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoServiceMock&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;aop:config&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:aspect&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;stub&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demo-service-stub&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:pointcut&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;stubPointcut&amp;#34;&lt;/span> expression=&lt;span style="color:#2aa198">&amp;#34;execution(* org.apache.dubbo.samples.DemoService+.*(..))&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:before&lt;/span> method=&lt;span style="color:#2aa198">&amp;#34;preProcess&amp;#34;&lt;/span> pointcut-ref=&lt;span style="color:#2aa198">&amp;#34;stubPointcut&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:after-returning&lt;/span> method=&lt;span style="color:#2aa198">&amp;#34;postProcess&amp;#34;&lt;/span> pointcut-ref=&lt;span style="color:#2aa198">&amp;#34;stubPointcut&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/aop:aspect&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:aspect&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;mock&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demo-service-mock&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:pointcut&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;mockPointcut&amp;#34;&lt;/span> expression=&lt;span style="color:#2aa198">&amp;#34;execution(* org.apache.dubbo.samples.DemoService+.*(..))&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;aop:after-throwing&lt;/span> method=&lt;span style="color:#2aa198">&amp;#34;mock&amp;#34;&lt;/span> pointcut-ref=&lt;span style="color:#2aa198">&amp;#34;mockPointcut&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/aop:aspect&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/aop:config&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>为了进一步的方便用户做 Dubbo 开发，框架提出了本地存根 &lt;em>Stub&lt;/em> 和本地伪装 &lt;em>Mock&lt;/em> 的概念。通过约定大于配置的理念，进一步的简化了配置，使用起来更加方便，并且不依赖额外的 &lt;em>AOP&lt;/em> 框架就达到了 &lt;em>AOP&lt;/em> 的效果。&lt;/p></description></item><item><title>Dubbo 中的 URL 统一模型</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/10/17/dubbo-%E4%B8%AD%E7%9A%84-url-%E7%BB%9F%E4%B8%80%E6%A8%A1%E5%9E%8B/</link><pubDate>Thu, 17 Oct 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/10/17/dubbo-%E4%B8%AD%E7%9A%84-url-%E7%BB%9F%E4%B8%80%E6%A8%A1%E5%9E%8B/</guid><description>&lt;h3 id="定义">定义&lt;/h3>
&lt;p>在不谈及 dubbo 时，我们大多数人对 URL 这个概念并不会感到陌生。统一资源定位器 (&lt;a href="https://www.ietf.org/rfc/rfc1738.txt">RFC1738&lt;/a>――Uniform Resource Locators (URL)）应该是最广为人知的一个 RFC 规范，它的定义也非常简单&lt;/p>
&lt;blockquote>
&lt;p>因特网上的可用资源可以用简单字符串来表示，该文档就是描述了这种字符串的语法和语
义。而这些字符串则被称为：“统一资源定位器”（URL）&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>一个标准的 URL 格式&lt;/strong>至多可以包含如下的几个部分&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>protocol://username:password@host:port/path?key=value&amp;amp;key=value
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>一些典型 URL&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>http://www.facebook.com/friends?param1=value1&amp;amp;amp;param2=value2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>https://username:password@10.20.130.230:8080/list?version=1.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ftp://username:password@192.168.1.7:21/1/read.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当然，也有一些&lt;strong>不太符合常规的 URL&lt;/strong>，也被归类到了 URL 之中&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>192.168.1.3:20880
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = null, url host = 192.168.1.3, port = 20880, url path = null
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file:///home/user1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = file, url host = null, url path = home/user1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file://home/user1/router.js?type=script&amp;lt;br&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = file, url host = home, url path = user1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file:///D:/1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = file, url host = null, url path = D:/1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>file:/D:/1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>同上 file:///D:/1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/home/user1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = null, url host = null, url path = home/user1/router.js
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>home/user1/router.js?type=script
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>url protocol = null, url host = home, url path = user1/router.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="dubbo-中的-url">Dubbo 中的 URL&lt;/h3>
&lt;p>在 dubbo 中，也使用了类似的 URL，主要用于在各个扩展点之间传递数据，组成此 URL 对象的具体参数如下:&lt;/p></description></item><item><title>研究 Dubbo 网卡地址注册时的一点思考</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/10/01/%E7%A0%94%E7%A9%B6-dubbo-%E7%BD%91%E5%8D%A1%E5%9C%B0%E5%9D%80%E6%B3%A8%E5%86%8C%E6%97%B6%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83/</link><pubDate>Tue, 01 Oct 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/10/01/%E7%A0%94%E7%A9%B6-dubbo-%E7%BD%91%E5%8D%A1%E5%9C%B0%E5%9D%80%E6%B3%A8%E5%86%8C%E6%97%B6%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83/</guid><description>&lt;h2 id="1-如何选择合适的网卡地址">1 如何选择合适的网卡地址&lt;/h2>
&lt;p>可能相当一部分人还不知道我这篇文章到底要讲什么，我说个场景，大家应该就明晰了。在分布式服务调用过程中，以 Dubbo 为例，服务提供者往往需要将自身的 IP 地址上报给注册中心，供消费者去发现。在大多数情况下 Dubbo 都可以正常工作，但如果你留意过 Dubbo 的 github issue，其实有不少人反馈：Dubbo Provider 注册了错误的 IP。如果你能立刻联想到：多网卡、内外网地址共存、VPN、虚拟网卡等关键词，那我建议你一定要继续将本文看下去，因为我也想到了这些，它们都是本文所要探讨的东西！那么“如何选择合适的网卡地址”呢，Dubbo 现有的逻辑到底算不算完备？我们不急着回答它，而是带着这些问题一起进行研究，相信到文末，其中答案，各位看官自有评说。&lt;/p>
&lt;h2 id="2-dubbo-是怎么做的">2 Dubbo 是怎么做的&lt;/h2>
&lt;p>Dubbo 获取网卡地址的逻辑在各个版本中也是千回百转，走过弯路，也做过优化，我们用最新的 2.7.2-SNAPSHOT 版本来介绍，在看以下源码时，大家可以怀着质疑的心态去阅读，在 dubbo github 的 master 分支可以获取源码。获取 localhost 的逻辑位于 &lt;code>org.apache.dubbo.common.utils.NetUtils#getLocalAddress0()&lt;/code> 之中&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> InetAddress &lt;span style="color:#268bd2">getLocalAddress0&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> InetAddress localAddress &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 首先尝试获取 /etc/hosts 中 hostname 对应的 IP&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> localAddress &lt;span style="color:#719e07">=&lt;/span> InetAddress.getLocalHost();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Optional&lt;span style="color:#719e07">&amp;lt;&lt;/span>InetAddress&lt;span style="color:#719e07">&amp;gt;&lt;/span> addressOp &lt;span style="color:#719e07">=&lt;/span> toValidAddress(localAddress);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (addressOp.isPresent()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> addressOp.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 没有找到适合注册的 IP，则开始轮询网卡&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Enumeration&lt;span style="color:#719e07">&amp;lt;&lt;/span>NetworkInterface&lt;span style="color:#719e07">&amp;gt;&lt;/span> interfaces &lt;span style="color:#719e07">=&lt;/span> NetworkInterface.getNetworkInterfaces();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">==&lt;/span> interfaces) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> localAddress;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> (interfaces.hasMoreElements()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NetworkInterface network &lt;span style="color:#719e07">=&lt;/span> interfaces.nextElement();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Enumeration&lt;span style="color:#719e07">&amp;lt;&lt;/span>InetAddress&lt;span style="color:#719e07">&amp;gt;&lt;/span> addresses &lt;span style="color:#719e07">=&lt;/span> network.getInetAddresses();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> (addresses.hasMoreElements()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 返回第一个匹配的适合注册的 IP&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Optional&lt;span style="color:#719e07">&amp;lt;&lt;/span>InetAddress&lt;span style="color:#719e07">&amp;gt;&lt;/span> addressOp &lt;span style="color:#719e07">=&lt;/span> toValidAddress(addresses.nextElement());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (addressOp.isPresent()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> addressOp.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> localAddress;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo 这段选取本地地址的逻辑大致分成了两步&lt;/p></description></item><item><title>Dubbo Admin服务测试功能</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/08/26/service-test/</link><pubDate>Mon, 26 Aug 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/08/26/service-test/</guid><description>&lt;p>基于Dubbo2.7的元数据，Dubbo Admin实现了服务测试功能，可以通过泛化调用，在控制台上调用真实的服务提供者&lt;/p>
&lt;h2 id="使用方式">使用方式&lt;/h2>
&lt;ul>
&lt;li>部署服务提供者： 可以在&lt;a href="https://github.com/nzomkxia/dubbo-demo">这里&lt;/a>下载demo，此工程基于spring boot，方便在IDE或者命令行启动，对于服务测试来说，只需要启动&lt;code>dubbo-basic-provider&lt;/code>即可。&lt;/li>
&lt;li>服务查询： 完成服务端部署后，可以到Dubbo Admin的&lt;code>服务测试&lt;/code>页面上查询对应的服务:
&lt;img alt="testSearch" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/testSearch.jpg">&lt;br>
这里的信息和元数据类似，包含方法名，参数类型和返回值信息，点击右边的标签就可以进入服务测试页面&lt;/li>
&lt;li>服务测试：
&lt;img alt="testSuccess" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/testSuccess.jpg">&lt;br>
服务测试页面包含了两个json编辑器，参数类型的信息都是以json格式保存，这里需要填入对应的参数值(本例中数类型时&lt;code>String&lt;/code>)，填写完成后点击&lt;code>执行&lt;/code>即可对服务端发起调用，调用结果展示在右边的编辑器中，如果调用失败，会显示详细的失败原因，下面来看一下调用失败的例子：&lt;br>
&lt;img alt="testFail" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/testFail.jpg">
本例中，先关掉Dubbo服务提供者的进程，再执行服务测试，可以看到返回的结果是&lt;code>找不到服务提供者&lt;/code>的异常。和普通调用一样，业务和框架的异常都会返回在结果中，方便业务排查。&lt;/li>
&lt;li>复合类型参数&lt;br>
考虑&lt;code>UserService&lt;/code>中的以下方法和类型：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//org.apache.dubbo.demo.api.UserService&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Result &lt;span style="color:#268bd2">getUser&lt;/span>(String name, UserInfoDO userInfoDO);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">UserInfoDO&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> id;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> LocationDO locationDO;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DepartmentDO departmentDO;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">toString&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;UserInfoDO{&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;id=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> id &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, locationDO=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> locationDO.toString() &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, departmentDO=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> departmentDO.toString() &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#39;}&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DepartmentDO&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String departName;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> LocationDO departLocation;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">toString&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;DepartmentDO{&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;departName=&amp;#39;&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> departName &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#39;\&amp;#39;&amp;#39;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, departLocation=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> departLocation.toString() &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#39;}&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">LocationDO&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String address;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> postNum;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">toString&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;LocationDO{&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;address=&amp;#39;&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> address &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#39;\&amp;#39;&amp;#39;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;, postNum=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> postNum &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#39;}&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>参数是比较复杂的符合类型参数，服务测试的时候，会逐层展开填写每一个field的值，如下图所示：&lt;br>
&lt;img alt="complex" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/complex.jpg">
同样可以调用成功并且返回结果&lt;/p></description></item><item><title>本地调用</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/08/11/%E6%9C%AC%E5%9C%B0%E8%B0%83%E7%94%A8/</link><pubDate>Sun, 11 Aug 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/08/11/%E6%9C%AC%E5%9C%B0%E8%B0%83%E7%94%A8/</guid><description>&lt;h3 id="本地调用介绍">本地调用介绍&lt;/h3>
&lt;p>当一个应用既是一个服务的提供者，同时也是这个服务的消费者的时候，可以直接对本机提供的服务发起本地调用。从 &lt;code>2.2.0&lt;/code> 版本开始，Dubbo 默认在本地以 &lt;em>injvm&lt;/em> 的方式暴露服务，这样的话，在同一个进程里对这个服务的调用会优先走本地调用。&lt;/p>
&lt;p>与本地对象上方法调用不同的是，Dubbo 本地调用会经过 Filter 链，其中包括了 Consumer 端的 Filter 链以及 Provider 端的 Filter 链。通过这样的机制，本地消费者和其他消费者都是统一对待，统一监控，服务统一进行治理。&lt;/p>
&lt;p>&lt;img alt="filter-chain" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-local-call-filter.png">&lt;/p>
&lt;p>同时，相比于远程调用来说，Dubbo 本地调用性能较优，省去了请求、响应的编解码及网络传输的过程。&lt;/p>
&lt;p>要使用 Dubbo 本地调用不需做特殊配置，按正常 Dubbo 服务暴露服务即可。任一服务在暴露远程服务的同时，也会同时以 &lt;em>injvm&lt;/em> 的协议暴露本地服务。&lt;em>injvm&lt;/em> 是一个伪协议，不会像其他协议那样对外开启端口，只用于本地调用的目的。&lt;/p>
&lt;p>以下面的 XML 配置为例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:protocol&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> port=&lt;span style="color:#2aa198">&amp;#34;20800&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoServiceTarget&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.impl.DemoServiceImpl&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.api.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoServiceTarget&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.api.DemoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里同时配置了同一服务 &lt;em>DemoService&lt;/em> 的提供者以及消费者。在这种情况下，该应用中的 &lt;em>DemoService&lt;/em> 的消费方会优先使用 &lt;em>injvm&lt;/em> 协议进行本地调用。上述的例子可以在 dubbo-samples 工程中找到源码：https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-local&lt;/p>
&lt;h3 id="细粒度控制本地调用">细粒度控制本地调用&lt;/h3>
&lt;p>本地调用是可以显示关闭的，通过这种方式，服务提供者可以做到对远端服务消费者和本地消费者一视同仁。具体做法是通过 &lt;em>scope=&amp;ldquo;remote&amp;rdquo;&lt;/em> 来关闭 &lt;em>injvm&lt;/em> 协议的暴露，这样，即使是本地调用者，也需要从注册中心上获取服务地址列表，然后才能发起调用，而这个时候的调用过程，与远端的服务消费者的过程是一致的。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;target&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.impl.DemoServiceImpl&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 服务提供者指定 scope=&amp;#34;remote&amp;#34; --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.api.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;target&amp;#34;&lt;/span> scope=&lt;span style="color:#2aa198">&amp;#34;remote&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.local.api.DemoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样的，服务消费者也支持通过 &lt;em>scope&lt;/em> 来限定发起调用优先走本地，还是只走远程。比如，可以通过以下的方式强制消费端通过&lt;strong>远程调用&lt;/strong>的方式来发起 dubbo 调用：&lt;/p></description></item><item><title>使用Apache Skywalking (Incubator) 做分布式跟踪</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/08/11/%E4%BD%BF%E7%94%A8apache-skywalking-incubator-%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</link><pubDate>Sun, 11 Aug 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/08/11/%E4%BD%BF%E7%94%A8apache-skywalking-incubator-%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</guid><description>&lt;h2 id="apache-skywalkingincubator简介">Apache Skywalking(Incubator)简介&lt;/h2>
&lt;p>&lt;a href="https://github.com/apache/skywalking">Apache Skywalking(Incubator)&lt;/a> 专门为微服务架构和云原生架构系统而设计并且支持分布式链路追踪的APM系统。&lt;a href="https://github.com/apache/skywalking">Apache Skywalking(Incubator)&lt;/a>通过加载探针的方式收集应用调用链路信息，并对采集的调用链路信息进行分析，生成应用间关系和服务间关系以及服务指标。&lt;a href="https://github.com/apache/skywalking">Apache Skywalking (Incubating)&lt;/a>目前支持多种语言，其中包括&lt;a href="https://github.com/apache/skywalking">Java&lt;/a>，&lt;a href="https://github.com/OpenSkywalking/skywalking-netcore">.Net Core&lt;/a>，&lt;a href="https://github.com/OpenSkywalking/skywalking-nodejs">Node.js&lt;/a>和&lt;a href="https://github.com/SkyAPM/go2sky">Go&lt;/a>语言。&lt;/p>
&lt;p>目前Skywalking已经支持从6个可视化维度剖析分布式系统的运行情况。总览视图是应用和组件的全局视图，其中包括组件和应用数量，应用的告警波动，慢服务列表以及应用吞吐量；拓扑图从应用依赖关系出发，展现整个应用的拓扑关系；应用视图则是从单个应用的角度，展现应用的上下游关系，TopN的服务和服务器，JVM的相关信息以及对应的主机信息。服务视图关注单个服务入口的运行情况以及此服务的上下游依赖关系，依赖度，帮助用户针对单个服务的优化和监控；调用链展现了调用的单次请求经过的所有埋点以及每个埋点的执行时长；告警视图根据配置阈值针对应用、服务器、服务进行实时告警。&lt;/p>
&lt;h2 id="dubbo与apache-skywalkingincubator">Dubbo与Apache Skywalking(Incubator)&lt;/h2>
&lt;h3 id="编写dubbo示例程序">编写Dubbo示例程序&lt;/h3>
&lt;p>Dubbo实例程序已上传到&lt;a href="https://github.com/SkywalkingTest/dubbo-trace-example">Github仓库&lt;/a>中。方便大家下载使用。&lt;/p>
&lt;h4 id="api工程">API工程&lt;/h4>
&lt;p>服务接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.skywalking.demo.interfaces;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public &lt;span style="color:#268bd2">interface&lt;/span> HelloService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="dubbo服务提供工程">Dubbo服务提供工程&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.skywalking.demo.provider;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@&lt;span style="color:#268bd2">Service&lt;/span>(version = &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	application = &lt;span style="color:#2aa198">&amp;#34;${dubbo.application.id}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	protocol = &lt;span style="color:#2aa198">&amp;#34;${dubbo.protocol.id}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	registry = &lt;span style="color:#2aa198">&amp;#34;${dubbo.registry.id}&amp;#34;&lt;/span>, timeout = &lt;span style="color:#2aa198">60000&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class HelloServiceImpl implements HelloService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	public String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		LockSupport.&lt;span style="color:#268bd2">parkNanos&lt;/span>(TimeUnit.SECONDS.&lt;span style="color:#268bd2">toNanos&lt;/span>(&lt;span style="color:#2aa198">1&lt;/span>));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="consumer工程">Consumer工程&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.skywalking.demo.consumer;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@RestController
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class ConsumerController {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	private static &lt;span style="color:#dc322f">int&lt;/span> COUNT = &lt;span style="color:#2aa198">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	@&lt;span style="color:#268bd2">Reference&lt;/span>(version = &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		application = &lt;span style="color:#2aa198">&amp;#34;${dubbo.application.id}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		url = &lt;span style="color:#2aa198">&amp;#34;dubbo://localhost:20880&amp;#34;&lt;/span>, timeout = &lt;span style="color:#2aa198">60000&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	private HelloService helloService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	@&lt;span style="color:#268bd2">GetMapping&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;/sayHello/{name}&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	public String &lt;span style="color:#268bd2">sayHello&lt;/span>(@&lt;span style="color:#268bd2">PathVariable&lt;/span>(name = &lt;span style="color:#2aa198">&amp;#34;name&amp;#34;&lt;/span>) String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">if&lt;/span> ((COUNT&lt;span style="color:#719e07">++&lt;/span>) &lt;span style="color:#719e07">%&lt;/span> &lt;span style="color:#2aa198">3&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			throw new &lt;span style="color:#268bd2">RuntimeException&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		LockSupport.&lt;span style="color:#268bd2">parkNanos&lt;/span>(TimeUnit.SECONDS.&lt;span style="color:#268bd2">toNanos&lt;/span>(&lt;span style="color:#2aa198">2&lt;/span>));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">return&lt;/span> helloService.&lt;span style="color:#268bd2">sayHello&lt;/span>(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="部署apache-skywalkingincubator">部署Apache Skywalking(Incubator)&lt;/h3>
&lt;p>Apache Skywalking(Incubator）共提供两种部署模式：单节点模式和集群模式，以下为单节点模式部署步骤，集群模式部署详情参考&lt;a href="https://skywalking.apache.org/docs/main/next/en/setup/backend/backend-setup/">文档&lt;/a>。&lt;/p></description></item><item><title>在 Dubbo 中使用 REST</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/07/26/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-rest/</link><pubDate>Fri, 26 Jul 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/07/26/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-rest/</guid><description>&lt;h2 id="什么是-rest">什么是 REST&lt;/h2>
&lt;p>REST 是 Roy Thomas Fielding &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> 在 2000 年他的博士论文 &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> “架构风格以及基于网络的软件架构设计” 中提出来的一个概念。REST 是 &lt;strong>RE&lt;/strong>presentational &lt;strong>S&lt;/strong>tate &lt;strong>T&lt;/strong>ransfer 的缩写，翻译过来就是 “表现层状态转化”。REST 就是 Roy 在这篇论文中提出的面向互联网的软件所应当具备的架构风格。&lt;/p>
&lt;p>按照 REpresentational State Transfer 的字面意思，可以把应用看成是一个虚拟的状态机，软件提供的不是服务而是一系列的&lt;strong>资源&lt;/strong>，对这些资源的访问通过&lt;strong>统一的操作&lt;/strong>来访问，而返回的结果代表了资源状态的一次跃迁。REST 是一种架构风格，如果一个软件架构符合 REST 风格，就可以称之为 RESTful 架构。这个架构应当具备以下一些设计上的约束：资源具有唯一标示、资源之间有关联关系、使用标准的方式来访问、资源有多种表现形式、无状态交互。&lt;/p>
&lt;p>举例来说，一个简单的静态 HTML 页面的网站就很好的符合了 RESTful 架构风格。访问 &lt;code>http://example.com/accounts&lt;/code> 返回一个包含所有账号的页面，选取其中一个链接 &lt;code>http://example.com/accounts/1&lt;/code> 又会返回包含用户 1 的详细信息。爬虫软件在这种场景下工作的很好，当知道了某个网站的首页地址后，可以自举发现这个网站上所有关联的网页。更重要的是，这种访问形式不依赖网站提供的任何客户端，而是仅仅通过 HTTP 标准的访问方式完成的。可以说，HTML 这种超媒体文档的组织形式就是资源的表现层状态迁移的一种形式。&lt;/p>
&lt;p>对于一个提供服务的动态网站来说，可以按照类似的思路将其 RESTful 化：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>GET &lt;code>http://example.com/accounts&lt;/code> 返回所有账号信息&lt;/p>
&lt;/li>
&lt;li>
&lt;p>POST &lt;code>http://example.com/accounts&lt;/code> 创建一个新的账号&lt;/p>
&lt;/li>
&lt;li>
&lt;p>GET &lt;code>http://example.com/accounts/1&lt;/code> 返回账号 1 的信息&lt;/p>
&lt;/li>
&lt;li>
&lt;p>DELETE &lt;code>http://example.com/accounts/1&lt;/code> 删除账号 1&lt;/p>
&lt;/li>
&lt;li>
&lt;p>PUT &lt;code>http://example.com/accounts/1&lt;/code> 更新账号 1 信息&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>其中的思路是利用 HTTP 协议的标准方法 POST、DELETE、PUT、GET 来表达对于一个资源的增删改查 (CRUD) 操作，利用 URL 来表示一个资源的唯一标识。对资源访问的错误码也复用 HTTP 协议的状态码。返回结果通常由 json 或 XML 来表示，如果其中包括了对关联资源的访问方式 (所谓的表现层状态迁移) ，这种类型的 RESTful 应用可以进一步的称之为 &lt;em>hypermedia as the engine of application state&lt;/em> (HATEOAS) 应用 &lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>。&lt;/p></description></item><item><title>使用 Dubbo 连接异构微服务体系</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/06/22/%E4%BD%BF%E7%94%A8-dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB/</link><pubDate>Sat, 22 Jun 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/06/22/%E4%BD%BF%E7%94%A8-dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB/</guid><description>&lt;p>从编程开发的角度来说，Dubbo 首先是一款 RPC 服务框架，它最大的优势在于提供了面向接口代理的服务编程模型，对开发者屏蔽了底层的远程通信细节。同时 Dubbo 也是一款服务治理框架，它为分布式部署的微服务提供了服务发现、流量调度等服务治理解决方案。&lt;/p>
&lt;p>在这篇文章中，我们将以以上基础能力为背景，尝试突破 Dubbo 体系自身，探索如何利用 Dubbo 对多协议、多服务发现模型的支持，来实现异构微服务体系间的互联互通。在实际业务场景中，这可以用来解决异构技术体系共存场景下的通信问题，帮助公司实现在异构技术体系间作平滑迁移，解决大规模跨区域、多集群部署场景的地址发现及流量调度等问题。&lt;/p>
&lt;h2 id="面向接口代理的透明服务开发框架">面向接口代理的透明服务开发框架&lt;/h2>
&lt;p>我们还是从 &lt;strong>Dubbo 是一个微服务开发框架&lt;/strong> 这个大家熟知的概念开始。就像 Spring 是开发 Java 应用的基础框架一样，我们经常会选用 Dubbo 作为开发微服务业的基础框架。 Dubbo 框架的最大优势我认为就在其面向接口的编程模型，使得开发远程服务调用就像开发本地服务一样（以 Java 语言为例）：&lt;/p>
&lt;ol>
&lt;li>服务定义&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">GreetingsService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>消费方调用服务&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 和调用本地服务一样，完全透明。&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Reference&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> GreetingService greetingService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doSayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greetingService.sayHi(&lt;span style="color:#2aa198">&amp;#34;Hello world!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下图是 Dubbo 的基本工作原理图，服务提供者与服务消费者之间通过注册中心协调地址，通过约定的协议实现数据交换。&lt;/p>
&lt;p>&lt;img alt="Dubbo basic work flow" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/architecture.png">&lt;/p>
&lt;h2 id="同构异构微服务体系面临的问题">同构/异构微服务体系面临的问题&lt;/h2>
&lt;p>关于 Dubbo 协议本身及其服务治理相关功能细节并不是本文的重点，我们今天将从一个更高的层次，来看看公司内部构建微服务体系所面的挑战，以及 Dubbo 能为架构选型和迁移等提供哪些解决思路。&lt;/p>
&lt;p>一个公司内部的微服务可能都是基于某一个相同的服务框架开发的，比如说 Dubbo，对于这样的架构，我们称之为是&lt;strong>同构的微服务体系&lt;/strong>；而有些公司的微服务可能是使用多个不同的服务框架所建设，我们称之为&lt;strong>异构的微服务体系&lt;/strong>，多个不同技术栈微服务体系的共存在大型组织内还是非常普遍的，造成这种局面可能有很多原因。比如，可能是遗留系统带来的，也可能是公司正在做技术栈迁移，或者就是不同业务部门为了满足各自特殊需求而做的独立选型（这也意味着异构微服务体系的长期共存）。&lt;/p>
&lt;p>&lt;strong>1. 异构微服务体系共存&lt;/strong>&lt;/p>
&lt;p>我们很容易想到的一个挑战是：&lt;strong>不同的体系间通常是使用不同的 RPC 通信协议、部署独立的注册中心集群，面对这种多协议、多注册中心集群的场景，要如何实现相互之间透明的地址发现和透明的 RPC 调用？&lt;/strong> 如果我们什么都不做，那么每个微服务体系就只能感知到自己体系内的服务状态，流量也在各自的体系内封闭。而要做到从体系 A 平滑的迁移到体系 B，或者想长期的保持公司内部多个体系的共存，则解决不同体系间的互联互通，实现流量的透明调度将是非常重要的环节。&lt;/p></description></item><item><title>Dubbo可扩展机制源码解析</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/05/02/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</link><pubDate>Thu, 02 May 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/05/02/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</guid><description>&lt;p>在&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/04/25/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E5%AE%9E%E6%88%98/">Dubbo可扩展机制实战&lt;/a>中，我们了解了Dubbo扩展机制的一些概念，初探了Dubbo中LoadBalance的实现，并自己实现了一个LoadBalance。是不是觉得Dubbo的扩展机制很不错呀，接下来，我们就深入Dubbo的源码，一睹庐山真面目。&lt;/p>
&lt;h2 id="extensionloader">ExtensionLoader&lt;/h2>
&lt;p>ExtensionLoader 是最核心的类，负责扩展点的加载和生命周期管理。我们就以这个类开始吧。
ExtensionLoader 的方法比较多，比较常用的方法有:&lt;/p>
&lt;ul>
&lt;li>&lt;code>public static &amp;lt;T&amp;gt; ExtensionLoader&amp;lt;T&amp;gt; getExtensionLoader(Class&amp;lt;T&amp;gt; type)&lt;/code>&lt;/li>
&lt;li>&lt;code>public T getExtension(String name)&lt;/code>&lt;/li>
&lt;li>&lt;code>public T getAdaptiveExtension()&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>比较常见的用法有:&lt;/p>
&lt;ul>
&lt;li>&lt;code>LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName)&lt;/code>&lt;/li>
&lt;li>&lt;code>RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension()&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>说明：在接下来展示的源码中，我会将无关的代码(比如日志，异常捕获等)去掉，方便大家阅读和理解。&lt;/p>
&lt;ol>
&lt;li>getExtensionLoader方法
这是一个静态工厂方法，入参是一个可扩展的接口，返回一个该接口的ExtensionLoader实体类。通过这个实体类，可以根据name获得具体的扩展，也可以获得一个自适应扩展。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getExtensionLoader&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 扩展点必须是接口&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>type.isInterface()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException(&lt;span style="color:#2aa198">&amp;#34;Extension type(&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) is not interface!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 必须要有@SPI注解&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>withExtensionAnnotation(type)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException(&lt;span style="color:#2aa198">&amp;#34;Extension type without @SPI Annotation!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 从缓存中根据接口获取对应的ExtensionLoader&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 每个扩展只会被加载一次&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> loader &lt;span style="color:#719e07">=&lt;/span> (ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span>) EXTENSION_LOADERS.get(type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (loader &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 初始化扩展&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EXTENSION_LOADERS.putIfAbsent(type, &lt;span style="color:#719e07">new&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span>(type));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loader &lt;span style="color:#719e07">=&lt;/span> (ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span>) EXTENSION_LOADERS.get(type);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> loader;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">ExtensionLoader&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> type) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.type &lt;span style="color:#719e07">=&lt;/span> type;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> objectFactory &lt;span style="color:#719e07">=&lt;/span> (type &lt;span style="color:#719e07">==&lt;/span> ExtensionFactory.class &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>getExtension方法&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">getExtension&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Holder&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> holder &lt;span style="color:#719e07">=&lt;/span> cachedInstances.get(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (holder &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cachedInstances.putIfAbsent(name, &lt;span style="color:#719e07">new&lt;/span> Holder&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">&amp;gt;&lt;/span>());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> holder &lt;span style="color:#719e07">=&lt;/span> cachedInstances.get(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object instance &lt;span style="color:#719e07">=&lt;/span> holder.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 从缓存中获取，如果不存在就创建&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> (holder) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> holder.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> createExtension(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> holder.set(instance);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> (T) instance;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>getExtension 方法中做了一些判断和缓存，主要的逻辑在 createExtension 方法中。我们继续看 createExtension 方法。&lt;/p></description></item><item><title>Dubbo 一致性Hash负载均衡实现剖析</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/05/01/dubbo-%E4%B8%80%E8%87%B4%E6%80%A7hash%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E5%AE%9E%E7%8E%B0%E5%89%96%E6%9E%90/</link><pubDate>Wed, 01 May 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/05/01/dubbo-%E4%B8%80%E8%87%B4%E6%80%A7hash%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E5%AE%9E%E7%8E%B0%E5%89%96%E6%9E%90/</guid><description>&lt;p>需要强调的是，Dubbo的Hash映射模型与大部分网上资料描述的&lt;strong>环形队列Hash映射模型&lt;/strong>是存在一些区别的。于我而言，环形队列Hash映射模型，不足以让我对一致性Hash有足够彻底的了解。直到看懂了Dubbo的一致性Hash的实现，才觉得豁然开朗。&lt;/p>
&lt;h3 id="一环形队列hash映射模型">一、环形队列Hash映射模型&lt;/h3>
&lt;p>这种方案，其基础还是基于取模运算。对2^32取模，那么，Hash值的区间为[0, 2^32-1]。接下来要做的，就包括两部分：&lt;/p>
&lt;h4 id="a映射服务">&lt;strong>a、映射服务&lt;/strong>&lt;/h4>
&lt;p>将服务地址（ip+端口）按照一定规则构造出特定的识别码（如md5码），再用识别码对2^32取模，确定服务在Hash值区间对应的位置。假设有Node1、Node2、Node3三个服务，其映射关系如下：&lt;/p>
&lt;p>&lt;img alt="Init" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-init-model.jpg">&lt;/p>
&lt;h4 id="b映射请求定位服务">&lt;strong>b、映射请求、定位服务&lt;/strong>&lt;/h4>
&lt;p>在发起请求时，我们往往会带上参数，而这些参数，就可以被我们用来确定具体调用哪一个服务。假设有请求R1、R2、R3，对它们的参数也经过计算特定识别码、取余的一系列运算之后，有如下映射关系：&lt;/p>
&lt;p>&lt;img alt="Request" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-request-model.jpg">&lt;/p>
&lt;p>从图中，我们可以看到，R1请求映射在0-Node1中间，R2请求映射在Node1-Node2中间，R3请求映射在Node2-Node3中间。我们取&lt;strong>服务Hash值大于等于请求Hash值&lt;/strong>的&lt;strong>第一个服务&lt;/strong>作为实际的调用服务。也就是说，R1请求将调用Node1服务，R2请求将调用Node2服务，R3请求将调用Node3服务。&lt;/p>
&lt;h4 id="c新增服务节点">&lt;strong>c、新增服务节点&lt;/strong>&lt;/h4>
&lt;p>假设新增服务Node4，映射在Node3之前，恰巧破坏了原来的一个映射关系：&lt;/p>
&lt;p>&lt;img alt="New Node" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-new-node-model.jpg">&lt;/p>
&lt;p>这样，请求R3将会实际调用服务Node4，但请求R1、R2不受影响。&lt;/p>
&lt;h4 id="d删除服务节点">&lt;strong>d、删除服务节点&lt;/strong>&lt;/h4>
&lt;p>假设服务Node2宕机，那么R2请求将会映射到Node3：&lt;/p>
&lt;p>&lt;img alt="Delete Node" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-delete-node-model.jpg">&lt;/p>
&lt;p>原本的R1、R3请求不受影响。&lt;/p>
&lt;blockquote>
&lt;p>可以看出，当新增、删除服务时，受影响的请求是有限的。不至于像简单取模映射一般，服务发生变化时，需要调整全局的映射关系。&lt;/p>
&lt;/blockquote>
&lt;h4 id="e平衡性与虚拟节点">&lt;strong>e、平衡性与虚拟节点&lt;/strong>&lt;/h4>
&lt;p>在我们上面的假设中，我们假设Node1、Node2、Node3三个服务在经过Hash映射后所分布的位置恰巧把环切成了均等的三分，请求的分布也基本是平衡的。但是实际上计算服务Hash值的时候，是很难这么巧的。也许一不小心就映射成了这个样子：&lt;/p>
&lt;p>&lt;img alt="Balance" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-balance-model.jpg">&lt;/p>
&lt;p>这样，就会导致大部分请求都会被映射到Node1上。因此，引出了虚拟节点。&lt;/p>
&lt;p>所谓虚拟节点，就是除了对服务本身地址进行Hash映射外，还通过在它地址上做些处理（比如Dubbo中，在ip+port的字符串后加上计数符1、2、3&amp;hellip;&amp;hellip;，分别代表虚拟节点1、2、3），以达到同一服务映射多个节点的目的。通过引入虚拟节点，我们可以把上图中映射给Node1的请求进一步拆分：&lt;/p>
&lt;p>&lt;img alt="Virtual Node" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/consistenthash/consistent-hash-virtual-node-model.jpg">&lt;/p>
&lt;p>如上图所示，若有请求落在Node3-Node1&amp;rsquo;区间，该请求应该是调用Node1&amp;rsquo;服务，但是因为Node1&amp;rsquo;是Node1的虚拟节点，所以实际调用的是Node1服务。通过引入虚拟节点，请求的分布就会比较平衡了。&lt;/p>
&lt;h3 id="二dubbo一致性hash的使用与负载均衡策略的引入阶段">&lt;strong>二、Dubbo一致性Hash的使用与负载均衡策略的引入阶段&lt;/strong>&lt;/h3>
&lt;h4 id="a如何使用一致性hash作为dubbo的负载均衡策略">&lt;strong>a、如何使用一致性Hash作为Dubbo的负载均衡策略？&lt;/strong>&lt;/h4>
&lt;p>dubbo:service、dubbo:reference、dubbo:provider、dubbo:consumer、dubbo:method这几个配置项都可以配置Dubbo的负载均衡策略，其中一致性Hash的属性值是：&lt;strong>consistenthash&lt;/strong>。&lt;/p>
&lt;p>以dubbo:reference为例：&lt;/p>
&lt;p>&lt;strong>XML配置：&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>&amp;lt;dubbo:reference loadbalance=&amp;ldquo;consistenthash&amp;rdquo; /&amp;gt;&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>Properties配置：&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>dubbo.reference.loadbalance=consistenthash&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>注解：&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>@Reference(loadbalance = &amp;ldquo;consistenthash&amp;rdquo;)&lt;/p>
&lt;/blockquote>
&lt;h4 id="bdubbo负载均衡策略的引入阶段">&lt;strong>b、Dubbo负载均衡策略的引入阶段&lt;/strong>&lt;/h4>
&lt;p>Dubbo实现的是客户端负载均衡。关于服务接口代理类的实现，这里不做详细描述，可以参考官网：&lt;/p>
&lt;blockquote>
&lt;p>服务引入：/zh-cn/docs/source_code_guide/refer-service.html&lt;/p>
&lt;/blockquote>
&lt;p>在接口代理类生成、并且装配好后，服务的调用基本是这样一个流程：proxy -&amp;gt; MockClusterInvoker -&amp;gt; 集群策略（如：FailoverClusterInvoker） -&amp;gt; 初始化负载均衡策略 -&amp;gt; 根据选定的负载均衡策略确定Invoker。&lt;/p>
&lt;p>&lt;strong>负载均衡策略的初始化&lt;/strong>是在AbstractClusterInvoker中的initLoadBalance方法中初始化的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> LoadBalance &lt;span style="color:#268bd2">initLoadBalance&lt;/span>(List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> invokers, Invocation invocation) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (CollectionUtils.isNotEmpty(invokers)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .getMethodParameter(RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这部分代码逻辑分为两部分：&lt;/p></description></item><item><title>Dubbo可扩展机制实战</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/04/25/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E5%AE%9E%E6%88%98/</link><pubDate>Thu, 25 Apr 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/04/25/dubbo%E5%8F%AF%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6%E5%AE%9E%E6%88%98/</guid><description>&lt;h2 id="1-dubbo的扩展机制">1. Dubbo的扩展机制&lt;/h2>
&lt;p>在Dubbo的官网上，Dubbo描述自己是一个高性能的RPC框架。今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性。
如同罗马不是一天建成的，任何系统都一定是从小系统不断发展成为大系统的，想要从一开始就把系统设计的足够完善是不可能的，相反的，我们应该关注当下的需求，然后再不断地对系统进行迭代。在代码层面，要求我们适当的对关注点进行抽象和隔离，在软件不断添加功能和特性时，依然能保持良好的结构和可维护性，同时允许第三方开发者对其功能进行扩展。在某些时候，软件设计者对扩展性的追求甚至超过了性能。&lt;/p>
&lt;p>在谈到软件设计时，可扩展性一直被谈起，那到底什么才是可扩展性，什么样的框架才算有良好的可扩展性呢？它必须要做到以下两点:&lt;/p>
&lt;ol>
&lt;li>作为框架的维护者，在添加一个新功能时，只需要添加一些新代码，而不用大量的修改现有的代码，即符合开闭原则。&lt;/li>
&lt;li>作为框架的使用者，在添加一个新功能时，不需要去修改框架的源码，在自己的工程中添加代码即可。&lt;/li>
&lt;/ol>
&lt;p>Dubbo很好的做到了上面两点。这要得益于Dubbo的微内核+插件的机制。接下来的章节中我们会慢慢揭开Dubbo扩展机制的神秘面纱。&lt;/p>
&lt;h2 id="2-可扩展的几种解决方案">2. 可扩展的几种解决方案&lt;/h2>
&lt;p>通常可扩展的实现有下面几种:&lt;/p>
&lt;ul>
&lt;li>Factory模式&lt;/li>
&lt;li>IoC容器&lt;/li>
&lt;li>OSGI容器&lt;/li>
&lt;/ul>
&lt;p>Dubbo作为一个框架，不希望强依赖其他的IoC容器，比如Spring，Guice。OSGI也是一个很重的实现，不适合Dubbo。最终Dubbo的实现参考了Java原生的SPI机制，但对其进行了一些扩展，以满足Dubbo的需求。&lt;/p>
&lt;h2 id="3-java-spi机制">3. Java SPI机制&lt;/h2>
&lt;p>既然Dubbo的扩展机制是基于Java原生的SPI机制，那么我们就先来了解下Java SPI吧。了解了Java的SPI，也就是对Dubbo的扩展机制有一个基本的了解。如果对Java SPI比较了解的同学，可以跳过。&lt;/p>
&lt;p>Java SPI(Service Provider Interface)是JDK内置的一种动态加载扩展点的实现。在ClassPath的&lt;code>META-INF/services&lt;/code>目录下放置一个与接口同名的文本文件，文件的内容为接口的实现类，多个实现类用换行符分隔。JDK中使用&lt;code>java.util.ServiceLoader&lt;/code>来加载具体的实现。
让我们通过一个简单的例子，来看看Java SPI是如何工作的。&lt;/p>
&lt;ol>
&lt;li>定义一个接口IRepository用于实现数据储存&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">IRepository&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">save&lt;/span>(String data);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>提供IRepository的实现
IRepository有两个实现。MysqlRepository和MongoRepository。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MysqlRepository&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> IRepository {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">save&lt;/span>(String data) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Save &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> data &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to Mysql&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MongoRepository&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> IRepository {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">save&lt;/span>(String data) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;Save &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> data &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to Mongo&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>添加配置文件
在&lt;code>META-INF/services&lt;/code>目录添加一个文件，文件名和接口全名称相同，所以文件是&lt;code>META-INF/services/com.demo.IRepository&lt;/code>。文件内容为:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>com.demo.MongoRepository
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>com.demo.MysqlRepository
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>通过ServiceLoader加载IRepository实现&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ServiceLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>IRepository&lt;span style="color:#719e07">&amp;gt;&lt;/span> serviceLoader &lt;span style="color:#719e07">=&lt;/span> ServiceLoader.load(IRepository.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Iterator&lt;span style="color:#719e07">&amp;lt;&lt;/span>IRepository&lt;span style="color:#719e07">&amp;gt;&lt;/span> it &lt;span style="color:#719e07">=&lt;/span> serviceLoader.iterator();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">while&lt;/span> (it &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> it.hasNext()){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> IRepository demoService &lt;span style="color:#719e07">=&lt;/span> it.next();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;class:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> demoService.getClass().getName());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> demoService.save(&lt;span style="color:#2aa198">&amp;#34;tom&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在上面的例子中，我们定义了一个扩展点和它的两个实现。在ClassPath中添加了扩展的配置文件，最后使用ServiceLoader来加载所有的扩展点。
最终的输出结果为：
class:testDubbo.MongoRepository
Save tom to Mongo
class:testDubbo.MysqlRepository
Save tom to Mysql&lt;/p></description></item><item><title>提前if判断帮助CPU分支预测</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/02/03/%E6%8F%90%E5%89%8Dif%E5%88%A4%E6%96%AD%E5%B8%AE%E5%8A%A9cpu%E5%88%86%E6%94%AF%E9%A2%84%E6%B5%8B/</link><pubDate>Sun, 03 Feb 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/02/03/%E6%8F%90%E5%89%8Dif%E5%88%A4%E6%96%AD%E5%B8%AE%E5%8A%A9cpu%E5%88%86%E6%94%AF%E9%A2%84%E6%B5%8B/</guid><description>&lt;h2 id="分支预测">分支预测&lt;/h2>
&lt;p>在stackoverflow上有一个非常有名的问题：&lt;a href="https://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array">为什么处理有序数组要比非有序数组快？&lt;/a>，可见分支预测对代码运行效率有非常大的影响。&lt;/p>
&lt;p>现代CPU都支持分支预测(branch prediction)和指令流水线(instruction pipeline)，这两个结合可以极大提高CPU效率。对于像简单的if跳转，CPU是可以比较好地做分支预测的。但是对于switch跳转，CPU则没有太多的办法。switch本质上是据索引，从地址数组里取地址再跳转。&lt;/p>
&lt;p>要提高代码执行效率，一个重要的原则就是尽量避免CPU把流水线清空，那么提高分支预测的成功率就非常重要。&lt;/p>
&lt;p>那么对于代码里，如果某个switch分支概率很高，是否可以考虑代码层面帮CPU把判断提前，来提高代码执行效率呢？&lt;/p>
&lt;h2 id="dubbo里channeleventrunnable的switch判断">Dubbo里ChannelEventRunnable的switch判断&lt;/h2>
&lt;p>在&lt;code>ChannelEventRunnable&lt;/code>里有一个switch来判断channel state，然后做对应的逻辑：&lt;a href="https://github.com/hengyunabc/dubbo/blob/dubbo-2.6.1/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispatcher/ChannelEventRunnable.java#L54">查看&lt;/a>&lt;/p>
&lt;p>一个channel建立起来之后，超过99.9%情况它的state都是&lt;code>ChannelState.RECEIVED&lt;/code>，那么可以考虑把这个判断提前。&lt;/p>
&lt;h2 id="benchmark验证">benchmark验证&lt;/h2>
&lt;p>下面通过jmh来验证下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">TestBenchMarks&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">enum&lt;/span> ChannelState {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		CONNECTED, DISCONNECTED, SENT, RECEIVED, CAUGHT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@State&lt;/span>(Scope.Benchmark)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ExecutionPlan&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">@Param&lt;/span>({ &lt;span style="color:#2aa198">&amp;#34;1000000&amp;#34;&lt;/span> })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> size;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">public&lt;/span> ChannelState&lt;span style="color:#719e07">[]&lt;/span> states &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">@Setup&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setUp&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			ChannelState&lt;span style="color:#719e07">[]&lt;/span> values &lt;span style="color:#719e07">=&lt;/span> ChannelState.values();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			states &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ChannelState&lt;span style="color:#719e07">[&lt;/span>size&lt;span style="color:#719e07">]&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			Random random &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Random(&lt;span style="color:#719e07">new&lt;/span> Date().getTime());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0; i &lt;span style="color:#719e07">&amp;lt;&lt;/span> size; i&lt;span style="color:#719e07">++&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#dc322f">int&lt;/span> nextInt &lt;span style="color:#719e07">=&lt;/span> random.nextInt(1000000);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">if&lt;/span> (nextInt &lt;span style="color:#719e07">&amp;gt;&lt;/span> 100) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					states&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> ChannelState.RECEIVED;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				} &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					states&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> values&lt;span style="color:#719e07">[&lt;/span>nextInt &lt;span style="color:#719e07">%&lt;/span> values.length&lt;span style="color:#719e07">]&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Fork&lt;/span>(value &lt;span style="color:#719e07">=&lt;/span> 5)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Benchmark&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@BenchmarkMode&lt;/span>(Mode.Throughput)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">benchSiwtch&lt;/span>(ExecutionPlan plan, Blackhole bh) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#dc322f">int&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> 0;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0; i &lt;span style="color:#719e07">&amp;lt;&lt;/span> plan.size; &lt;span style="color:#719e07">++&lt;/span>i) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">switch&lt;/span> (plan.states&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> CONNECTED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.CONNECTED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> DISCONNECTED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.DISCONNECTED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> SENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.SENT.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> RECEIVED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.RECEIVED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">case&lt;/span> CAUGHT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.CAUGHT.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		bh.consume(result);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Fork&lt;/span>(value &lt;span style="color:#719e07">=&lt;/span> 5)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Benchmark&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@BenchmarkMode&lt;/span>(Mode.Throughput)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">benchIfAndSwitch&lt;/span>(ExecutionPlan plan, Blackhole bh) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#dc322f">int&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> 0;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0; i &lt;span style="color:#719e07">&amp;lt;&lt;/span> plan.size; &lt;span style="color:#719e07">++&lt;/span>i) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			ChannelState state &lt;span style="color:#719e07">=&lt;/span> plan.states&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">if&lt;/span> (state &lt;span style="color:#719e07">==&lt;/span> ChannelState.RECEIVED) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.RECEIVED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			} &lt;span style="color:#719e07">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">switch&lt;/span> (state) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">case&lt;/span> CONNECTED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.CONNECTED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">case&lt;/span> SENT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.SENT.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">case&lt;/span> DISCONNECTED:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.DISCONNECTED.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">case&lt;/span> CAUGHT:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					result &lt;span style="color:#719e07">+=&lt;/span> ChannelState.CAUGHT.ordinal();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#719e07">break&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		bh.consume(result);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>benchSiwtch里是纯switch判断&lt;/li>
&lt;li>benchIfAndSwitch 里用一个if提前判断state是否&lt;code>ChannelState.RECEIVED&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>benchmark结果是：&lt;/p></description></item><item><title>当Dubbo遇上Arthas：排查问题的实践</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/02/02/%E5%BD%93dubbo%E9%81%87%E4%B8%8Aarthas%E6%8E%92%E6%9F%A5%E9%97%AE%E9%A2%98%E7%9A%84%E5%AE%9E%E8%B7%B5/</link><pubDate>Sat, 02 Feb 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/02/02/%E5%BD%93dubbo%E9%81%87%E4%B8%8Aarthas%E6%8E%92%E6%9F%A5%E9%97%AE%E9%A2%98%E7%9A%84%E5%AE%9E%E8%B7%B5/</guid><description>&lt;p>Apache Dubbo是Alibaba开源的高性能RPC框架，在国内有非常多的用户。&lt;/p>
&lt;ul>
&lt;li>Github: &lt;a href="https://github.com/apache/dubbo">https://github.com/apache/dubbo&lt;/a>&lt;/li>
&lt;li>文档：http://dubbo.apache.org/zh-cn/&lt;/li>
&lt;/ul>
&lt;p>Arthas是Alibaba开源的应用诊断利器，9月份开源以来，Github Star数三个月超过6000。&lt;/p>
&lt;ul>
&lt;li>Github: &lt;a href="https://github.com/alibaba/arthas">https://github.com/alibaba/arthas&lt;/a>&lt;/li>
&lt;li>文档：https://arthas.aliyun.com/doc/&lt;/li>
&lt;li>Arthas开源交流QQ群: 916328269&lt;/li>
&lt;li>Arthas开源交流钉钉群: 21965291&lt;/li>
&lt;/ul>
&lt;p>当Dubbo遇上Arthas，会碰撞出什么样的火花呢？下面来分享Arthas排查Dubbo问题的一些经验。&lt;/p>
&lt;h3 id="dubbo-arthas-demo">dubbo-arthas-demo&lt;/h3>
&lt;p>下面的排查分享基于这个&lt;code>dubbo-arthas-demo&lt;/code>，非常简单的一个应用，浏览器请求从Spring MVC到Dubbo Client，再发送到Dubbo Server。&lt;/p>
&lt;p>Demo里有两个spring boot应用，可以先启动&lt;code>server-demo&lt;/code>，再启动&lt;code>client-demo&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/hengyunabc/dubbo-arthas-demo">https://github.com/hengyunabc/dubbo-arthas-demo&lt;/a>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span> /user/{id} -&amp;gt; UserService -&amp;gt; UserServiceImpl 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Browser Dubbo Client Dubbo Server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Client端：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@RestController&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">UserController&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Reference&lt;/span>(version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">private&lt;/span> UserService userService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@GetMapping&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;/user/{id}&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> User &lt;span style="color:#268bd2">findUserById&lt;/span>(&lt;span style="color:#268bd2">@PathVariable&lt;/span> Integer id) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">return&lt;/span> userService.findUser(id);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Server端：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>(version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">UserServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> UserService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#268bd2">public&lt;/span> User &lt;span style="color:#268bd2">findUser&lt;/span>(&lt;span style="color:#dc322f">int&lt;/span> id) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">if&lt;/span> (id &lt;span style="color:#719e07">&amp;lt;&lt;/span> 1) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException(&lt;span style="color:#2aa198">&amp;#34;user id &amp;lt; 1, id: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> id);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">for&lt;/span> (User user : users) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#719e07">if&lt;/span> (user.getId() &lt;span style="color:#719e07">==&lt;/span> id) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#719e07">return&lt;/span> user;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException(&lt;span style="color:#2aa198">&amp;#34;Can not find user, id: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> id);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="arthas快速开始">Arthas快速开始&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://arthas.aliyun.com/doc/install-detail.html">https://arthas.aliyun.com/doc/install-detail.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ wget https://arthas.aliyun.com/arthas-boot.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ java -jar arthas-boot.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启动后，会列出所有的java进程，选择1，然后回车，就会连接上&lt;code>ServerDemoApplication&lt;/code>&lt;/p></description></item><item><title>如何使用Seata保证Dubbo微服务间的一致性</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/01/17/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8seata%E4%BF%9D%E8%AF%81dubbo%E5%BE%AE%E6%9C%8D%E5%8A%A1%E9%97%B4%E7%9A%84%E4%B8%80%E8%87%B4%E6%80%A7/</link><pubDate>Thu, 17 Jan 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/01/17/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8seata%E4%BF%9D%E8%AF%81dubbo%E5%BE%AE%E6%9C%8D%E5%8A%A1%E9%97%B4%E7%9A%84%E4%B8%80%E8%87%B4%E6%80%A7/</guid><description>&lt;h2 id="案例">案例&lt;/h2>
&lt;p>用户采购商品业务，整个业务包含3个微服务:&lt;/p>
&lt;ul>
&lt;li>库存服务: 扣减给定商品的库存数量。&lt;/li>
&lt;li>订单服务: 根据采购请求生成订单。&lt;/li>
&lt;li>账户服务: 用户账户金额扣减。&lt;/li>
&lt;/ul>
&lt;h3 id="业务结构图">业务结构图&lt;/h3>
&lt;p>&lt;img alt="Architecture" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/fescar/fescar-1.png">&lt;/p>
&lt;h3 id="storageservice">StorageService&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">StorageService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 扣除存储数量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">deduct&lt;/span>(String commodityCode, &lt;span style="color:#dc322f">int&lt;/span> count);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="orderservice">OrderService&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">OrderService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 创建订单
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Order &lt;span style="color:#268bd2">create&lt;/span>(String userId, String commodityCode, &lt;span style="color:#dc322f">int&lt;/span> orderCount);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="accountservice">AccountService&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">AccountService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 从用户账户中借出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">debit&lt;/span>(String userId, &lt;span style="color:#dc322f">int&lt;/span> money);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="主要的业务逻辑">主要的业务逻辑：&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">BusinessServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> BusinessService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> StorageService storageService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> OrderService orderService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 采购
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">purchase&lt;/span>(String userId, String commodityCode, &lt;span style="color:#dc322f">int&lt;/span> orderCount) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storageService.deduct(commodityCode, orderCount);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> orderService.create(userId, commodityCode, orderCount);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">StorageServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> StorageService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> StorageDAO storageDAO;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">deduct&lt;/span>(String commodityCode, &lt;span style="color:#dc322f">int&lt;/span> count) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Storage storage &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Storage();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storage.setCount(count);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storage.setCommodityCode(commodityCode);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> storageDAO.update(storage);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">OrderServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> OrderService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> OrderDAO orderDAO;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> AccountService accountService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Order &lt;span style="color:#268bd2">create&lt;/span>(String userId, String commodityCode, &lt;span style="color:#dc322f">int&lt;/span> orderCount) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> orderMoney &lt;span style="color:#719e07">=&lt;/span> calculate(commodityCode, orderCount);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> accountService.debit(userId, orderMoney);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Order order &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Order();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> order.userId &lt;span style="color:#719e07">=&lt;/span> userId;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> order.commodityCode &lt;span style="color:#719e07">=&lt;/span> commodityCode;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> order.count &lt;span style="color:#719e07">=&lt;/span> orderCount;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> order.money &lt;span style="color:#719e07">=&lt;/span> orderMoney;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> orderDAO.insert(order);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="seata-分布式事务解决方案">Seata 分布式事务解决方案&lt;/h2>
&lt;p>&lt;img alt="undefined" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/fescar/fescar-2.png">&lt;/p></description></item><item><title>浅谈 RPC</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/01/07/%E6%B5%85%E8%B0%88-rpc/</link><pubDate>Mon, 07 Jan 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/01/07/%E6%B5%85%E8%B0%88-rpc/</guid><description>&lt;p>近几年随着微服务化项目的崛起，逐渐成为许多公司中大型分布式系统架构的主流方式，而今天所说的 RPC 在这其中扮演着至关重要的角色。随着这段日子公司项目微服务化的演进，发现在日常开发中都在隐式或显式的使用 RPC，一些刚刚接触 RPC 的小伙伴会感觉无所适从，而一些入行多年的老手虽然使用 RPC 经验丰富，但有些对其原理也一知半解，缺乏对原理的深入理解，往往也会造成开发中的一些误用。&lt;/p>
&lt;h2 id="什么是rpc">什么是RPC？&lt;/h2>
&lt;p>RPC（Remote Procedure Call）—远程过程调用，它是一种通过网络从远程计算机程序上请求服务，而不需要了解底层网络技术的协议。也就是说两台服务器A，B，一个应用部署在A服务器上，想要调用B服务器上应用提供的方法，由于不在一个内存空间，不能直接调用，需要通过网络来表达调用的语义和传达调用的数据。&lt;/p>
&lt;p>RPC协议假定某些传输协议的存在，如TCP或UDP，为通信程序之间携带信息数据。在OSI网络通信模型中，RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。现在业界有很多开源的优秀 RPC 框架，例如 Spring Cloud、Dubbo、Thrift 等。&lt;/p>
&lt;h2 id="rpc-起源">RPC 起源&lt;/h2>
&lt;p>RPC 这个概念术语在上世纪 80 年代由 &lt;strong>Bruce Jay Nelson&lt;/strong> 提出。这里我们追溯下当初开发 RPC 的原动机是什么？在 Nelson 的论文 &amp;ldquo;Implementing Remote Procedure Calls&amp;rdquo; 中他提到了几点：&lt;/p>
&lt;ul>
&lt;li>简单：RPC 概念的语义十分清晰和简单，这样建立分布式计算就更容易。&lt;/li>
&lt;li>高效：过程调用看起来十分简单而且高效。&lt;/li>
&lt;li>通用：在单机计算中过程往往是不同算法部分间最重要的通信机制。&lt;/li>
&lt;/ul>
&lt;p>通俗一点说，就是一般程序员对于本地的过程调用很熟悉，那么我们把 RPC 作成和本地调用完全类似，那么就更容易被接受，使用起来毫无障碍。Nelson 的论文发表于 30 年前，其观点今天看来确实高瞻远瞩，今天我们使用的 RPC 框架基本就是按这个目标来实现的。&lt;/p>
&lt;h2 id="rpc-结构">RPC 结构&lt;/h2>
&lt;p>Nelson 的论文中指出实现 RPC 的程序包括 5 个部分：&lt;/p>
&lt;ol>
&lt;li>User&lt;/li>
&lt;li>User-stub&lt;/li>
&lt;li>RPCRuntime&lt;/li>
&lt;li>Server-stub&lt;/li>
&lt;li>Server&lt;/li>
&lt;/ol>
&lt;p>&lt;img alt="RPC结构" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/rpc/rpc-structure-1.png">&lt;/p>
&lt;p>这里 user 就是 client 端，当 user 想发起一个远程调用时，它实际是通过本地调用 user-stub。user-stub 负责将调用的接口、方法和参数通过约定的协议规范进行编码并通过本地的 RPCRuntime 实例传输到远端的实例。远端 RPCRuntime 实例收到请求后交给 server-stub 进行解码后发起本地端调用，调用结果再返回给 user 端。&lt;/p></description></item><item><title>新版 Dubbo Admin 介绍</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/01/07/%E6%96%B0%E7%89%88-dubbo-admin-%E4%BB%8B%E7%BB%8D/</link><pubDate>Mon, 07 Jan 2019 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2019/01/07/%E6%96%B0%E7%89%88-dubbo-admin-%E4%BB%8B%E7%BB%8D/</guid><description>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>github: https://github.com/apache/dubbo-ops
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo Admin之前的版本过于老旧，也长期疏于维护，因此在去年年中的时候，对该项目进行了一次重构，项目结构上的变化如下：&lt;/p>
&lt;ul>
&lt;li>将后端框架从webx替换成spring boot&lt;/li>
&lt;li>前端采用Vue和Vuetify.js作为开发框架&lt;/li>
&lt;li>移除velocity模板&lt;/li>
&lt;li>集成swagger，提供api管理功能&lt;/li>
&lt;/ul>
&lt;p>当前版本的Dubbo Admin包含了之前版本中的绝大部分功能，包括服务治理，服务查询等，同时支持了Dubbo2.7中服务治理的新特性。&lt;/p>
&lt;h2 id="配置规范">配置规范&lt;/h2>
&lt;p>由于在Dubbo2.7中，配置中心和注册中心做了分离，并且增加了元数据中心，因此Dubbo Admin的配置方式也做了更新，&lt;code>application.properties&lt;/code>中的配置如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>admin.registry.address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>admin.config-center&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>admin.metadata-report.address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>也可以和Dubbo2.7一样，在配置中心指定元数据和注册中心的地址，以zookeeper为例，配置的路径和内容如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># /dubbo/config/dubbo/dubbo.properties&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.metadata-report.address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">zookeeper://127.0.0.1:2181&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置中心里的地址会覆盖掉本地&lt;code>application.properties&lt;/code>的配置&lt;/p>
&lt;h2 id="功能介绍">功能介绍&lt;/h2>
&lt;p>功能上，主要延续了之前版本的功能，包括服务查询和服务治理，2.7版本在服务治理的功能上有了很大的改进，这些改进也大部分都会以Dubbo Admin作为入口来体现。&lt;/p>
&lt;h3 id="标签路由">标签路由&lt;/h3>
&lt;p>标签路由是Dubbo2.7引入的新功能，配置以应用作为维度，给不同的服务器打上不同名字的标签，配置如下图所示：&lt;/p>
&lt;p>&lt;img alt="tag" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/route.jpg">&lt;/p>
&lt;p>调用的时候，客户端可以通过&lt;code>setAttachment&lt;/code>的方式，来设置不同的标签名称，比如本例中，&lt;code>setAttachment(tag1)&lt;/code>，客户端的选址范围就在如图所示的三台机器中，可以通过这种方式来实现流量隔离，灰度发布等功能。&lt;/p>
&lt;h3 id="应用级别的服务治理">应用级别的服务治理&lt;/h3>
&lt;p>在Dubbo2.6及更早版本中，所有的服务治理规则都只针对服务粒度，如果要把某条规则作用到应用粒度上，需要为应用下的所有服务配合相同的规则，变更，删除的时候也需要对应的操作，这样的操作很不友好，因此Dubbo2.7版本中增加了应用粒度的服务治理操作，对于条件路由(包括黑白名单)，动态配置(包括权重，负载均衡)都可以做应用级别的配置：&lt;/p>
&lt;p>&lt;img alt="condition" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/conditionRoute.jpg">&lt;/p>
&lt;p>上图是条件路由的配置，可以按照应用名，服务名两个维度来填写，也可以按照这两个维度来查询。&lt;/p>
&lt;p>&lt;img alt="weight" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/weight.jpg">&lt;/p>
&lt;p>条件路由，标签路由和动态配置都采用了&lt;code>yaml&lt;/code>格式的文本编写，其他的规则配置还是采用了表单的形式。&lt;/p>
&lt;h4 id="关于兼容性">关于兼容性&lt;/h4>
&lt;p>Dubbo2.6到Dubbo2.7，服务治理发生了比较大的变化，Dubbo Admin兼容两个版本的用法：&lt;/p>
&lt;ul>
&lt;li>对于服务级别的配置，会按照Dubbo2.6(URL)和Dubbo2.7(配置文件)两种格式进行写入，保证Dubbo2.6的客户端能够正确读取，解析规则&lt;/li>
&lt;li>对于应用级别的配置，包括标签路由，只会按照Dubbo2.7的格式进行写入，因为Dubbo2.6无此功能，不需要做向前兼容。&lt;/li>
&lt;li>Dubbo Admin只会按照Dubbo2.7的格式进行配置读取，因此，所有在Dubbo Admin上做的配置都可以被读到，但是之前遗留的，Dubbo2.6格式的URL无法被读取。&lt;/li>
&lt;li>对于同一个应用或者服务，每种规则只能够配置一条，否则新的会覆盖旧的。&lt;/li>
&lt;/ul>
&lt;h3 id="配置管理">配置管理&lt;/h3>
&lt;p>配置管理也是配合Dubbo2.7新增的功能，在Dubbo2.7中，增加了全局和应用维度的配置，&lt;/p>
&lt;ul>
&lt;li>全局配置：&lt;/li>
&lt;/ul>
&lt;p>&lt;img alt="config" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/config.jpg">&lt;/p>
&lt;p>全局配置里可以指定注册中心，元数据中心的地址，服务端和客户端的超时时间等，这些配置在全局内生效。除了配置写入，也可以用来查看。如果使用zookeeper作为注册中心和元数据中心，还可以看到配置文件所在位置的目录结构。&lt;/p>
&lt;ul>
&lt;li>应用， 服务配置&lt;/li>
&lt;/ul>
&lt;p>&lt;img alt="appConfig" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/appConfig.jpg">&lt;/p>
&lt;p>应用级别的配置可以为应用或者应用内的服务指定配置，在服务维度上，需要区分提供者和消费者。&lt;code>dubbo.reference.{serviceName}&lt;/code>表示作为该服务消费者的配置，&lt;code>dubbo.provider.{servcieName}&lt;/code>表示作为该服务提供者的配置。优先级服务 &amp;gt; 应用 &amp;gt; 全局。其中注册中心和元数据中心的地址，只能在全局配置中指定，这也是Dubbo2.7中推荐的使用方式。&lt;/p>
&lt;h3 id="元数据和服务测试">元数据和服务测试&lt;/h3>
&lt;p>元数据是Dubbo2.7中新引入的元素，主要的使用场景就在Dubbo Admin中，主要体现在两个地方：&lt;/p>
&lt;ul>
&lt;li>服务详情展示：&lt;/li>
&lt;/ul>
&lt;p>&lt;img alt="metadata" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/metadata.jpg">&lt;/p>
&lt;p>跟之前版本相比，Dubbo2.7中增加了对服务方法完整签名的记录，因此服务详情中也增加了方法信息的详情，可以看到方法名，方法参数列表以及返回值信息。&lt;/p>
&lt;ul>
&lt;li>服务测试：&lt;/li>
&lt;/ul>
&lt;p>&lt;img alt="test" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/admin/test.jpg">&lt;/p>
&lt;p>更重要的，元数据为服务测试提供了数据基础，可以在页面上调用真实的服务提供者，方便测试，也不需要为了调用服务去搭建一套Dubbo环境以及编写消费端代码。&lt;/p></description></item><item><title>Dubbo 融合 Nacos 成为注册中心</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/11/07/dubbo-%E8%9E%8D%E5%90%88-nacos-%E6%88%90%E4%B8%BA%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</link><pubDate>Wed, 07 Nov 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/11/07/dubbo-%E8%9E%8D%E5%90%88-nacos-%E6%88%90%E4%B8%BA%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</guid><description>&lt;p>Nacos 作为 Dubbo 生态系统中重要的注册中心实现，其中 &lt;a href="https://github.com/apache/dubbo/tree/master/dubbo-registry/dubbo-registry-nacos">&lt;code>dubbo-registry-nacos&lt;/code>&lt;/a> 则是 Dubbo 融合 Nacos 注册中心的实现。&lt;/p>
&lt;h2 id="预备工作">预备工作&lt;/h2>
&lt;p>当您将 &lt;a href="https://github.com/apache/dubbo/tree/master/dubbo-registry/dubbo-registry-nacos">&lt;code>dubbo-registry-nacos&lt;/code>&lt;/a> 整合到您的 Dubbo 工程之前，请确保后台已经启动 Nacos 服务。如果您尚且不熟悉 Nacos 的基本使用的话，可先行参考 &lt;a href="https://nacos.io/en-us/docs/quick-start.html">Nacos 快速入门&lt;/a>：https://nacos.io/en-us/docs/quick-start.html。建议使用 Nacos &lt;code>0.6.1&lt;/code> 以上的版本。&lt;/p>
&lt;h2 id="快速上手">快速上手&lt;/h2>
&lt;p>Dubbo 融合 Nacos 成为注册中心的操作步骤非常简单，大致步骤可分为“增加 Maven 依赖”以及“配置注册中心“。&lt;/p>
&lt;h3 id="增加-maven-依赖">增加 Maven 依赖&lt;/h3>
&lt;p>首先，您需要 &lt;code>dubbo-registry-nacos&lt;/code> 的 Maven 依赖添加到您的项目中 &lt;code>pom.xml&lt;/code> 文件中，并且强烈地推荐您使用 Dubbo &lt;code>2.6.5&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- Dubbo Nacos registry dependency --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo-registry-nacos&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>0.0.2&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- Keep latest Nacos client version --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba.nacos&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>nacos-client&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>[0.6.1,)&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- Dubbo dependency --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>2.6.5&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- Alibaba Spring Context extension --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba.spring&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-context-support&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>1.0.2&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当项目中添加 &lt;code>dubbo-registry-nacos&lt;/code> 后，您无需显示地编程实现服务发现和注册逻辑，实际实现由该三方包提供，接下来配置 Naocs 注册中心。&lt;/p></description></item><item><title>Dubbo服务分组和版本聚合</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/10/27/dubbo%E6%9C%8D%E5%8A%A1%E5%88%86%E7%BB%84%E5%92%8C%E7%89%88%E6%9C%AC%E8%81%9A%E5%90%88/</link><pubDate>Sat, 27 Oct 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/10/27/dubbo%E6%9C%8D%E5%8A%A1%E5%88%86%E7%BB%84%E5%92%8C%E7%89%88%E6%9C%AC%E8%81%9A%E5%90%88/</guid><description>&lt;p>我们在调用Dubbo服务的时候，一般只需要将Consumer端的&lt;code>dubbo:reference&lt;/code>指定成服务端中&lt;code>dubbo:service&lt;/code>暴露的服务，就可以找到服务端，完成调用，也就是说，Dubbo只需要服务接口信息就可以找到服务提供者。
其实除了服务提供者以外，Dubbo也有服务分组和版本的概念，在客户端去寻找“匹配”的服务端的时候，需要服务接口，版本号，组别这三个信息都匹配，才算是一个有效的服务端：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isMatch&lt;/span>(URL consumerUrl, URL providerUrl) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerInterface &lt;span style="color:#719e07">=&lt;/span> consumerUrl.getServiceInterface();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerInterface &lt;span style="color:#719e07">=&lt;/span> providerUrl.getServiceInterface();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>(Constants.ANY_VALUE.equals(consumerInterface) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isEquals(consumerInterface, providerInterface)))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>isMatchCategory(providerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY))) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>providerUrl.getParameter(Constants.ENABLED_KEY, &lt;span style="color:#cb4b16">true&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>Constants.ANY_VALUE.equals(consumerUrl.getParameter(Constants.ENABLED_KEY))) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerGroup &lt;span style="color:#719e07">=&lt;/span> consumerUrl.getParameter(Constants.GROUP_KEY);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerVersion &lt;span style="color:#719e07">=&lt;/span> consumerUrl.getParameter(Constants.VERSION_KEY);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerClassifier &lt;span style="color:#719e07">=&lt;/span> consumerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerGroup &lt;span style="color:#719e07">=&lt;/span> providerUrl.getParameter(Constants.GROUP_KEY);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerVersion &lt;span style="color:#719e07">=&lt;/span> providerUrl.getParameter(Constants.VERSION_KEY);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerClassifier &lt;span style="color:#719e07">=&lt;/span> providerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> (Constants.ANY_VALUE.equals(consumerGroup) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isEquals(consumerGroup, providerGroup) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isContains(consumerGroup, providerGroup))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> (Constants.ANY_VALUE.equals(consumerVersion) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isEquals(consumerVersion, providerVersion))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> (consumerClassifier &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> Constants.ANY_VALUE.equals(consumerClassifier) &lt;span style="color:#719e07">||&lt;/span> StringUtils.isEquals(consumerClassifier, providerClassifier));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果没有配置组别和版本号，默认值为空。服务端和消费端都没有配，只有服务接口，其他两个信息都为空，也是可以“找到”对方的，那服务名和版本号可以如何使用呢？下面我们来看一下具体的场景：&lt;/p></description></item><item><title>Dubbo 协议详解</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/10/05/dubbo-%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3/</link><pubDate>Fri, 05 Oct 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/10/05/dubbo-%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h2 id="协议的概念">协议的概念&lt;/h2>
&lt;p>协议是两个网络实体进行通信的基础，数据在网络上从一个实体传输到另一个实体，以字节流的形式传递到对端。在这个字节流的世界里，如果没有协议，就无法将这个一维的字节流重塑成为二维或者多维的数据结构以及领域对象。&lt;/p>
&lt;h3 id="协议是什么">协议是什么&lt;/h3>
&lt;p>协议是双方确定的交流语义，比如：我们设计一个字符串传输的协议，它允许客户端发送一个字符串，服务端接收到对应的字符串。这个协议很简单，首先发送一个4字节的消息总长度，然后再发送1字节的字符集charset长度，接下来就是消息的payload，字符集名称和字符串正文。&lt;/p>
&lt;p>发送一个&lt;code>iso-8859-1&lt;/code>的字符串&lt;code>abc&lt;/code>到对端。经过协议编码，内容是：&lt;code>18 = 4 + 1 + 10 + 3|10|iso-8859-1|abc&lt;/code>，当这些字节流发往服务端后，当服务端收到字节流后，首先读取4个字节，将其转换为int，在这个例子中是18，接下来继续读14个字节，将首个字节得到字符集名称长度10，将后续内容的前10字节转换为字符串，内容是&lt;code>iso-8859-1&lt;/code>，使用该字符集将后续的字节数组造型成为字符串&lt;code>new String(bytes, &amp;quot;iso-8859-1&amp;quot;)&lt;/code>。&lt;/p>
&lt;p>在前面自定义字符串传输协议的例子中，我们已经看到协议在双方传输数据中起到的作用，没有协议就无法完成数据交换，下面是维基百科对于通信协议的定义。&lt;/p>
&lt;blockquote>
&lt;p>In telecommunication, a communication protocol is a system of rules that allow two or more entities of a communications system to transmit information via any kind of variation of a physical quantity. The protocol defines the rules syntax, semantics and synchronization of communication and possible error recovery methods. Protocols may be implemented by hardware, software, or a combination of both.&lt;/p></description></item><item><title>Dubbo与Kubernetes集成</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/09/30/dubbo%E4%B8%8Ekubernetes%E9%9B%86%E6%88%90/</link><pubDate>Sun, 30 Sep 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/09/30/dubbo%E4%B8%8Ekubernetes%E9%9B%86%E6%88%90/</guid><description>&lt;h2 id="大体目标">大体目标&lt;/h2>
&lt;p>Dubbo的provider不再关心服务注册的事宜，只需要把其Dubbo服务端口打开，由Kubernetes来进行服务的声明和发布；Dubbo的consumer在服务发现时直接发现kubernetes的对应服务endpoints，从而复用Dubbo已有的微服务通道能力。好处是无需依赖三方的软负载注册中心；同时无缝融入Kubernetes的多租户安全体系。Demo的代码参照： &lt;a href="https://github.com/dubbo/dubbo-kubernetes">https://github.com/dubbo/dubbo-kubernetes&lt;/a>&lt;/p>
&lt;h2 id="闲谈">闲谈&lt;/h2>
&lt;p>Kubernates是建立在扩展性的具备二次开发的功能层次丰富的体系化系统&lt;/p>
&lt;ul>
&lt;li>首先其最核心的功能是管理容器集群，能管理容器化的集群（包括存储，计算），当然这个是建立在对容器运行时(CRI)，网络接口(CNI),存储服务接口（CSI/FV）的基础上；&lt;/li>
&lt;li>其次是面向应用(包括无状态/有状态,批处理/服务型应用)的部署和路由能力，特别是基于微服务架构的应用管理，具备了其服务定义和服务发现，以及基于configmap的统一配置能力；&lt;/li>
&lt;li>在基础资源（主要是抽象底层IaaS的资源）和应用层的抽象模型之上是治理层，包含弹性扩容，命名空间/租户，等。当然，基于其原子内核的基础能力，在Kubernetes的核心之上搭建统一的日志中心和全方位监控等服务是水到渠成的，CNCF更是有其认定推荐。&lt;/li>
&lt;/ul>
&lt;p>来张Kubernetes Architecture的一张图解释下上述描述。在2018年Kubernetes往事实的paas底座的标配迈出质的一步，有人说原因在于基于扩展的二次开发能力，有人说在于其声明式编程和背靠Google和Redhat的强大社区运作，我觉得回归本质是在于下图中的&lt;strong>Layered架构和其问题域的领域建模抽象&lt;/strong>。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/k8s/1.png">&lt;/p>
&lt;p>以微服务架构视角，Kubernetes在一定意义上是微服务框架（这时较叫微服务平台或toolkit集更合适），支持微服务的服务发现/注册的基本能力。借用如下图做一个简单描述。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/k8s/2.jpeg">&lt;/p>
&lt;p>话题再展开一下，微服务领域涉及众多问题，大概可以用下图说明。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/k8s/3.jpeg">&lt;/p>
&lt;p>Kubernetes解决得只是少部分，而像动态路由，稳定性控制（断路器，隔水舱等），分布式服务追踪等是个空白，这也就是servicemesh要解决的，是在CNCF的Trail Map占有重要一席；当然Dubbo是基本具备完备的微服务，也就是使得其集成到k8s体系下具有相当的意义。Dubbo在serviemesh中基于sidecar的方案是解决跨语言诉求的通用servicemesh方案，需要新开一个话题来展开说；而引用serviemsh的原始定义：&lt;/p>
&lt;blockquote>
&lt;p>A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>首先服务网格是一个云原生环境下基础设施层，功能在于处理服务间通信，职责是负责实现请求的可靠传递，被使得被监控跟踪，被治理，最终使得微服务架构被赋予高可控的稳定性和快速的问题定位排查能力。&lt;/p>
&lt;/blockquote>
&lt;p>可以得出现有Dubbo集成云原生基础设施Kubernetes的基础能力而并解决微服务相关核心问题也算是一种狭义上的servicemesh方案，只是是Java领域的罢了；当玩笑理解也行，哈哈。&lt;/p>
&lt;h2 id="思路方案">思路/方案&lt;/h2>
&lt;p>Kubernetes是天然可作为微服务的地址注册中心，类似于Zookeeper， 阿里巴巴内部用到的VIPserver，Configserver。 具体来说，Kubernetes中的Pod是对于应用的运行实例，Pod的被调度部署/启停都会调用API-Server的服务来保持其状态到ETCD；Kubernetes中的service是对应微服务的概念，定义如下&lt;/p>
&lt;blockquote>
&lt;p>A Kubernetes Service is an abstraction layer which defines a logical set of Pods and enables external traffic exposure, load balancing and service discovery for those Pods.&lt;/p></description></item><item><title>Dubbo Mesh ｜ Service Mesh的实践与探索</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/09/20/dubbo-mesh-service-mesh%E7%9A%84%E5%AE%9E%E8%B7%B5%E4%B8%8E%E6%8E%A2%E7%B4%A2/</link><pubDate>Thu, 20 Sep 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/09/20/dubbo-mesh-service-mesh%E7%9A%84%E5%AE%9E%E8%B7%B5%E4%B8%8E%E6%8E%A2%E7%B4%A2/</guid><description>&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/meetup-chengdu/all-hands.webp">&lt;/p>
&lt;p>近日，在Aliware Open Source•成都站-Apache Dubbo 开发者沙龙上，阿里巴巴中间件高级技术专家李云（至简）向开发者们分享了阿里巴巴中间件团队在Service Mmesh领域的探索和最新实践。本文是根据至简的现场分享所整理，为大家回顾分享中的精彩内容。&lt;/p>
&lt;h2 id="精彩观点导读">精彩观点导读&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>我们去探索一项技术，并不会仅仅因为其先进性，而是因为我们目前遇到了一些无法解决的问题，而这项技术正好能解决这个问题。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>所有软件最重要的使命不是满足功能要求，而是演进，从而持续成长。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>微服务本质是对服务的拆分，微服务架构符合工程领域常用的“分而治之”范式。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="前言">前言&lt;/h2>
&lt;p>我们去探索一项技术，并不会仅仅因为其先进性，而是因为我们目前遇到了一些无法解决的问题，而这项技术正好能解决这个问题。现在，阿里巴巴整个集团业务的体量很大，在技术上会遇到很多的挑战。而正是因为这些挑战，让我们思考通过哪些新技术可以去解决这些痛点，这也是我们在Service Mesh领域进行探索和实践的出发点。首先，我们先来看看自己遇到了哪些挑战。&lt;/p>
&lt;h2 id="微服务的5大挑战">微服务的5大挑战&lt;/h2>
&lt;h4 id="挑战一微服务框架自身演进困难">挑战一：微服务框架自身演进困难。&lt;/h4>
&lt;p>任何软件都会有他的生命进化曲线，从最初的萌芽，进入形成期，往上发展，再进入平台期，最后进入衰亡期。当然我们希望我们的软件可以在进入平台期后，能借助某次演进进入新的发展期。从这个维度看，所有软件最重要的使命不是满足功能要求，而是演进，从而持续成长。相反，当某个软件无法演进的时候，就会意味着死亡。但软件的演进并不是一个简单的事情，以微服务框架为例，为了进一步提升双11期间整个中间件平台的稳定性，我们会修改若干个功能，并以SDK的方式去提供给业务方，但业务代码和微服务框架SDK是强耦合的，这时候需要我们推动各个业务方和我们一同去做升级。虽然我们的初衷是实现平台稳定性的提升，帮助业务更好的发展，但这时由于大家的出发点和诉求有所不同，业务方和我们一起去做升级是比较困难的。所以要发展微服务框架，首先遇到的挑战就是演进困难。
&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/meetup-chengdu/challenges.jpg">&lt;/p>
&lt;h4 id="挑战二微服务框架sdk多语言并行开发与维护成本高">挑战二：微服务框架SDK多语言并行开发与维护成本高。&lt;/h4>
&lt;p>以前我们都是通过对技术栈的统一来提升成本优势和团队效率，大家可以用一种语言去开发和维护，避免多语言时团队的不聚焦。但在软件和开源生态演进的过程中，多语言已经成为一种流行，因为不同语言都有其自身的优势，今天大家能看到的一个现象是云原生的生态中有多种开发语言，使用频率最高的语言已经不是Java了，而是Go，是因为Go的footprint很小。再以 Dubbo为例，除了Java，我们还提供C++，Node.js的SDK，以便让更多的开发者可以加入Dubbo生态，但所有的这些，如果没有社区力量的参与，是很难维持的。
&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/meetup-chengdu/speaker.webp">&lt;/p>
&lt;h4 id="挑战三异构服务框架难以共存完成渐进式演进">挑战三：异构服务框架难以共存完成渐进式演进。&lt;/h4>
&lt;p>我们结合场景来看看这个挑战。阿里巴巴收购了一些企业，被收购企业的技术栈可能和阿里巴巴不同，比如有些用的是Go语言，有些用的是PHP，这时候为了统一技术栈，我们需要对这类技术平台推倒重来，但这个过程中，我们会面临一系列问题，首当其冲的就是推倒重来会带来巨大的技术风险，其次是可能会面临技术人员大批量流失的风险，这在社会责任的层面也是很难接受。所以我们在寻求一种可能的方案，去解决这类问题。&lt;/p>
&lt;h4 id="挑战四是单一的语言限制了人才的多样性">挑战四：是单一的语言限制了人才的多样性。&lt;/h4>
&lt;p>这里，我们不去争论某个编程语言的好与坏，每个语言都有其适用场景，你不能说我手里有个榔头，你面对的都是钉子。以前我们觉得统一技术栈可以集中开发力量，并且带来较高的运维便利性。但伴随着互联网带来的快节奏，以往的团队能力设置已经很难满足这类变化，对工程师个体提出了更高的要求，我们不仅仅需要是某一方面的专家，而且还需要具备多域的工作技能，DevOps和全栈工程师就是这类快节奏变化下最好的注脚。
&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/meetup-chengdu/challenges-continued.jpg">&lt;/p>
&lt;h4 id="挑战五是点状的服务治理难以做到及时有效和经济">挑战五：是点状的服务治理难以做到及时、有效和经济。&lt;/h4>
&lt;p>微服务和架构的核心是拆分，通过拆分，让每个模块可以独立运行，跟上业务的发展速度，持续推动业务的创新。但拆完后新的问题出来了，缺少横向的内容拉通所有独立的烟囱，从而在服务治理上带来极大的挑战。&lt;/p>
&lt;h2 id="分布式应用的发展趋势">分布式应用的发展趋势&lt;/h2>
&lt;p>微服务会成为大规模分布式应用的主流架构。任何复杂的工程问题都会归结为devide and conquer（分而治之），意思就是就是把一个复杂的问题分成两个或更多的相同或相似的子问题，再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解，原问题的解即子问题的解的合并。微服务本质是对服务的拆分，与工程领域惯用的“分而治之”的思路是一致的。&lt;/p>
&lt;p>微服务架构下应用的开发是多语言的。没有一个语言是一家独大的，每种语言在特定场景下都有其自身的优势，我们希望这种优势能够将技术到产品的周期（time to market）缩短。技术的核心在于创造价值，无论是交付给客户，还是服务于整个社会。因此，微服务是需要不同语言的开发者发挥自身的优势，去进一步完善我们的微服务架构，释放技术价值。
&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/meetup-chengdu/trends.jpg">&lt;/p>
&lt;p>数据安全将成为公有云分布式应用的生命线。云原生时代，业务即便没上云，企业对自身数据的安全都是有诉求的，尤其是在金融行业，如果通过抓包就能获取一些敏感信息，这将会给企业带来巨大的风险。&lt;/p>
&lt;p>Cloud native成为distributionless（无分布式）的主要探索路径。分布式发展的终极形式是无分布式，在未来我们做开发，所有的代码在web上写好后，通过点击一个按钮，所有部署都会自动实现，所有的code review的工作可以在一个统一的工作台上全部实现。
&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/meetup-chengdu/audience-shapshot.webp">&lt;/p>
&lt;p>以更快的速度，通过构建软件去探索新业务。工程师服务的是客户，通过技术输出来实现技术价值，以互联网的架构帮助赋能传统企业，帮助企业获得差异化竞争力。&lt;/p>
&lt;h2 id="什么是service-mesh">什么是Service Mesh&lt;/h2>
&lt;p>Service Mesh是层次化、规范化、体系化、无侵入的分布式服务治理技术平台。&lt;/p>
&lt;h4 id="层次化">层次化&lt;/h4>
&lt;p>分为数据面和控制面两个概念，数据面是指所有数据流动的那个层面，控制面是用来控制这个数据面的，对服务去做处理。对数据面和控制面进行分层，带来的好处是，针对一个复杂的系统进行切分，可以获得更清晰的认识，这和devide and conque是同一个理念。&lt;/p>
&lt;h4 id="规范化">规范化&lt;/h4>
&lt;p>是指通过标准协议完成数据平面和控制平面的连接，同时，sidecar成为所有traffic互联、互通的约束标准。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/meetup-chengdu/what.jpg">&lt;/p>
&lt;h4 id="体系化">体系化&lt;/h4>
&lt;p>包含两个维度，一是指observability全局考虑。目前在整个分布式治理过程中的最大挑战是：logging、metrics、tracing这三个observability领域的核心内容缺少体系性的关注。另一个是集中管理的维度，包括服务管理、限流、熔断、安全、灰度在内的服务模块都可以在获得体系化的呈现，每个服务都可以被看到，而非团队a只看限流，团队b只看logging，需要一种技术能力拉通所有的服务模块，这个体系化这个角度看，Service Mesh是一个理想的技术方案。&lt;/p>
&lt;h4 id="无侵入">无侵入&lt;/h4>
&lt;p>是指我们希望通过无侵入，当新增一个业务的时候，不需要考虑一个SDK去初始化，而是可以通过sidecar的进程方式来解耦。&lt;/p>
&lt;h2 id="service-mesh的形态">Service Mesh的形态&lt;/h2>
&lt;p>我们从三个维度对比的来看 ServiceMesh 的形态。&lt;/p>
&lt;p>图中左边是传统的微服务形态，调用者和被调用者是通过一个SDK的方式来实现共享服务的，以Dubbo为例，我们会在SDK里提供服务路由、服务发现等功能，虽然我们的开发者在做应用开发的时候并不会太关注SDK的构成，但这些功能是面临不断被变更的可能，有着比较重的逻辑。在右边Service Mesh的形态中，我们首先会对厚重的SDK进行分解，将复杂的逻辑下沉到sidecar，借助sidecar来实现服务的调用。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/meetup-chengdu/forms.jpg">&lt;/p>
&lt;p>虽然在Service Mesh的形态，调用路径要长于传统的形态，路径越长消耗越大，对性能影响越大。但在当前的分布式应用的治理过程中，易用性已经成为一个比性能更重要的话题。当我们给客户部署一套微服务，即便性能很强，但没有处理好易用性问题的话，这将会给技术的推广带来巨大的阻碍，不仅是会影响外部的客户，也会影响内部的用户，如何实现喝着咖啡从容应对双11，必须先解决易用性的问题。在解决易用性问题后，沿着技术的发展路径再去解决性能问题。&lt;/p>
&lt;p>Service Mesh的形态中的control plan不会导致重复建设，但在shared service是有可能存在重复建设的。&lt;/p>
&lt;h2 id="service-mesh下的应用架构">Service Mesh下的应用架构&lt;/h2>
&lt;p>无论是单体应用，还是分布式应用，都可以建立在Service Mesh上，mesh上的sidecar支撑了所有的上层应用，业务开发者无须关心底层构成，可以用Java，也可以用Go等语言完成自己的业务开发。&lt;/p></description></item><item><title>如何基于Dubbo实现全异步调用链</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/09/02/%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8Edubbo%E5%AE%9E%E7%8E%B0%E5%85%A8%E5%BC%82%E6%AD%A5%E8%B0%83%E7%94%A8%E9%93%BE/</link><pubDate>Sun, 02 Sep 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/09/02/%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8Edubbo%E5%AE%9E%E7%8E%B0%E5%85%A8%E5%BC%82%E6%AD%A5%E8%B0%83%E7%94%A8%E9%93%BE/</guid><description>&lt;p>基于Dubbo实现全异步编程，是在2.7.0版本中对现有异步方式增强后新引入的功能。本文先是回顾2.6.x及之前版本对异步的支持情况及存在的问题，引出了2.7.0版本基于CompletableFuture做了哪些针对性的增强，通过几个示例详细阐述了增强后的异步编程的使用方式，最后总结了引入异步模式带来的新问题及Dubbo的解决方法。通过阅读这篇文章，可以很容易的基于Dubbo2.7.0+版本实现一个全异步的远程服务调用链路。&lt;/p>
&lt;p>从3.0.0版本开始，Dubbo框架提供了对Reactive编程范式的支持，除了编程接口之外，在跨进程的RPC通信中引入了Reactive的语义。如果你所在的环境需要使用Reactive编程范式，或者你的RPC调用需要支持流式传输，Reactive应该会给你带来帮助，具体请参考发布在阿里巴巴中间件公众号上的响应式编程支持相关文章。&lt;/p>
&lt;blockquote>
&lt;p>注意，你可能并不是总需要Reactive的语义，尤其是在RPC的场景，CompletableFuture本身也能带给你Reactive模式的编程模型，在选择Reactive（RxJava、Reactor之类）而不是理解及使用成本更低的CompletableFuture前，请尝试关注以下问题：&lt;/p>
&lt;ol>
&lt;li>你是请求/响应是一次性传输的还是流式传输的，一个明显特征是你定义的数据类型是 &lt;code>List&amp;lt;String&amp;gt;&lt;/code> 还是 &lt;code>Stream&amp;lt;String&amp;gt;&lt;/code>&lt;/li>
&lt;li>你的RPC请求有没有要求是Cold，即在subscribe后触发，因为CompletableFuture总是hot的&lt;/li>
&lt;li>你依赖的编程上下文中是否已经在大量使用Reactive的编程接口&lt;/li>
&lt;li>你是否需要Rx框架提供的更丰富的Operator，而这点和1又是密切相关的&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;h2 id="26x版本之前的异步方式">2.6.x版本之前的异步方式&lt;/h2>
&lt;p>在2.6.x及之前的版本提供了一定的异步编程能力，包括Consumer端&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/user/examples/async-call/">异步调用&lt;/a>、&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/user/examples/callback-parameter/">参数回调&lt;/a>、&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/user/examples/events-notify/">事件通知&lt;/a>等，在上面的文档链接中有关于使用方式的简单介绍和Demo。&lt;/p>
&lt;p>关于参数回调，其本质上是一种服务端的数据推送能力，这是终端应用很常见的一种需求，关于这部分的重构计划，不在本文讨论范围。&lt;/p>
&lt;p>但当前的异步方式存在以下问题：&lt;/p>
&lt;ul>
&lt;li>Future获取方式不够直接&lt;/li>
&lt;li>Future接口无法实现自动回调，而自定义ResponseFuture虽支持回调但支持的异步场景有限，如不支持Future间的相互协调或组合等&lt;/li>
&lt;li>不支持Provider端异步&lt;/li>
&lt;/ul>
&lt;p>以Consumer端异步使用方式为例：&lt;/p>
&lt;ol>
&lt;li>定义一个普通的同步接口并声明支持异步调用&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">FooService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">findFoo&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;fooService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.foo.FooService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;findFoo&amp;#34;&lt;/span> async=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>通过RpcContext获取Future&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 此调用会立即返回null&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>fooService.findFoo(fooId);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 拿到调用的Future引用，当结果返回后，会被通知和设置到此Future&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Future&lt;span style="color:#719e07">&amp;lt;&lt;/span>Foo&lt;span style="color:#719e07">&amp;gt;&lt;/span> fooFuture &lt;span style="color:#719e07">=&lt;/span> RpcContext.getContext().getFuture();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>fooFuture.get();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>或&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 此调用会立即返回null&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>fooService.findFoo(fooId);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 拿到Dubbo内置的ResponseFuture并设置回调&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ResponseFuture future &lt;span style="color:#719e07">=&lt;/span> ((FutureAdapter)RpcContext.getContext().getFuture()).getFuture();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>future.setCallback(&lt;span style="color:#719e07">new&lt;/span> ResponseCallback() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">done&lt;/span>(Object response) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.print(response);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">caught&lt;/span>(Throwable exception) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exception.printStackTrace();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>});
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>从这个简单的示例我们可以体会到一些使用中的不便之处：&lt;/p></description></item><item><title>Dubbo 集群容错</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/22/dubbo-%E9%9B%86%E7%BE%A4%E5%AE%B9%E9%94%99/</link><pubDate>Wed, 22 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/22/dubbo-%E9%9B%86%E7%BE%A4%E5%AE%B9%E9%94%99/</guid><description>&lt;h3 id="design-for-failure">Design For failure&lt;/h3>
&lt;p>在分布式系统中，集群某个某些节点出现问题是大概率事件，因此在设计分布式RPC框架的过程中，必须要把失败作为设计的一等公民来对待。一次调用失败之后，应该如何选择对失败的选择策略，这是一个见仁见智的问题，每种策略可能都有自己独特的应用场景。因此，作为框架来说，应当针对不同场景提供多种策略，供用户进行选择。&lt;/p>
&lt;p>在Dubbo设计中，通过Cluster这个接口的抽象，把一组可供调用的Provider信息组合成为一个统一的&lt;code>Invoker&lt;/code>供调用方进行调用。经过路由规则过滤，负载均衡选址后，选中一个具体地址进行调用，如果调用失败，则会按照集群配置的容错策略进行容错处理。&lt;/p>
&lt;p>Dubbo默认内置了若干容错策略，如果不能满足用户需求，则可以通过自定义容错策略进行配置。&lt;/p>
&lt;h3 id="内置容错策略">内置容错策略&lt;/h3>
&lt;p>Dubbo主要内置了如下几种策略：&lt;/p>
&lt;ul>
&lt;li>Failover(失败自动切换)&lt;/li>
&lt;li>Failsafe(失败安全)&lt;/li>
&lt;li>Failfast(快速失败)&lt;/li>
&lt;li>Failback(失败自动恢复)&lt;/li>
&lt;li>Forking(并行调用)&lt;/li>
&lt;li>Broadcast(广播调用)&lt;/li>
&lt;/ul>
&lt;p>这些名称比较相似，概念也比较容易混淆，下面逐一进行解释。&lt;/p>
&lt;h4 id="failover失败自动切换">Failover(失败自动切换)&lt;/h4>
&lt;p>&lt;code>Failover&lt;/code>是高可用系统中的一个常用概念，服务器通常拥有主备两套机器配置，如果主服务器出现故障，则自动切换到备服务器中，从而保证了整体的高可用性。&lt;/p>
&lt;p>Dubbo也借鉴了这个思想，并且把它作为Dubbo&lt;code>默认的容错策略&lt;/code>。当调用出现失败的时候，根据配置的重试次数，会自动从其他可用地址中重新选择一个可用的地址进行调用，直到调用成功，或者是达到重试的上限位置。&lt;/p>
&lt;p>Dubbo里默认配置的重试次数是2，也就是说，算上第一次调用，最多会调用3次。&lt;/p>
&lt;p>其配置方法，容错策略既可以在服务提供方配置，也可以服务调用方进行配置。而重试次数的配置则更为灵活，既可以在服务级别进行配置，也可以在方法级别进行配置。具体优先顺序为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>服务调用方方法级配置 &amp;gt; 服务调用方服务级配置 &amp;gt; 服务提供方方法级配置 &amp;gt; 服务提供方服务级配置
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>以XML方式为例，具体配置方法如下：&lt;/p>
&lt;p>服务提供方，服务级配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> cluster=&lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span> retries=&lt;span style="color:#2aa198">&amp;#34;2&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务提供方，方法级配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>cluster=&lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span> retries=&lt;span style="color:#2aa198">&amp;#34;2&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务调用方，服务级配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> cluster=&lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span> retries=&lt;span style="color:#2aa198">&amp;#34;1&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务调用方，方法级配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> cluster=&lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span> retries=&lt;span style="color:#2aa198">&amp;#34;3&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Failover可以自动对失败进行重试，对调用者屏蔽了失败的细节，但是Failover策略也会带来一些副作用：&lt;/p>
&lt;ul>
&lt;li>重试会额外增加一下开销，例如增加资源的使用，在高负载系统下，额外的重试可能让系统雪上加霜。&lt;/li>
&lt;li>重试会增加调用的响应时间。&lt;/li>
&lt;li>某些情况下，重试甚至会造成资源的浪费。考虑一个调用场景，A-&amp;gt;B-&amp;gt;C，如果A处设置了超时100ms，再B-&amp;gt;C的第一次调用完成时已经超过了100ms，但很不幸B-&amp;gt;C失败，这时候会进行重试，但其实这时候重试已经没有意义，因此在A看来这次调用已经超时，A可能已经开始执行其他逻辑。&lt;/li>
&lt;/ul>
&lt;h4 id="failsafe失败安全">Failsafe(失败安全)&lt;/h4>
&lt;p>失败安全策略的核心是即使失败了也不会影响整个调用流程。通常情况下用于旁路系统或流程中，它的失败不影响核心业务的正确性。在实现上，当出现调用失败时，会忽略此错误，并记录一条日志，同时返回一个空结果，在上游看来调用是成功的。&lt;/p>
&lt;p>应用场景，可以用于写入审计日志等操作。&lt;/p>
&lt;p>具体配置方法：&lt;/p>
&lt;p>服务提供方，服务级配置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> cluster=&lt;span style="color:#2aa198">&amp;#34;failsafe&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务调用方，服务级配置&lt;/p></description></item><item><title>Spring应用快速集成Dubbo + Hystrix</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/22/spring%E5%BA%94%E7%94%A8%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90dubbo--hystrix/</link><pubDate>Wed, 22 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/22/spring%E5%BA%94%E7%94%A8%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90dubbo--hystrix/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点，从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离，请求缓存和请求打包，以及监控和配置等功能。&lt;/p>
&lt;p>Dubbo是Alibaba开源的，目前国内最流行的java rpc框架。&lt;/p>
&lt;p>本文介绍在spring应用里，怎么把Dubbo和Hystrix结合起来使用。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/Netflix/Hystrix">https://github.com/Netflix/Hystrix&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/apache/dubbo">https://github.com/apache/dubbo&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="spring-boot应用">Spring Boot应用&lt;/h2>
&lt;p>Demo地址： &lt;a href="https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-boot-hystrix">https://github.com/dubbo/dubbo-samples/tree/master/4-governance/dubbo-samples-spring-boot-hystrix&lt;/a>&lt;/p>
&lt;h3 id="生成dubbo集成spring-boot的应用">生成dubbo集成spring boot的应用&lt;/h3>
&lt;p>对于不熟悉dubbo 集成spring boot应用的同学，可以在这里直接生成dubbo + spring boot的工程： &lt;a href="http://start.dubbo.io/">http://start.dubbo.io/&lt;/a>&lt;/p>
&lt;h3 id="配置spring-cloud-starter-netflix-hystrix">配置spring-cloud-starter-netflix-hystrix&lt;/h3>
&lt;p>spring boot官方提供了对hystrix的集成，直接在pom.xml里加入依赖：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.cloud&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-cloud-starter-netflix-hystrix&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>1.4.4.RELEASE&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后在Application类上增加&lt;code>@EnableHystrix&lt;/code>来启用hystrix starter：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SpringBootApplication&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableHystrix&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderApplication&lt;/span> {
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="配置provider端">配置Provider端&lt;/h3>
&lt;p>在Dubbo的Provider上增加&lt;code>@HystrixCommand&lt;/code>配置，这样子调用就会经过Hystrix代理。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>(version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">HelloServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> HelloService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixCommand&lt;/span>(commandProperties &lt;span style="color:#719e07">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixProperty&lt;/span>(name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;circuitBreaker.requestVolumeThreshold&amp;#34;&lt;/span>, value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;10&amp;#34;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@HystrixProperty&lt;/span>(name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;execution.isolation.thread.timeoutInMilliseconds&amp;#34;&lt;/span>, value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;2000&amp;#34;&lt;/span>) })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// System.out.println(&amp;#34;async provider received: &amp;#34; + name);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// return &amp;#34;annotation: hello, &amp;#34; + name;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException(&lt;span style="color:#2aa198">&amp;#34;Exception to show hystrix enabled.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="配置consumer端">配置Consumer端&lt;/h3>
&lt;p>对于Consumer端，则可以增加一层method调用，并在method上配置&lt;code>@HystrixCommand&lt;/code>。当调用出错时，会走到&lt;code>fallbackMethod = &amp;quot;reliable&amp;quot;&lt;/code>的调用里。&lt;/p></description></item><item><title>Dubbo 现有心跳方案总结以及改进建议</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/19/dubbo-%E7%8E%B0%E6%9C%89%E5%BF%83%E8%B7%B3%E6%96%B9%E6%A1%88%E6%80%BB%E7%BB%93%E4%BB%A5%E5%8F%8A%E6%94%B9%E8%BF%9B%E5%BB%BA%E8%AE%AE/</link><pubDate>Sun, 19 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/19/dubbo-%E7%8E%B0%E6%9C%89%E5%BF%83%E8%B7%B3%E6%96%B9%E6%A1%88%E6%80%BB%E7%BB%93%E4%BB%A5%E5%8F%8A%E6%94%B9%E8%BF%9B%E5%BB%BA%E8%AE%AE/</guid><description>&lt;h3 id="1-前言">1 前言&lt;/h3>
&lt;p>设计一个好的心跳机制并不是一件容易的事，就我所熟知的几个 RPC 框架，它们的心跳机制可以说大相径庭，这篇文章我将探讨一下&lt;strong>如何设计一个优雅的心跳机制，主要从 Dubbo 的现有方案以及一个改进方案来做分析&lt;/strong>。&lt;/p>
&lt;h3 id="2-预备知识">2 预备知识&lt;/h3>
&lt;p>因为后续我们将从源码层面来进行介绍，所以一些服务治理框架的细节还需要提前交代一下，方便大家理解。&lt;/p>
&lt;h4 id="21-客户端如何得知请求失败了">2.1 客户端如何得知请求失败了？&lt;/h4>
&lt;p>高性能的 RPC 框架几乎都会选择使用 Netty 来作为通信层的组件，非阻塞式通信的高效不需要我做过多的介绍。但也由于非阻塞的特性，导致其发送数据和接收数据是一个异步的过程，所以当存在服务端异常、网络问题时，客户端是接收不到响应的，那么我们如何判断一次 RPC 调用是失败的呢？&lt;/p>
&lt;p>误区一：Dubbo 调用不是默认同步的吗？&lt;/p>
&lt;p>Dubbo 在通信层是异步的，呈现给使用者同步的错觉是因为内部做了阻塞等待，实现了异步转同步。&lt;/p>
&lt;p>误区二： &lt;code>Channel.writeAndFlush&lt;/code> 会返回一个 &lt;code>channelFuture&lt;/code>，我只需要判断 &lt;code>channelFuture.isSuccess&lt;/code> 就可以判断请求是否成功了。&lt;/p>
&lt;p>注意，writeAndFlush 成功并不代表对端接受到了请求，返回值为 true 只能保证写入网络缓冲区成功，并不代表发送成功。&lt;/p>
&lt;p>避开上述两个误区，我们再来回到本小节的标题：客户端如何得知请求失败？&lt;strong>正确的逻辑应当是以客户端接收到失败响应为判断依据&lt;/strong>。等等，前面不还在说在失败的场景中，服务端是不会返回响应的吗？没错，既然服务端不会返回，那就只能客户端自己造了。&lt;/p>
&lt;p>一个常见的设计是：客户端发起一个 RPC 请求，会设置一个超时时间 &lt;code>client_timeout&lt;/code>，发起调用的同时，客户端会开启一个延迟 &lt;code>client_timeout&lt;/code> 的定时器&lt;/p>
&lt;ul>
&lt;li>接收到正常响应时，移除该定时器。&lt;/li>
&lt;li>定时器倒计时完毕，还没有被移除，则认为请求超时，构造一个失败的响应传递给客户端。&lt;/li>
&lt;/ul>
&lt;p>Dubbo 中的超时判定逻辑：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> DefaultFuture &lt;span style="color:#268bd2">newFuture&lt;/span>(Channel channel, Request request, &lt;span style="color:#dc322f">int&lt;/span> timeout) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> DefaultFuture future &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DefaultFuture(channel, request, timeout);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// timeout check&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timeoutCheck(future);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> future;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">timeoutCheck&lt;/span>(DefaultFuture future) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TimeoutCheckTask task &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> TimeoutCheckTask(future);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">TimeoutCheckTask&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> TimerTask {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DefaultFuture future;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TimeoutCheckTask(DefaultFuture future) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>.future &lt;span style="color:#719e07">=&lt;/span> future;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">run&lt;/span>(Timeout timeout) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (future &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> future.isDone()) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// create exception response.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Response timeoutResponse &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Response(future.getId());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// set timeout status.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timeoutResponse.setStatus(future.isSent() &lt;span style="color:#719e07">?&lt;/span> Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timeoutResponse.setErrorMessage(future.getTimeoutMessage(&lt;span style="color:#cb4b16">true&lt;/span>));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// handle response.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DefaultFuture.received(future.getChannel(), timeoutResponse);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>主要逻辑涉及的类：&lt;code>DubboInvoker&lt;/code>，&lt;code>HeaderExchangeChannel&lt;/code>，&lt;code>DefaultFuture&lt;/code> ，通过上述代码，我们可以得知一个细节，无论是何种调用，都会经过这个定时器的检测，&lt;strong>超时即调用失败，一次 RPC 调用的失败，必须以客户端收到失败响应为准&lt;/strong>。&lt;/p></description></item><item><title>Dubbo2.7 三大新特性详解</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/15/dubbo2.7-%E4%B8%89%E5%A4%A7%E6%96%B0%E7%89%B9%E6%80%A7%E8%AF%A6%E8%A7%A3/</link><pubDate>Wed, 15 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/15/dubbo2.7-%E4%B8%89%E5%A4%A7%E6%96%B0%E7%89%B9%E6%80%A7%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h2 id="1-背景介绍">1 背景介绍&lt;/h2>
&lt;p>自 2017 年 7 月阿里重启 Dubbo 开源，到目前为止 github star 数，contributor 数都有了非常大的提升。2018 年 2 月 9 日阿里决定将 Dubbo 项目贡献给 Apache，经过一周的投票，顺利成为了 Apache 的孵化项目，也就是大家现在看到的 &lt;strong>Incubator Dubbo&lt;/strong>。预计在 2019 年 4 月，Dubbo 可以达成毕业，成为 Apache 的顶级项目。&lt;/p>
&lt;h2 id="2-分支介绍">2 分支介绍&lt;/h2>
&lt;p>&lt;img alt="分支" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/270/branches.png">&lt;/p>
&lt;p>Dubbo 目前有如图所示的 5 个分支，其中 2.7.1-release 只是一个临时分支，忽略不计，对其他 4 个分支进行介绍。&lt;/p>
&lt;ul>
&lt;li>2.5.x 近期已经通过投票，Dubbo 社区即将停止对其的维护。&lt;/li>
&lt;li>2.6.x 为长期支持的版本，也是 Dubbo 贡献给 Apache 之前的版本，其包名前缀为：com.alibaba，JDK 版本对应 1.6。&lt;/li>
&lt;li>3.x-dev 是前瞻性的版本，对 Dubbo 进行一些高级特性的补充，如支持 rx 特性。&lt;/li>
&lt;li>master 为长期支持的版本，版本号为 2.7.x，也是 Dubbo 贡献给 Apache 的开发版本，其包名前缀为：org.apache，JDK 版本对应 1.8。&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>如果想要研究 Dubbo 的源码，建议直接浏览 master 分支。&lt;/p></description></item><item><title>Dubbo 关于同步/异步调用的几种方式</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%85%B3%E4%BA%8E%E5%90%8C%E6%AD%A5/%E5%BC%82%E6%AD%A5%E8%B0%83%E7%94%A8%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%85%B3%E4%BA%8E%E5%90%8C%E6%AD%A5/%E5%BC%82%E6%AD%A5%E8%B0%83%E7%94%A8%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F/</guid><description>&lt;p>我们知道，Dubbo 缺省协议采用单一长连接，底层实现是 Netty 的 NIO 异步通讯机制；基于这种机制，Dubbo 实现了以下几种调用方式：&lt;/p>
&lt;ul>
&lt;li>同步调用&lt;/li>
&lt;li>异步调用&lt;/li>
&lt;li>参数回调&lt;/li>
&lt;li>事件通知&lt;/li>
&lt;/ul>
&lt;h2 id="同步调用">同步调用&lt;/h2>
&lt;p>同步调用是一种阻塞式的调用方式，即 Consumer 端代码一直阻塞等待，直到 Provider 端返回为止；&lt;/p>
&lt;p>通常，一个典型的同步调用过程如下：&lt;/p>
&lt;ol>
&lt;li>Consumer 业务线程调用远程接口，向 Provider 发送请求，同时当前线程处于&lt;code>阻塞&lt;/code>状态；&lt;/li>
&lt;li>Provider 接到 Consumer 的请求后，开始处理请求，将结果返回给 Consumer；&lt;/li>
&lt;li>Consumer 收到结果后，当前线程继续往后执行。&lt;/li>
&lt;/ol>
&lt;p>这里有 2 个问题：&lt;/p>
&lt;ol>
&lt;li>Consumer 业务线程是怎么进入&lt;code>阻塞&lt;/code>状态的？&lt;/li>
&lt;li>Consumer 收到结果后，如何唤醒业务线程往后执行的？&lt;/li>
&lt;/ol>
&lt;p>其实，Dubbo 的底层 IO 操作都是异步的。Consumer 端发起调用后，得到一个 Future 对象。对于同步调用，业务线程通过&lt;code>Future#get(timeout)&lt;/code>，阻塞等待 Provider 端将结果返回；&lt;code>timeout&lt;/code>则是 Consumer 端定义的超时时间。当结果返回后，会设置到此 Future，并唤醒阻塞的业务线程；当超时时间到结果还未返回时，业务线程将会异常返回。&lt;/p>
&lt;h2 id="异步调用">异步调用&lt;/h2>
&lt;p>基于 Dubbo 底层的异步 NIO 实现异步调用，对于 Provider 响应时间较长的场景是必须的，它能有效利用 Consumer 端的资源，相对于 Consumer 端使用多线程来说开销较小。&lt;/p>
&lt;p>异步调用，对于 Provider 端不需要做特别的配置。下面的例子中，Provider 端接口定义如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">AsyncService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">goodbye&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="consumer-配置">Consumer 配置&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;asyncService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.async.api.AsyncService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;goodbye&amp;#34;&lt;/span> async=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dubbo:reference&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>需要异步调用的方法，均需要使用 &lt;code>&amp;lt;dubbo:method/&amp;gt;&lt;/code>标签进行描述。&lt;/p></description></item><item><title>Dubbo 基本用法 - Dubbo Consumer 配置</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95-dubbo-consumer-%E9%85%8D%E7%BD%AE/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95-dubbo-consumer-%E9%85%8D%E7%BD%AE/</guid><description>&lt;h2 id="dubbo-consumer配置">Dubbo Consumer配置&lt;/h2>
&lt;h3 id="consumer配置详解">Consumer配置详解&lt;/h3>
&lt;p>配置Dubbo Consumer有3种方式：XML配置，API调用方式配置，注解方式配置。&lt;/p>
&lt;h4 id="xml配置">XML配置&lt;/h4>
&lt;h6 id="最简单的配置的样例">最简单的配置的样例：&lt;/h6>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;beans xmlns=&amp;#34;http://www.springframework.org/schema/beans&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:xsi=&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:dubbo=&amp;#34;http://dubbo.apache.org/schema/dubbo&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xsi:schemaLocation=&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd&amp;#34;&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:application name=&amp;#34;hello-world-app&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:registry address=&amp;#34;multicast://224.5.6.7:1234&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:protocol name=&amp;#34;dubbo&amp;#34; port=&amp;#34;20880&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:reference id=&amp;#34;demoServiceRemote&amp;#34; interface=&amp;#34;com.alibaba.dubbo.demo.DemoService&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/beans&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>支持的配置标签及对应的配置项详解，参考provider中的用法。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>接下来重点讲解下&amp;lt;dubbo:reference/&amp;gt;的配置。&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>&amp;lt;dubbo:reference/&amp;gt;支持的主要属性列表：&lt;/li>
&lt;/ul>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">属性名&lt;/th>
 &lt;th style="text-align: left">说明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">id&lt;/td>
 &lt;td style="text-align: left">服务引用id，作为java bean id，需要唯一&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">interface&lt;/td>
 &lt;td style="text-align: left">接口名，用于查找服务&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">version&lt;/td>
 &lt;td style="text-align: left">版本号，与服务提供者的版本一致&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">timeout&lt;/td>
 &lt;td style="text-align: left">服务方法调用超时时间(毫秒)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">retries&lt;/td>
 &lt;td style="text-align: left">远程服务调用重试次数，不包括第一次调用，不需要重试请设为0&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">connections&lt;/td>
 &lt;td style="text-align: left">对每个提供者的最大连接数，rmi、http、hessian等短连接协议表示限制连接数，dubbo等长连接协表示建立的长连接个数&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">loadbalance&lt;/td>
 &lt;td style="text-align: left">负载均衡策略，可选值：random,roundrobin,leastactive，分别表示：随机，轮询，最少活跃调用&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">async&lt;/td>
 &lt;td style="text-align: left">是否异步执行，不可靠异步，只是忽略返回值，不阻塞执行线程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">generic&lt;/td>
 &lt;td style="text-align: left">泛化调用，可以绕过&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">check&lt;/td>
 &lt;td style="text-align: left">启动时检查提供者是否存在，true报错，false忽略&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">actives&lt;/td>
 &lt;td style="text-align: left">每服务消费者每服务每方法最大并发调用数&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>其他配置属性请参考xsd：http://dubbo.apache.org/schema/dubbo/dubbo.xsd&lt;/p></description></item><item><title>Dubbo 基础用法 - Provider 配置</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%9F%BA%E7%A1%80%E7%94%A8%E6%B3%95-provider-%E9%85%8D%E7%BD%AE/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E5%9F%BA%E7%A1%80%E7%94%A8%E6%B3%95-provider-%E9%85%8D%E7%BD%AE/</guid><description>&lt;h2 id="dubbo基本用法">Dubbo基本用法&lt;/h2>
&lt;p>本章节主要讲述如何配置dubbo，按照配置方式上分，可以分为：XML配置，properties方式配置，注解方式配置，API调用方式配置。
按照功能角度进行划分，可以分为Dubbo Provider和Dubbo Consumer。接下来章节中，分别对dubbo provider和Dubbo consumer进行讲解。&lt;/p>
&lt;h3 id="dubbo-provider配置">Dubbo Provider配置&lt;/h3>
&lt;h4 id="provider-配置详解">Provider 配置详解&lt;/h4>
&lt;p>配置Dubbo Provider有4种方式：XML配置，properties方式配置，API调用方式配置，注解方式配置。&lt;/p>
&lt;h5 id="xml配置">XML配置&lt;/h5>
&lt;h6 id="最简单的配置的样例">最简单的配置的样例：&lt;/h6>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;beans xmlns=&amp;#34;http://www.springframework.org/schema/beans&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:xsi=&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:dubbo=&amp;#34;http://dubbo.apache.org/schema/dubbo&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xsi:schemaLocation=&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd&amp;#34;&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:application name=&amp;#34;hello-world-app&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:registry address=&amp;#34;multicast://224.5.6.7:1234&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:protocol name=&amp;#34;dubbo&amp;#34; port=&amp;#34;20880&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:service interface=&amp;#34;com.alibaba.dubbo.demo.DemoService&amp;#34; ref=&amp;#34;demoServiceLocal&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:reference id=&amp;#34;demoServiceRemote&amp;#34; interface=&amp;#34;com.alibaba.dubbo.demo.DemoService&amp;#34; /&amp;gt; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/beans&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面样例中，注意下dubbo schema的写法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;beans xmlns:xsi=&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:dubbo=&amp;#34;http://code.alibabatech.com/schema/dubbo&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns=&amp;#34;http://www.springframework.org/schema/beans&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xsi:schemaLocation=&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd&amp;#34;&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="支持的配置标签">支持的配置标签&lt;/h6>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">标签&lt;/th>
 &lt;th style="text-align: left">用途&lt;/th>
 &lt;th style="text-align: left">解释&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:service/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">服务配置&lt;/td>
 &lt;td style="text-align: left">用于暴露一个服务，定义服务的元信息，一个服务可以用多个协议暴露，一个服务也可以注册到多个注册中心&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:reference/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">引用配置&lt;/td>
 &lt;td style="text-align: left">用于创建一个远程服务代理，一个引用可以指向多个注册中心&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:protocol/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">协议配置&lt;/td>
 &lt;td style="text-align: left">用于配置提供服务的协议信息，协议由提供方指定，消费方被动接受&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:application/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">应用配置&lt;/td>
 &lt;td style="text-align: left">用于配置当前应用信息，不管该应用是提供者还是消费者&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:module/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">模块配置&lt;/td>
 &lt;td style="text-align: left">用于配置当前模块信息，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:registry/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">注册中心配置&lt;/td>
 &lt;td style="text-align: left">用于配置连接注册中心相关信息&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:monitor/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">监控中心配置&lt;/td>
 &lt;td style="text-align: left">用于配置连接监控中心相关信息，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:provider/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">提供方配置&lt;/td>
 &lt;td style="text-align: left">当 ProtocolConfig 和 ServiceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:consumer/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">消费方配置&lt;/td>
 &lt;td style="text-align: left">当 ReferenceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:method/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">方法配置&lt;/td>
 &lt;td style="text-align: left">用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&amp;lt;dubbo:argument/&amp;gt;&lt;/td>
 &lt;td style="text-align: left">参数配置&lt;/td>
 &lt;td style="text-align: left">用于指定方法参数配置&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;img alt="配置之间关系图" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/user/dubbo-config.jpg">&lt;/p></description></item><item><title>Dubbo 优雅停机</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E4%BC%98%E9%9B%85%E5%81%9C%E6%9C%BA/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo-%E4%BC%98%E9%9B%85%E5%81%9C%E6%9C%BA/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>对于任何一个线上应用，如何在服务更新部署过程中保证客户端无感知是开发者必须要解决的问题，即从应用停止到重启恢复服务这个阶段不能影响正常的业务请求。理想条件下，在没有请求的时候再进行更新是最安全可靠的，然而互联网应用必须要保证可用性，因此在技术层面上优化应用更新流程来保证服务在更新时无损是必要的。&lt;/p>
&lt;p>传统的解决方式是通过将应用更新流程划分为手工摘流量、停应用、更新重启三个步骤，由人工操作实现客户端无对更新感知。这种方式简单而有效，但是限制较多：不仅需要使用借助网关的支持来摘流量，还需要在停应用前人工判断来保证在途请求已经处理完毕。这种需要人工介入的方式运维复杂度较高，只能适用规模较小的应用，无法在大规模系统上使用。&lt;/p>
&lt;p>因此，如果在容器/框架级别提供某种自动化机制，来自动进行摘流量并确保处理完以到达的请求，不仅能保证业务不受更新影响，还可以极大地提升更新应用时的运维效率。&lt;/p>
&lt;p>这个机制也就是优雅停机，目前Tomcat/Undertow/Dubbo等容器/框架都有提供相关实现。下面给出正式一些的定义：优雅停机是指在停止应用时，执行的一系列保证应用正常关闭的操作。这些操作往往包括等待已有请求执行完成、关闭线程、关闭连接和释放资源等，优雅停机可以避免非正常关闭程序可能造成数据异常或丢失，应用异常等问题。优雅停机本质上是JVM即将关闭前执行的一些额外的处理代码。&lt;/p>
&lt;h2 id="适用场景">适用场景&lt;/h2>
&lt;ul>
&lt;li>JVM主动关闭(&lt;code>System.exit(int)&lt;/code>；&lt;/li>
&lt;li>JVM由于资源问题退出(&lt;code>OOM&lt;/code>)；&lt;/li>
&lt;li>应用程序接受到&lt;code>SIGTERM&lt;/code>或&lt;code>SIGINT&lt;/code>信号。&lt;/li>
&lt;/ul>
&lt;h2 id="配置方式">配置方式&lt;/h2>
&lt;h3 id="服务的优雅停机">服务的优雅停机&lt;/h3>
&lt;p>在Dubbo中，优雅停机是默认开启的，停机等待时间为10000毫秒。可以通过配置&lt;code>dubbo.service.shutdown.wait&lt;/code>来修改等待时间。&lt;/p>
&lt;p>例如将等待时间设置为20秒可通过增加以下配置实现：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>dubbo.service.shutdown.wait&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">20000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="容器的优雅停机">容器的优雅停机&lt;/h3>
&lt;p>当使用&lt;code>org.apache.dubbo.container.Main&lt;/code>这种容器方式来使用 Dubbo 时，也可以通过配置&lt;code>dubbo.shutdown.hook&lt;/code>为&lt;code>true&lt;/code>来开启优雅停机。&lt;/p>
&lt;h3 id="通过qos优雅上下线">通过QOS优雅上下线&lt;/h3>
&lt;p>基于&lt;code>ShutdownHook&lt;/code>方式的优雅停机无法确保所有关闭流程一定执行完，所以 Dubbo 推出了多段关闭的方式来保证服务完全无损。&lt;/p>
&lt;p>多段关闭即将停止应用分为多个步骤，通过运维自动化脚本或手工操作的方式来保证脚本每一阶段都能执行完毕。&lt;/p>
&lt;p>在关闭应用前，首先通过 QOS 的&lt;code>offline&lt;/code>指令下线所有服务，然后等待一定时间确保已经到达请求全部处理完毕，由于服务已经在注册中心下线，当前应用不会有新的请求。这时再执行真正的关闭(&lt;code>SIGTERM&lt;/code> 或&lt;code> SIGINT&lt;/code>)流程，就能保证服务无损。&lt;/p>
&lt;p>QOS可通过 telnet 或 HTTP 方式使用，具体方式请见&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/user/references/qos/">Dubbo-QOS命令使用说明&lt;/a>。&lt;/p>
&lt;h2 id="流程">流程&lt;/h2>
&lt;p>Provider在接收到停机指令后&lt;/p>
&lt;ul>
&lt;li>从注册中心上注销所有服务；&lt;/li>
&lt;li>从配置中心取消监听动态配置；&lt;/li>
&lt;li>向所有连接的客户端发送只读事件，停止接收新请求；&lt;/li>
&lt;li>等待一段时间以处理已到达的请求，然后关闭请求处理线程池；&lt;/li>
&lt;li>断开所有客户端连接。&lt;/li>
&lt;/ul>
&lt;p>Consumer在接收到停机指令后&lt;/p>
&lt;ul>
&lt;li>拒绝新到请求，直接返回调用异常；&lt;/li>
&lt;li>等待当前已发送请求执行完毕，如果响应超时则强制关闭连接。&lt;/li>
&lt;/ul>
&lt;p>当使用容器方式运行 Dubbo 时，在容器准备退出前，可进行一系列的资源释放和清理工。&lt;/p>
&lt;p>例如使用 SpringContainer时，Dubbo 的ShutdownHook线程会执行&lt;code>ApplicationContext&lt;/code>的&lt;code>stop&lt;/code>和&lt;code>close&lt;/code>方法，保证 Bean的生命周期完整。&lt;/p>
&lt;h2 id="实现原理">实现原理&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>在加载类&lt;code>org.apache.dubbo.config.AbstractConfig&lt;/code>时，通过&lt;code>org.apache.dubbo.config.DubboShutdownHook&lt;/code>向JVM注册 ShutdownHook。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Register the ShutdownHook
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">register&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (&lt;span style="color:#719e07">!&lt;/span>registered.get() &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registered.compareAndSet(&lt;span style="color:#cb4b16">false&lt;/span>, &lt;span style="color:#cb4b16">true&lt;/span>)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Runtime.getRuntime().addShutdownHook(getDubboShutdownHook());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>每个ShutdownHook都是一个单独的线程，由JVM在退出时触发执行&lt;code>org.apache.dubbo.config.DubboShutdownHook&lt;/code>。&lt;/p></description></item><item><title>Dubbo的泛化调用</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo%E7%9A%84%E6%B3%9B%E5%8C%96%E8%B0%83%E7%94%A8/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/dubbo%E7%9A%84%E6%B3%9B%E5%8C%96%E8%B0%83%E7%94%A8/</guid><description>&lt;p>以下几种场景可以考虑使用泛化调用：&lt;/p>
&lt;ul>
&lt;li>服务测试平台&lt;/li>
&lt;li>API 服务网关&lt;/li>
&lt;/ul>
&lt;p>泛化调用主要用于消费端没有 API 接口的情况；不需要引入接口 jar 包，而是直接通过 GenericService 接口来发起服务调用，参数及返回值中的所有 POJO 均用 &lt;code>Map&lt;/code> 表示。泛化调用对于服务端无需关注，按正常服务进行暴露即可。&lt;/p>
&lt;p>下面来看看消费端如何使用泛化调用进行服务调用。&lt;/p>
&lt;h2 id="通过-spring-xml-配置进行泛化调用">通过 Spring XML 配置进行泛化调用&lt;/h2>
&lt;p>在 Spring 配置申明 &lt;code>generic=&amp;quot;true&amp;quot;&lt;/code>，如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;userService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.generic.api.IUserService&amp;#34;&lt;/span> generic=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>需要使用的地方，通过强制类型转化为 GenericService 进行调用：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>GenericService userService &lt;span style="color:#719e07">=&lt;/span> (GenericService) context.getBean(&lt;span style="color:#2aa198">&amp;#34;userService&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// primary param and return value&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>String name &lt;span style="color:#719e07">=&lt;/span> (String) userService.$invoke(&lt;span style="color:#2aa198">&amp;#34;delete&amp;#34;&lt;/span>, &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span>{&lt;span style="color:#dc322f">int&lt;/span>.class.getName()}, &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[]&lt;/span>{1});
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>System.out.println(name);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中：&lt;/p>
&lt;ol>
&lt;li>GenericService 这个接口只有一个方法，名为 &lt;code>$invoke&lt;/code>，它接受三个参数，分别为方法名、方法参数类型数组和参数值数组；&lt;/li>
&lt;li>对于方法参数类型数组
&lt;ol>
&lt;li>如果是基本类型，如 int 或 long，可以使用 &lt;code>int.class.getName()&lt;/code>获取其类型；&lt;/li>
&lt;li>如果是基本类型数组，如 int[]，则可以使用 &lt;code>int[].class.getName()&lt;/code>；&lt;/li>
&lt;li>如果是 POJO，则直接使用全类名，如 &lt;code>com.alibaba.dubbo.samples.generic.api.Params&lt;/code>。&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ol>
&lt;h2 id="通过-api-编程进行泛化调用">通过 API 编程进行泛化调用&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>ApplicationConfig application = new ApplicationConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application.setName(&amp;#34;api-generic-consumer&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>RegistryConfig registry = new RegistryConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>registry.setAddress(&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application.setRegistry(registry);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ReferenceConfig&amp;lt;GenericService&amp;gt; reference = new ReferenceConfig&amp;lt;GenericService&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 弱类型接口名
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference.setInterface(&amp;#34;com.alibaba.dubbo.samples.generic.api.IUserService&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 声明为泛化接口
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference.setGeneric(true);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference.setApplication(application);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>GenericService genericService = reference.get();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>String name = (String) genericService.$invoke(&amp;#34;delete&amp;#34;, new String[]{int.class.getName()}, new Object[]{1});
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>System.out.println(name);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过 API 的方式，不需要像 XML 的方式需要提前将服务配置好，可以动态构建 ReferenceConfig；相对 XML 来说，API 的方式更常见。&lt;/p></description></item><item><title>Spring Boot Dubbo应用启停源码分析</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/spring-boot-dubbo%E5%BA%94%E7%94%A8%E5%90%AF%E5%81%9C%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/spring-boot-dubbo%E5%BA%94%E7%94%A8%E5%90%AF%E5%81%9C%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</guid><description>&lt;h2 id="背景介绍">背景介绍&lt;/h2>
&lt;p>&lt;a href="https://github.com/apache/dubbo-spring-boot-project">Dubbo Spring Boot&lt;/a> 工程致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发。同时也整合了 Spring Boot 特性：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/apache/dubbo-spring-boot-project/blob/master/dubbo-spring-boot-autoconfigure">自动装配&lt;/a> (比如： 注解驱动, 自动装配等).&lt;/li>
&lt;li>&lt;a href="https://github.com/apache/dubbo-spring-boot-project/blob/master/dubbo-spring-boot-actuator">Production-Ready&lt;/a> (比如： 安全, 健康检查, 外部化配置等).&lt;/li>
&lt;/ul>
&lt;h2 id="dubboconsumer启动分析">DubboConsumer启动分析&lt;/h2>
&lt;p>你有没有想过一个问题？&lt;code>dubbo-spring-boot-project&lt;/code>中的&lt;code>DubboConsumerDemo&lt;/code>应用就一行代码，&lt;code>main&lt;/code>方法执行完之后，为什么不会直接退出呢？&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SpringBootApplication&lt;/span>(scanBasePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.alibaba.boot.dubbo.demo.consumer.controller&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboConsumerDemo&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>(String&lt;span style="color:#719e07">[]&lt;/span> args) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SpringApplication.run(DubboConsumerDemo.class,args);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其实要回答这样一个问题，我们首先需要把这个问题进行一个抽象，即一个JVM进程，在什么情况下会退出？&lt;/p>
&lt;p>以Java 8为例，通过查阅JVM语言规范[1]，在12.8章节中有清晰的描述：&lt;/p>
&lt;p>A program terminates all its activity and &lt;em>exits&lt;/em> when one of two things happens:&lt;/p>
&lt;ul>
&lt;li>All the threads that are not daemon threads terminate.&lt;/li>
&lt;li>Some thread invokes the &lt;code>exit&lt;/code> method of class &lt;code>Runtime&lt;/code> or class &lt;code>System&lt;/code>, and the &lt;code>exit&lt;/code> operation is not forbidden by the security manager.&lt;/li>
&lt;/ul>
&lt;p>也就是说，导致JVM的退出只有2种情况：&lt;/p></description></item><item><title>通过QoS对服务进行动态控制</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/%E9%80%9A%E8%BF%87qos%E5%AF%B9%E6%9C%8D%E5%8A%A1%E8%BF%9B%E8%A1%8C%E5%8A%A8%E6%80%81%E6%8E%A7%E5%88%B6/</link><pubDate>Tue, 14 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/14/%E9%80%9A%E8%BF%87qos%E5%AF%B9%E6%9C%8D%E5%8A%A1%E8%BF%9B%E8%A1%8C%E5%8A%A8%E6%80%81%E6%8E%A7%E5%88%B6/</guid><description>&lt;p>QoS，全称为&lt;code>Quality of Service&lt;/code>, 是常见于网络设备中的一个术语 ，例如在路由器中，可以通过Qos动态的调整和控制某些端口的权重，从而优先的保障运行在这些端口上的服务质量。&lt;/p>
&lt;p>在Dubbo中，QoS这个概念被用于动态的对服务进行查询和控制。例如对获取当前提供和消费的所有服务，以及对服务进行动态的上下线，即从注册中心上进行注册和反注册操作。&lt;/p>
&lt;h2 id="qos工作机制">QoS工作机制&lt;/h2>
&lt;p>从Dubbo 2.5.8开始，默认引入了Qos功能，默认处于启动状态。所有的QoS功能被抽象成一个个的命令，通过执行这些命令，Qos会返回响应的结果。&lt;/p>
&lt;blockquote>
&lt;p>Qos功能基于Netty4实现，在Dubbo 2.6.x之前的版本中，默认依赖的是Netty3，因此需要显示的添加Netty4的依赖，才能确保Netty4正常工作。如果使用http://start.dubbo.io自动生成的Dubbo应用，则无需添加额外的配置，因为已经默认加上了Netty4的依赖。&lt;/p>
&lt;/blockquote>
&lt;p>Qos的工作机制如下图所示：&lt;/p>
&lt;p>&lt;img alt="undefined" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/qos-architecture.png">&lt;/p>
&lt;ol>
&lt;li>启动并监听一个端口，默认端口是22222&lt;/li>
&lt;li>识别目标请求的协议是Http或者是Telnet，根据协议不同动态添加对应的处理器&lt;/li>
&lt;li>针对不同的协议进行解码，解析出需要执行的命令&lt;/li>
&lt;li>执行命令并返回结果&lt;/li>
&lt;/ol>
&lt;h2 id="qos命令">QoS命令&lt;/h2>
&lt;p>QoS目前支持的命令包括：&lt;/p>
&lt;ul>
&lt;li>help: 帮助命令，列出&lt;/li>
&lt;li>ls: 列出当前所有的正在提供的服务，以及消费的服务&lt;/li>
&lt;li>online: 动态将某个或全部服务向注册中心进行注册&lt;/li>
&lt;li>offline: 动态将某个或全部服务从注册中心摘除（反注册）&lt;/li>
&lt;li>quit: 退出当前telnet会话&lt;/li>
&lt;/ul>
&lt;p>下面，我们具体来操作一下如何通过用QoS对服务进行动态控制。&lt;/p>
&lt;h3 id="通过telnet方式访问qos">通过Telnet方式访问QoS&lt;/h3>
&lt;p>假设我们的Dubbo服务端已经启动，我们通过Telnet方式进行连接：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>$ telnet localhost 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Trying 127.0.0.1...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Connected to localhost.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Escape character is &amp;#39;^]&amp;#39;.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ????????? ??? ?? ??????????? ??????????? ????????
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ???? ??? ??? ??? ??? ??? ??? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ??? ??? ??? ??? ??? ??? ??? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ??? ??? ??? ?????????? ?????????? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ??? ??? ??? ??????????? ??????????? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ??? ??? ??? ??? ??? ??? ??? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ??? ???? ??? ??? ??? ??? ??? ??? ??? ???
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ????????? ????????? ??????????? ??????????? ????????
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>连接成功后，会出现&lt;code>dubbo&amp;gt;&lt;/code>提示符，此时输入&lt;code>help&lt;/code>命令&lt;/p></description></item><item><title>Dubbo的负载均衡</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/10/dubbo%E7%9A%84%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/</link><pubDate>Fri, 10 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/10/dubbo%E7%9A%84%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;p>Dubbo是一个分布式服务框架，能避免单点故障和支持服务的横向扩容。一个服务通常会部署多个实例。如何从多个服务 Provider 组成的集群中挑选出一个进行调用，就涉及到一个负载均衡的策略。&lt;/p>
&lt;h2 id="几个概念">几个概念&lt;/h2>
&lt;p>在讨论负载均衡之前，我想先解释一下这3个概念。&lt;/p>
&lt;ol>
&lt;li>负载均衡&lt;/li>
&lt;li>集群容错&lt;/li>
&lt;li>服务路由&lt;/li>
&lt;/ol>
&lt;p>这3个概念容易混淆。他们都描述了怎么从多个 Provider 中选择一个来进行调用。那他们到底有什么区别呢?下面我来举一个简单的例子，把这几个概念阐述清楚吧。&lt;/p>
&lt;p>有一个Dubbo的用户服务，在北京部署了10个，在上海部署了20个。一个杭州的服务消费方发起了一次调用，然后发生了以下的事情:&lt;/p>
&lt;ol>
&lt;li>根据配置的路由规则，如果杭州发起的调用，会路由到比较近的上海的20个 Provider。&lt;/li>
&lt;li>根据配置的随机负载均衡策略，在20个 Provider 中随机选择了一个来调用，假设随机到了第7个 Provider。&lt;/li>
&lt;li>结果调用第7个 Provider 失败了。&lt;/li>
&lt;li>根据配置的Failover集群容错模式，重试其他服务器。&lt;/li>
&lt;li>重试了第13个 Provider，调用成功。&lt;/li>
&lt;/ol>
&lt;p>上面的第1，2，4步骤就分别对应了路由，负载均衡和集群容错。 Dubbo中，先通过路由，从多个 Provider 中按照路由规则，选出一个子集。再根据负载均衡从子集中选出一个 Provider 进行本次调用。如果调用失败了，根据集群容错策略，进行重试或定时重发或快速失败等。 可以看到Dubbo中的路由，负载均衡和集群容错发生在一次RPC调用的不同阶段。最先是路由，然后是负载均衡，最后是集群容错。 本文档只讨论负载均衡，路由和集群容错在其他的文档中进行说明。&lt;/p>
&lt;h2 id="dubbo内置负载均衡策略">Dubbo内置负载均衡策略&lt;/h2>
&lt;p>Dubbo内置了4种负载均衡策略:&lt;/p>
&lt;ol>
&lt;li>RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的&lt;strong>默认&lt;/strong>负载均衡策略。&lt;/li>
&lt;li>RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。&lt;/li>
&lt;li>LeastActiveLoadBalance:最少活跃调用数，相同活跃数的随机。活跃数指调用前后计数差。使慢的 Provider 收到更少请求，因为越慢的 Provider 的调用前后计数差会越大。&lt;/li>
&lt;li>ConsistentHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。&lt;/li>
&lt;/ol>
&lt;h3 id="1随机负载均衡">1.随机负载均衡&lt;/h3>
&lt;p>顾名思义，随机负载均衡策略就是从多个 Provider 中随机选择一个。但是 Dubbo 中的随机负载均衡有一个权重的概念，即按照权重设置随机概率。比如说，有10个 Provider，并不是说，每个 Provider 的概率都是一样的，而是要结合这10个 Provider 的权重来分配概率。&lt;/p>
&lt;p>Dubbo中，可以对 Provider 设置权重。比如机器性能好的，可以设置大一点的权重，性能差的，可以设置小一点的权重。权重会对负载均衡产生影响。可以在Dubbo Admin中对 Provider 进行权重的设置。&lt;/p>
&lt;p>&lt;strong>基于权重的负载均衡算法&lt;/strong>&lt;/p>
&lt;p>随机策略会先判断所有的 Invoker 的权重是不是一样的，如果都是一样的，那么处理就比较简单了。使用random.nexInt(length)就可以随机生成一个 Invoker 的序号,根据序号选择对应的 Invoker 。如果没有在Dubbo Admin中对服务 Provider 设置权重，那么所有的 Invoker 的权重就是一样的，默认是100。 如果权重不一样，那就需要结合权重来设置随机概率了。算法大概如下： 假如有4个 Invoker。&lt;/p></description></item><item><title>Dubbo 注解驱动</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-%E6%B3%A8%E8%A7%A3%E9%A9%B1%E5%8A%A8/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-%E6%B3%A8%E8%A7%A3%E9%A9%B1%E5%8A%A8/</guid><description>&lt;h2 id="注解驱动annotation-driven">注解驱动（Annotation-Driven）&lt;/h2>
&lt;h3 id="dubbocomponentscan">&lt;code>@DubboComponentScan&lt;/code>&lt;/h3>
&lt;h4 id="起始版本-257">起始版本： &lt;code>2.5.7&lt;/code>&lt;/h4>
&lt;h4 id="dubboannotation-历史遗留问题">&lt;code>&amp;lt;dubbo:annotation&amp;gt; &lt;/code>历史遗留问题&lt;/h4>
&lt;h5 id="1-注解支持不充分">1. 注解支持不充分&lt;/h5>
&lt;p>在 Dubbo &lt;code>2.5.7&lt;/code>之前的版本 ，Dubbo 提供了两个核心注解 &lt;code>@Service&lt;/code> 以及 &lt;code>@Reference&lt;/code>，分别用于Dubbo 服务提供和 Dubbo 服务引用。&lt;/p>
&lt;p>其中，&lt;code>@Service&lt;/code> 作为 XML 元素 &lt;code>&amp;lt;dubbo:service&amp;gt;&lt;/code>的替代注解，与 Spring Framework &lt;code>@org.springframework.stereotype.Service&lt;/code> 类似，用于服务提供方 Dubbo 服务暴露。与之相对应的&lt;code>@Reference&lt;/code>，则是替代&lt;code>&amp;lt;dubbo:reference&lt;/code> 元素，类似于 Spring 中的 &lt;code>@Autowired&lt;/code>。
&lt;code>2.5.7&lt;/code> 之前的Dubbo，与早期的 Spring Framework 2.5 存在类似的不足，即注解支持不够充分。注解需要和 XML 配置文件配合使用，如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;beans&lt;/span> xmlns:xsi=&lt;span style="color:#2aa198">&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns:dubbo=&lt;span style="color:#2aa198">&amp;#34;http://code.alibabatech.com/schema/dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xmlns=&lt;span style="color:#2aa198">&amp;#34;http://www.springframework.org/schema/beans&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> xsi:schemaLocation=&lt;span style="color:#2aa198">&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:application&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;annotation-provider&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;127.0.0.1:4548&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:annotation&lt;/span> package=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.config.spring.annotation.provider&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/beans&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="2--service-bean-不支持-spring-aop">2. &lt;code>@Service&lt;/code> Bean 不支持 Spring AOP&lt;/h5>
&lt;p>同时，使用 &lt;code>&amp;lt;dubbo:annotation&amp;gt; &lt;/code> 方式扫描后的Dubbo &lt;code>@Service&lt;/code> ，在 Spring 代理方面存在问题，如 GitHub 上的 &lt;a href="https://github.com/apache/dubbo/issues/794">issue&lt;/a>：&lt;/p></description></item><item><title>第一个 Dubbo 应用</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-101/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-101/</guid><description>&lt;h2 id="java-rmi-简介">Java RMI 简介&lt;/h2>
&lt;p>Java RMI （Remote Method Invocation）- 远程方法调用，能够让客户端像使用本地调用一样调用服务端 Java 虚拟机中的对象方法。RMI 是面向对象语言领域对 RPC （Remote Procedure Call）的完善，用户无需依靠 IDL 的帮助来完成分布式调用，而是通过依赖接口这种更简单自然的方式。&lt;/p>
&lt;h3 id="java-rmi-工作原理">Java RMI 工作原理&lt;/h3>
&lt;p>一个典型的 RMI 调用如下图所示：&lt;/p>
&lt;ol>
&lt;li>服务端向 RMI 注册服务绑定自己的地址，&lt;/li>
&lt;li>客户端通过 RMI 注册服务获取目标地址，&lt;/li>
&lt;li>客户端调用本地的 Stub 对象上的方法，和调用本地对象上的方法一致，&lt;/li>
&lt;li>本地存根对象将调用信息打包，通过网络发送到服务端，&lt;/li>
&lt;li>服务端的 Skeleton 对象收到网络请求之后，将调用信息解包，&lt;/li>
&lt;li>然后找到真正的服务对象发起调用，并将返回结果打包通过网络发送回客户端。&lt;/li>
&lt;/ol>
&lt;p>&lt;img alt="RMI Flow" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/rmi-flow.png">&lt;/p>
&lt;p>(来源：https://www.cs.rutgers.edu/~pxk/417/notes/images/rpc-rmi_flow.png)&lt;/p>
&lt;h3 id="java-rmi-基本概念">Java RMI 基本概念&lt;/h3>
&lt;p>Java RMI 是 Java 领域创建分布式应用的技术基石。后续的 EJB 技术，以及现代的分布式服务框架，其中的基本理念依旧是 Java RMI 的延续。在 RMI 调用中，有以下几个核心的概念：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>通过&lt;strong>接口&lt;/strong>进行远程调用&lt;/p>
&lt;/li>
&lt;li>
&lt;p>通过客户端的 &lt;strong>Stub 对象&lt;/strong>和服务端的 &lt;strong>Skeleton 对象&lt;/strong>的帮助将远程调用伪装成本地调用&lt;/p>
&lt;/li>
&lt;li>
&lt;p>通过 &lt;strong>RMI 注册服务&lt;/strong>完成服务的注册和发现&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>对于第一点，客户端需要依赖接口，而服务端需要提供该接口的实现。&lt;/p>
&lt;p>对于第二点，在 J2SE 1.5 版本之前需要通过 rmic 预先编译好客户端的 Stub 对象和服务端的 Skeleton 对象。在之后的版本中，不再需要事先生成 Stub 和 Skeleton 对象。&lt;/p></description></item><item><title>使用jdk17编译运行dubbo 2.7.14项目</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E4%BD%BF%E7%94%A8jdk17%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8Cdubbo-2.7.14%E9%A1%B9%E7%9B%AE/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E4%BD%BF%E7%94%A8jdk17%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8Cdubbo-2.7.14%E9%A1%B9%E7%9B%AE/</guid><description>&lt;h2 id="概述">概述&lt;/h2>
&lt;p>java 17是java目前最新的长期支持(LTS)版本，但是由于其强封装 JDK 的内部 API的新特性，导致dubbo项目无法直接使用jdk17编译运行。通过参考&lt;a href="https://openjdk.java.net/jeps/403">openjdk的说明&lt;/a>，可以发现只需要添加相应参数即可绕开java 17的限制&lt;br>
对于普通的dubbo项目，只需要在运行时添加&lt;br>
&lt;code>--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED&lt;/code>&lt;br>
如上参数即可。如果项目的其它依赖也有类似问题则可能需要加入更多参数，参数的获得方式和详细示例将在下面给出&lt;br>
本解决方案只能解决由于java 17强封装 JDK 的内部 API的特性造成的问题，其他的兼容性问题请寻找其它方案&lt;/p>
&lt;h2 id="参数的获得方法和示例">参数的获得方法和示例&lt;/h2>
&lt;p>我们以dubbo官方仓库中的demo为例
首先使用java 17作为我们的开发环境，通过&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>git clone git@github.com:apache/dubbo.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git checkout dubbo-2.7.14
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd dubbo-demo/dubbo-demo-annotation
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>获得dubbo官方仓库的代码中的demo，然后可以尝试直接使用java 17编译dubbo的demo&lt;br>
确认java版本&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>➜ ~ java -version
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>openjdk version &amp;#34;17.0.1&amp;#34; 2021-10-19
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>OpenJDK Runtime Environment Temurin-17.0.1+12 (build 17.0.1+12)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>OpenJDK 64-Bit Server VM Temurin-17.0.1+12 (build 17.0.1+12, mixed mode, sharing)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后运行&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>mvn &lt;span style="color:#719e07">-&lt;/span>U clean &lt;span style="color:#719e07">package&lt;/span> &lt;span style="color:#719e07">--&lt;/span>no&lt;span style="color:#719e07">-&lt;/span>transfer&lt;span style="color:#719e07">-&lt;/span>progress &lt;span style="color:#719e07">-&lt;/span>D maven.test.skip=&lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启动zookeeper &lt;code>docker run --name some-zookeepep -p 2181:2181 -it --rm zookeeper&lt;/code> 作为注册中心
尝试运行provider&lt;/p></description></item><item><title>在 Dubbo 应用中使用 Zookeeper</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E5%9C%A8-dubbo-%E5%BA%94%E7%94%A8%E4%B8%AD%E4%BD%BF%E7%94%A8-zookeeper/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E5%9C%A8-dubbo-%E5%BA%94%E7%94%A8%E4%B8%AD%E4%BD%BF%E7%94%A8-zookeeper/</guid><description>&lt;h2 id="zookeeper-介绍">Zookeeper 介绍&lt;/h2>
&lt;h3 id="基本概念">基本概念&lt;/h3>
&lt;p>在现代的分布式应用中，往往会出现节点和节点之间的协调问题，其中就包括了：选主、集群管理、分布式锁、分布式配置管理、统一命名服务、状态同步等诉求。&lt;a href="https://zookeeper.apache.org">Apache Zookeeper&lt;/a>，正如它的名字所暗示的那样，&lt;em>动物园管理员&lt;/em>，就是为了解决这些诉求的一个分布式协调服务框架。&lt;/p>
&lt;p>为了保证高可用，ZooKeeper 本身也可以部署成集群模式，称之为 &lt;em>ZooKeeper ensemble&lt;/em>。ZooKeeper 集群中始终确保其中的一台为 leader 的角色，并通过 &lt;em>ZAB (Zookeeper Atomic Broadcast Protocol) &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/em> 协议确保所有节点上的信息的一致。客户端可以访问集群中的任何一台进行读写操作，而不用担心数据出现不一致的现象。&lt;/p>
&lt;p>&lt;img alt="Diagram shows client-server architecture of ZooKeeper" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/zk-emsemble.png">
&lt;em>Image Credit : ebook -Zookeeper-Distributed Process Coordination from O&amp;rsquo;Reilly&lt;/em>&lt;/p>
&lt;p>Zookeeper 中的数据存储方式与传统的 UNIX 文件系统相似，节点按照树状结构来组织，其中，节点被称之为 &lt;em>znodes (ZooKeeper data nodes)&lt;/em>&lt;/p>
&lt;p>&lt;img alt="zk-tree" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/zk-tree.png">
&lt;em>Image Credit : ebook -Zookeeper-Distributed Process Coordination from O&amp;rsquo;Reilly&lt;/em>&lt;/p>
&lt;h3 id="基本用法">基本用法&lt;/h3>
&lt;p>可以通过直接下载的方式 &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>安装并运行 Zookeeper ，在 Mac 上也可以通过 Homebrew &lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> &lt;code>brew install zookeeper&lt;/code> 来安装，考虑到通用性，本文采用 docker 的方式来运行 Zookeeper。如果没有安装 docker，请先准备好 docker 环境 &lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>。&lt;/p>
&lt;h4 id="1-启动-zookeeper">1. 启动 Zookeeper&lt;/h4>
&lt;p>执行命令将 Zookeeper 运行在 docker 容器中&lt;/p></description></item><item><title>在 Dubbo 中使用注解</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%B3%A8%E8%A7%A3/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%B3%A8%E8%A7%A3/</guid><description>&lt;h1 id="在-dubbo-中使用注解">在 Dubbo 中使用注解&lt;/h1>
&lt;p>随着微服务架构的广泛地推广和实施。在 Java 生态系统中，以 Spring Boot 和 Spring Cloud 为代表的微服务框架，引入了全新的编程模型，包括：&lt;/p>
&lt;ul>
&lt;li>注解驱动（Annotation-Driven）&lt;/li>
&lt;li>外部化配置（External Configuration）&lt;/li>
&lt;li>以及自动装配（Auto-Configure）&lt;/li>
&lt;/ul>
&lt;p>新的编程模型无需 XML 配置、简化部署、提升开发效率。为了更好地实践微服务架构，Dubbo 从 &lt;code>2.5.8&lt;/code> 版本开始， 分别针对了上述的三个场景，提供了更完善的支持。本文不讨论传统的 XML 配置方式，而是侧重介绍注解这种方式。外部配置、自动装配两种自动装配会在另外的文章中专门介绍。&lt;/p>
&lt;h2 id="注解介绍">注解介绍&lt;/h2>
&lt;h3 id="enabledubbo">@EnableDubbo&lt;/h3>
&lt;p>&lt;code>@EnableDubbo&lt;/code> 注解是 &lt;code>@EnableDubboConfig&lt;/code> 和 &lt;code>@DubboComponentScan&lt;/code>两者组合的便捷表达方式。与注解驱动相关的是 &lt;code>@DubboComponentScan&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.dubbo.config.spring.context.annotation;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableDubboConfig&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@DubboComponentScan&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">@interface&lt;/span> EnableDubbo {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Base packages to scan for annotated @Service classes.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Use {@link #scanBasePackageClasses()} for a type-safe alternative to String-based
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * package names.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return the base packages to scan
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @see DubboComponentScan#basePackages()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@AliasFor&lt;/span>(annotation &lt;span style="color:#719e07">=&lt;/span> DubboComponentScan.class, attribute &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;basePackages&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> &lt;span style="color:#268bd2">scanBasePackages&lt;/span>() &lt;span style="color:#719e07">default&lt;/span> {};
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Type-safe alternative to {@link #scanBasePackages()} for specifying the packages to
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * scan for annotated @Service classes. The package of each class specified will be
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * scanned.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return classes from the base packages to scan
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @see DubboComponentScan#basePackageClasses
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@AliasFor&lt;/span>(annotation &lt;span style="color:#719e07">=&lt;/span> DubboComponentScan.class, attribute &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;basePackageClasses&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]&lt;/span> scanBasePackageClasses() &lt;span style="color:#719e07">default&lt;/span> {}; 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过 &lt;code>@EnableDubbo&lt;/code> 可以在指定的包名下（通过 &lt;code>scanBasePackages&lt;/code>），或者指定的类中（通过 &lt;code>scanBasePackageClasses&lt;/code>）扫描 Dubbo 的服务提供者（以 &lt;code>@Service&lt;/code> 标注）以及 Dubbo 的服务消费者（以 &lt;code>Reference&lt;/code> 标注）。&lt;/p></description></item><item><title>Sentinel 为 Dubbo 服务保驾护航</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/27/sentinel-%E4%B8%BA-dubbo-%E6%9C%8D%E5%8A%A1%E4%BF%9D%E9%A9%BE%E6%8A%A4%E8%88%AA/</link><pubDate>Fri, 27 Jul 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/27/sentinel-%E4%B8%BA-dubbo-%E6%9C%8D%E5%8A%A1%E4%BF%9D%E9%A9%BE%E6%8A%A4%E8%88%AA/</guid><description>&lt;p>在复杂的生产环境下可能部署着成千上万的 Dubbo 服务实例，流量持续不断地进入，服务之间进行相互调用。但是分布式系统中可能会因流量激增、系统负载过高、网络延迟等一系列问题，导致某些服务不可用，如果不进行相应的控制可能导致级联故障，影响服务的可用性，因此如何对流量进行合理的控制，成为保障服务稳定性的关键。&lt;/p>
&lt;p>&lt;a href="https://github.com/alibaba/Sentinel">Sentinel&lt;/a> 是阿里中间件团队开源的，面向分布式服务架构的轻量级流量控制产品，主要以流量为切入点，从&lt;strong>流量控制&lt;/strong>、&lt;strong>熔断降级&lt;/strong>、&lt;strong>系统负载保护&lt;/strong>等多个维度来帮助用户保护服务的稳定性。本文将基于 Dubbo，看看 Sentinel 是如何进行流量控制的，并且提供 Dubbo 整合 Sentinel 的最佳实践。&lt;/p>
&lt;h2 id="快速接入-sentinel">快速接入 Sentinel&lt;/h2>
&lt;p>Sentinel 意为&lt;strong>哨兵&lt;/strong>，这个命名形象的诠释了 Sentinel 在分布式系统中的工作角色和重要性。以 Sentinel 在 Dubbo 生态系统中的作用为例，Dubbo 的核心模块包括注册中心、服务提供方、服务消费方（服务调用方）和监控四个模块。Sentinel 通过对服务提供方和服务消费方的限流来进一步提升服务的可用性。接下来我们看看 Sentinel 对服务提供方和服务消费方限流的技术实现方式。&lt;/p>
&lt;p>&lt;img alt="Dubbo Arch" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/architecture.png">&lt;/p>
&lt;p>Sentinel 提供了与 Dubbo 适配的模块 – &lt;a href="https://github.com/dubbo/dubbo-sentinel-support">Sentinel Dubbo Adapter&lt;/a>，包括针对服务提供方的过滤器和服务消费方的过滤器（Filter）。使用时我们只需引入以下模块（以 Maven 为例）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba.csp&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>sentinel-dubbo-adapter&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>x.y.z&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>引入此依赖后，Dubbo 的服务接口和方法（包括调用端和服务端）就会成为 Sentinel 中的资源，在配置了规则后就可以自动享受到 Sentinel 的防护能力。同时提供了灵活的配置选项，例如若不希望开启 Sentinel Dubbo Adapter 中的某个 Filter，可以手动关闭对应的 Filter。&lt;/p>
&lt;p>接入 Sentinel Dubbo Adapter 后，即使未配置规则，Sentinel 也会对相应的 Dubbo 服务的调用信息进行统计。那么我们怎么知道 Sentinel 接入成功了呢？这时候就要请出一大利器 —— Sentinel 控制台了。&lt;/p>
&lt;h2 id="限流必备---监控管理">限流必备 - 监控管理&lt;/h2>
&lt;p>流量具有很强的实时性，之所以需要限流，是因为我们无法对流量的到来作出精确的预判，不然的话我们完全可以通过弹性的计算资源来处理，所以这时候为了保证限流的准确性，限流框架的监控功能就非常重要了。&lt;/p></description></item><item><title>Dubbo 2.7.x repackage 后的兼容实现方案</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/22/dubbo-2.7.x-repackage-%E5%90%8E%E7%9A%84%E5%85%BC%E5%AE%B9%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88/</link><pubDate>Sun, 22 Jul 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/22/dubbo-2.7.x-repackage-%E5%90%8E%E7%9A%84%E5%85%BC%E5%AE%B9%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88/</guid><description>&lt;p>Dubbo至加入Apache孵化器以来，一个很强的诉求就是需要rename groupId和package name，这两项工作在项目毕业前需要完成。其中rename package相对来说复杂一些，除了要修改所有类的包名为&lt;code>org.apache.dubbo&lt;/code>外，更多的是需要考虑如何老版本的兼容性。&lt;/p>
&lt;p>常见的兼容性包括但不限于以下几种情况：&lt;/p>
&lt;ul>
&lt;li>用户API
&lt;ul>
&lt;li>编程API&lt;/li>
&lt;li>Spring注解&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>扩展SPI
&lt;ul>
&lt;li>扩展Filter&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>2.7.x里就是通过增加了一个新的模块&lt;code>dubbo-compatible&lt;/code>来解决以上兼容性问题。&lt;/p>
&lt;h2 id="编程使用api">编程使用API&lt;/h2>
&lt;p>编程使用API是最直接最原始的使用方式，其他方式诸如Spring schema、注解等方式都是基于原始API的；因此非常有必要对API编程形式进行兼容。&lt;/p>
&lt;p>所有编程相关API的兼容代码均在&lt;code>com.alibaba.dubbo.config&lt;/code>包下，下面我们看看几个常见API的兼容实现。&lt;/p>
&lt;h3 id="applicationconfig">ApplicationConfig&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.config;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Deprecated&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ApplicationConfig&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> org.apache.dubbo.config.ApplicationConfig {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ApplicationConfig&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ApplicationConfig&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="protocolconfig">ProtocolConfig&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.config;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Deprecated&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProtocolConfig&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> org.apache.dubbo.config.ProtocolConfig {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ProtocolConfig&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ProtocolConfig&lt;/span>(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ProtocolConfig&lt;/span>(String name, &lt;span style="color:#dc322f">int&lt;/span> port) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>(name, port);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到：&lt;/p>
&lt;ol>
&lt;li>兼容类是直接通过继续repacakge后的类，达到最大程度的代码复用；&lt;/li>
&lt;li>构造函数也需要保持兼容；&lt;/li>
&lt;/ol>
&lt;p>整个兼容包中，除了上述API以外，包括一些常用的类比如&lt;code>Constants&lt;/code>、&lt;code>URL&lt;/code>以及绝大部分的兼容类都是通过简单的继承，让用户基于老的API实现的类能正确运行。&lt;/p></description></item><item><title>Dubbo 上下文信息</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/12/dubbo-%E4%B8%8A%E4%B8%8B%E6%96%87%E4%BF%A1%E6%81%AF/</link><pubDate>Thu, 12 Jul 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/12/dubbo-%E4%B8%8A%E4%B8%8B%E6%96%87%E4%BF%A1%E6%81%AF/</guid><description>&lt;h2 id="简介">简介&lt;/h2>
&lt;p>上下文信息是一次 RPC 调用过程中附带的环境信息，如方法名、参数类型、真实参数、本端/对端地址等。这些数据仅属于一次调用，作用于 Consumer 到 Provider 调用的整个流程。&lt;/p>
&lt;p>提供上下文信息是 RPC 框架很重要的一个功能，使用上下文不仅可以为单次调用指定不同配置，还能在此基础上提供强大的上层功能，如分布式链路追踪。其实现原理就是在上下文中维护一个&lt;code>span_id&lt;/code>，Consumer 和 Provider 通过传递&lt;code>span_id&lt;/code>来连接一次RPC调用，分别上报日志后可以在追踪系统中串联并展示完整的调用流程。这样可以更方便地发现异常，定位问题。&lt;/p>
&lt;h2 id="使用说明">使用说明&lt;/h2>
&lt;p>Dubbo中代表上下文的类是&lt;code>org.apache.dubbo.rpc.RpcContext&lt;/code>，可通过下述代码来获取上下文信息。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>RpcContext.getContext()
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="使用场景">使用场景&lt;/h2>
&lt;h3 id="获取调用信息">获取调用信息&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">方法名&lt;/th>
 &lt;th style="text-align: left">用途&lt;/th>
 &lt;th style="text-align: left">作用范围&lt;/th>
 &lt;th style="text-align: left">说明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">getRequest&lt;/td>
 &lt;td style="text-align: left">获取 RPC 请求对象&lt;/td>
 &lt;td style="text-align: left">Consumer&lt;/td>
 &lt;td style="text-align: left">获取底层 RPC 请求对象，例如 HttpServletRequest，其他情况为 null&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getResponse&lt;/td>
 &lt;td style="text-align: left">获取 RPC 请求响应&lt;/td>
 &lt;td style="text-align: left">Consumer&lt;/td>
 &lt;td style="text-align: left">获取底层 RPC 响应对象，例如HttpServletResponse，其他情况为 null&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">isProviderSide&lt;/td>
 &lt;td style="text-align: left">当前是否属于 Provider 上下文&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">服务被调用时为 true，调用其他服务时为false&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">isConsumerSide&lt;/td>
 &lt;td style="text-align: left">当前是否属于 Consumer 上下文&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">服务被调用时为 false，调用其他服务时为 true&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getUrls&lt;/td>
 &lt;td style="text-align: left">获取当前能调用的 Url 列表&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">Consumer 端会根据不同的 Failover 策略实时变化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemotePort&lt;/td>
 &lt;td style="text-align: left">获取远端端口&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">Consumer 端为最后一次调用的 Provider 端口，Provider 为当前请求的 Consumer 端口&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemoteHost&lt;/td>
 &lt;td style="text-align: left">获取远端主机地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemoteHostName&lt;/td>
 &lt;td style="text-align: left">获取远端主机名&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemoteAddressString&lt;/td>
 &lt;td style="text-align: left">获取远端地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getRemoteAddress&lt;/td>
 &lt;td style="text-align: left">获取远端地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalPort&lt;/td>
 &lt;td style="text-align: left">获取本端端口&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalHost&lt;/td>
 &lt;td style="text-align: left">获取本端主机地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalHostName&lt;/td>
 &lt;td style="text-align: left">获取本端主机名&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalAddressString&lt;/td>
 &lt;td style="text-align: left">获取本端地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">getLocalAddress&lt;/td>
 &lt;td style="text-align: left">获取本端地址&lt;/td>
 &lt;td style="text-align: left">Both&lt;/td>
 &lt;td style="text-align: left">&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="传递用户参数">传递用户参数&lt;/h3>
&lt;h4 id="本端传递">本端传递&lt;/h4>
&lt;p>调用&lt;code>get&lt;/code>和&lt;code>set&lt;/code>方法即可完成参数传递。主要用于本端 Filter 之间的数据共享。&lt;/p></description></item><item><title>使用Pinpoint做分布式跟踪</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/12/%E4%BD%BF%E7%94%A8pinpoint%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</link><pubDate>Thu, 12 Jul 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/12/%E4%BD%BF%E7%94%A8pinpoint%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA/</guid><description>&lt;p>在使用Dubbo进行服务化或者整合应用后，假设某个服务后台日志显示有异常，这个服务又被多个应用调用的情况下，我们通常很难判断是哪个应用调用的，问题的起因是什么，因此我们需要一套分布式跟踪系统来快速定位问题，Pinpoint可以帮助我们快速定位问题（当然，解决方案也不止这一种）。&lt;/p>
&lt;h2 id="什么是pinpoint">什么是Pinpoint&lt;/h2>
&lt;blockquote>
&lt;p>摘自&lt;a href="https://skyao.gitbooks.io/learning-pinpoint/content/index.html">Pinpoint学习笔记&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;a href="https://github.com/naver/pinpoint">Pinpoint&lt;/a>是一个开源的 APM (Application Performance Management/应用性能管理)工具，用于基于java的大规模分布式系统。
仿照Google Dapper，Pinpoint通过跟踪分布式应用之间的调用来提供解决方案，以帮助分析系统的总体结构和内部模块之间如何相互联系。&lt;/p>
&lt;blockquote>
&lt;p>注：对于各个模块之间的通讯英文原文中用的是transaction一词，但是我觉得如果翻译为&amp;quot;事务&amp;quot;容易引起误解，所以替换为&amp;quot;交互&amp;quot;或者&amp;quot;调用&amp;quot;这种比较直白的字眼。&lt;/p>
&lt;/blockquote>
&lt;p>在使用上力图简单高效：&lt;/p>
&lt;ul>
&lt;li>安装agent，不需要修改哪怕一行代码&lt;/li>
&lt;li>最小化性能损失&lt;/li>
&lt;/ul>
&lt;h3 id="服务器地图servermap">服务器地图(ServerMap)&lt;/h3>
&lt;p>通过可视化分布式系统的模块和他们之间的相互联系来理解系统拓扑。点击某个节点会展示这个模块的详情，比如它当前的状态和请求数量。&lt;/p>
&lt;h3 id="实时活动线程图表realtime-active-thread-chart">实时活动线程图表(Realtime Active Thread Chart)&lt;/h3>
&lt;p>实时监控应用内部的活动线程。&lt;/p>
&lt;h3 id="请求应答分布图表requestresponse-scatter-chart">请求/应答分布图表(Request/Response Scatter Chart)&lt;/h3>
&lt;p>长期可视化请求数量和应答模式来定位潜在问题。通过在图表上拉拽可以选择请求查看更多的详细信息。&lt;/p>
&lt;h3 id="调用栈callstack">调用栈(CallStack)&lt;/h3>
&lt;p>在分布式环境中为每个调用生成代码级别的可视图，在单个视图中定位瓶颈和失败点。&lt;/p>
&lt;h3 id="巡查inspector">巡查(Inspector)&lt;/h3>
&lt;p>查看应用上的其他详细信息，比如CPU使用率，内存/垃圾回收，TPS，和JVM参数。&lt;/p>
&lt;h3 id="支持模块">支持模块&lt;/h3>
&lt;ul>
&lt;li>JDK 6+&lt;/li>
&lt;li>Tomcat 6/7/8, Jetty 8/9, JBoss EAP 6, Resin 4, Websphere 6/7/8, Vertx 3.3/3.4/3.5&lt;/li>
&lt;li>Spring, Spring Boot (Embedded Tomcat, Jetty)&lt;/li>
&lt;li>Apache HTTP Client 3.x/4.x, JDK HttpConnector, GoogleHttpClient, OkHttpClient, NingAsyncHttpClient&lt;/li>
&lt;li>Thrift Client, Thrift Service, DUBBO PROVIDER, DUBBO CONSUMER&lt;/li>
&lt;li>ActiveMQ, RabbitMQ&lt;/li>
&lt;li>MySQL, Oracle, MSSQL, CUBRID,POSTGRESQL, MARIA&lt;/li>
&lt;li>Arcus, Memcached, Redis, CASSANDRA&lt;/li>
&lt;li>iBATIS, MyBatis&lt;/li>
&lt;li>DBCP, DBCP2, HIKARICP&lt;/li>
&lt;li>gson, Jackson, Json Lib&lt;/li>
&lt;li>log4j, Logback&lt;/li>
&lt;li>自定义模块&lt;/li>
&lt;/ul>
&lt;h2 id="pinpoint与dubbo的结合">Pinpoint与Dubbo的结合&lt;/h2>
&lt;h3 id="启动pinpoint">启动Pinpoint&lt;/h3>
&lt;p>参考Pinpoint的&lt;a href="https://pinpoint-apm.github.io/pinpoint/quickstart.html">Quick start&lt;/a>搭建环境（不需要启动TestApp）&lt;/p></description></item><item><title>第一个 Dubbo Filter</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/01/%E7%AC%AC%E4%B8%80%E4%B8%AA-dubbo-filter/</link><pubDate>Sun, 01 Jul 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/07/01/%E7%AC%AC%E4%B8%80%E4%B8%AA-dubbo-filter/</guid><description>&lt;h3 id="概述">概述&lt;/h3>
&lt;p>在Dubbo的整体设计中，Filter是一个很重要的概念，包括Dubbo本身的大多数功能，都是基于此扩展点实现的，在每次的调用过程中，Filter的拦截都会被执行。&lt;/p>
&lt;h4 id="dubbo-filter的加载机制">Dubbo Filter的加载机制&lt;/h4>
&lt;p>Dubbo中已经实现的Filter大概有二十几个，它们的入口都是ProtocolFilterWrapper，ProtocolFilterWrapper对Protocol做了Wrapper，会在加载扩展的时候被加载进来，下面我们来看下这个Filter链是如何构造的。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ProtocolFilterWrapper.java&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">refer&lt;/span>(Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type, URL url) &lt;span style="color:#268bd2">throws&lt;/span> RpcException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol.refer(type, url);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">buildInvokerChain&lt;/span>(&lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker, String key, String group) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> last &lt;span style="color:#719e07">=&lt;/span> invoker;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Filter&lt;span style="color:#719e07">&amp;gt;&lt;/span> filters &lt;span style="color:#719e07">=&lt;/span> ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> (filters.size() &lt;span style="color:#719e07">&amp;gt;&lt;/span> 0) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> filters.size() &lt;span style="color:#719e07">-&lt;/span> 1; i &lt;span style="color:#719e07">&amp;gt;=&lt;/span> 0; i &lt;span style="color:#719e07">--&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> Filter filter &lt;span style="color:#719e07">=&lt;/span> filters.get(i);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> next &lt;span style="color:#719e07">=&lt;/span> last;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> last &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getInterface&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker.getInterface();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> URL &lt;span style="color:#268bd2">getUrl&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker.getUrl();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isAvailable&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker.isAvailable();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Result &lt;span style="color:#268bd2">invoke&lt;/span>(Invocation invocation) &lt;span style="color:#268bd2">throws&lt;/span> RpcException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> filter.invoke(next, invocation);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">destroy&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker.destroy();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">toString&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker.toString();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> last;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="dubbo-filter的激活机制">Dubbo Filter的激活机制&lt;/h4>
&lt;p>通过上述代码我们可以看到，在&lt;code>buildInvokerChain&lt;/code>中,先获取所有已经激活的调用链，这里的调用链是已经排好序的。再通过Invoker来构造出一个Filter的调用链，最后构建出的调用链大致可以表示为：Filter1-&amp;gt;Filter2-&amp;gt;Filter3-&amp;gt;&amp;hellip;&amp;hellip;-&amp;gt;Invoker,下面我们来看一下，第一步中获取已经激活的调用链的详细流程：&lt;/p></description></item><item><title>回声测试</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/06/26/%E5%9B%9E%E5%A3%B0%E6%B5%8B%E8%AF%95/</link><pubDate>Tue, 26 Jun 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/06/26/%E5%9B%9E%E5%A3%B0%E6%B5%8B%E8%AF%95/</guid><description>&lt;p>回声测试用于检测服务是否可用。客户端通过 EchoService 来使用回声测试。EchoService 申明如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-Java" data-lang="Java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">EchoService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * echo test.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param message message.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return message.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object &lt;span style="color:#268bd2">$echo&lt;/span>(Object message);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>用户通过 $echo 方法发起的请求，会按照正常请求的流程执行，能够测试整个调用是否通畅，监控系统可以使用回声测试来检测服务可用性。&lt;/p>
&lt;h2 id="使用范例">使用范例&lt;/h2>
&lt;p>所有服务引用自动实现 EchoService 接口，用户只需将服务引用强制转型为 EchoService，即可使用。配置和代码范例如下所示。
Spring 配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;dubbo:reference id=&amp;#34;demoService&amp;#34; interface=&amp;#34;org.apache.dubbo.samples.echo.DemoService&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-Java" data-lang="Java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 远程服务引用&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>DemoService demoService&lt;span style="color:#719e07">=&lt;/span> ctx.getBean(&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 强制转型为EchoService&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>EchoService echoService &lt;span style="color:#719e07">=&lt;/span> (EchoService) demoService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 回声测试可用性&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>String status &lt;span style="color:#719e07">=&lt;/span> echoService.$echo(&lt;span style="color:#2aa198">&amp;#34;OK&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">assert&lt;/span>(status.equals(&lt;span style="color:#2aa198">&amp;#34;OK&amp;#34;&lt;/span>));
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="实现原理">实现原理&lt;/h2>
&lt;p>我们在配置服务引用时，并没有配置 EchoService 这个接口，为什么可以直接把服务引用转型为 EchoService 呢？
用户拿到的服务引用其实是一个 Proxy，Dubbo 在生成 Proxy 的时候，已经默认将 EchoService 这个接口加入到 Proxy 的接口列表中，所以用户拿到的 Proxy 都已经实现了 EchoService。生成代理相关代码如下：&lt;/p></description></item><item><title>在 Dubbo 中使用 Zipkin</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/06/17/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-zipkin/</link><pubDate>Sun, 17 Jun 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/06/17/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8-zipkin/</guid><description>&lt;p>随着业务的发展，应用的规模不断的扩大，传统的应用架构无法满足诉求，服务化架构改造势在必行，以 Dubbo 为代表的分布式服务框架成为了服务化改造架构中的基石。随着微服务理念逐渐被大众接受，应用进一步向更细粒度拆分，并且，不同的应用由不同的开发团队独立负责，整个分布式系统变得十分复杂。没有人能够清晰及时的知道当前系统整体的依赖关系。当出现问题时，也无法及时知道具体是链路上的哪个环节出了问题。&lt;/p>
&lt;p>在这个背景下，Google 发表了 &lt;a href="https://research.google/pubs/dapper-a-large-scale-distributed-systems-tracing-infrastructure/">Dapper&lt;/a> 的论文，描述了如何通过一个分布式追踪系统解决上述问题。基于该论文，各大互联网公司实现并部署了自己的分布式追踪系统，其中比较出名的有阿里巴巴的 EagleEye。本文中提到的 Zipkin 是 Twitter 公司开源的分布式追踪系统。下面会详细介绍如何在 Dubbo 中使用 Zipkin 来实现分布式追踪。&lt;/p>
&lt;h2 id="zipkin-简介">Zipkin 简介&lt;/h2>
&lt;p>Zipkin 是基于 &lt;a href="https://research.google/pubs/dapper-a-large-scale-distributed-systems-tracing-infrastructure/">Dapper&lt;/a> 论文实现，由 Twitter 开源的分布式追踪系统，通过收集分布式服务执行时间的信息来达到追踪服务调用链路、以及分析服务执行延迟等目的。&lt;/p>
&lt;h3 id="zipkin-架构">Zipkin 架构&lt;/h3>
&lt;p>&lt;img alt="Zipkin architecture" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/zipkin-architecture.png">&lt;/p>
&lt;p>Collector 收集器、Storage 存储、API、UI 用户界面等几部分构成了 Zipkin Server 部分，对应于 GitHub 上 &lt;a href="https://github.com/openzipkin/zipkin">openzipkin/zipkin&lt;/a> 这个项目。而收集应用中调用的耗时信息并将其上报的组件与应用共生，并拥有各个语言的实现版本，其中 Java 的实现是 GitHub 上 &lt;a href="https://github.com/openzipkin/brave">openzipkin/brave&lt;/a>。除了 Java 客户端实现之外，openzipkin 还提供了许多其他语言的实现，其中包括了 go、php、JavaScript、.net、ruby 等，具体列表可以参阅 Zipkin 的 &lt;a href="https://zipkin.io/pages/tracers_instrumentation.html">Exiting instrumentations&lt;/a>。&lt;/p>
&lt;h3 id="zipkin-的工作过程">Zipkin 的工作过程&lt;/h3>
&lt;p>当用户发起一次调用时，Zipkin 的客户端会在入口处为整条调用链路生成一个全局唯一的 trace id，并为这条链路中的每一次分布式调用生成一个 span id。span 与 span 之间可以有父子嵌套关系，代表分布式调用中的上下游关系。span 和 span 之间可以是兄弟关系，代表当前调用下的两次子调用。一个 trace 由一组 span 组成，可以看成是由 trace 为根节点，span 为若干个子节点的一棵树。&lt;/p>
&lt;p>&lt;img alt="Related image" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/trace-sample.png">&lt;/p></description></item><item><title>以 Dubbo 为例，聊聊如何向开源项目做贡献</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/06/03/%E4%BB%A5-dubbo-%E4%B8%BA%E4%BE%8B%E8%81%8A%E8%81%8A%E5%A6%82%E4%BD%95%E5%90%91%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%81%9A%E8%B4%A1%E7%8C%AE/</link><pubDate>Sun, 03 Jun 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/06/03/%E4%BB%A5-dubbo-%E4%B8%BA%E4%BE%8B%E8%81%8A%E8%81%8A%E5%A6%82%E4%BD%95%E5%90%91%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%81%9A%E8%B4%A1%E7%8C%AE/</guid><description>&lt;p>Github 上有众多优秀的开源项目，大多数 IT 从业者将其当做了予取予求的工具库，遇到什么需求，先去 Github 搜一把，但有没有想过有一天自己也可以给开源事业做一些贡献呢？本文将会以 dubbo 项目为例，向你阐释，给开源项目做贡献并不是一件难事。&lt;/p>
&lt;h2 id="1-为何要给开源贡献力量">1 为何要给开源贡献力量&lt;/h2>
&lt;p>为开源项目做贡献得到的收益是多方面的，为了让你有足够的信心加入到开源项目中，我在文章最开始列举出它的诸多好处。&lt;/p>
&lt;h3 id="11-巩固技能">1.1 巩固技能&lt;/h3>
&lt;p>无论你是提交代码，撰写文档，提交 Issue，组织活动，当你切身参与到一个开源项目中，相关的技能都会得到历练，并且在开源项目中找到自己的位置。一方面，日常工作中我们中的大多数人接触到的是业务场景，并没有太多机会接触到基础架构组件，开源项目为我们提供了一个平台，在这里，你可以尽情挑选自己熟悉的项目为它添砖加瓦（以 Dubbo 为例，并不是所有 IT 公司都有能力自研服务治理框架）；另一方面，你所提交的代码，会有管理员协助审核，他们会给出专业的建议，更好的代码规范以及更优的编程思路最终都会变成你的经验。&lt;/p>
&lt;h3 id="12-结交朋友">1.2 结交朋友&lt;/h3>
&lt;p>开源社区为你提供了一个平台，在这里，你可以认识很多纯粹的技术爱好者，开源贡献者是最符合 geek 定义的那群人，你所接触到的往往是某个领域最厉害的那批人。&lt;/p>
&lt;h3 id="13-建立口碑">1.3 建立口碑&lt;/h3>
&lt;p>这是一个很好的展示个人实力的地方，俗话说：talk is cheap，show me the code. 作为技术人员，没有什么比一个漂亮的 Github 主页更有说服力的了。如果你能够为开源项目做出可观的贡献，你也将收获到业界的知名度，此时开源项目的成就和你是密不可分的。&lt;/p>
&lt;h3 id="14-传承开源精神">1.4 传承开源精神&lt;/h3>
&lt;p>只有源源不断的贡献者给开源项目添砖加瓦，才可以为 Github 一类的开源社区形成良好的开源风气。否则，只有输出没有输入，开源会失去活力。&lt;/p>
&lt;h3 id="15-养成习惯">1.5 养成习惯&lt;/h3>
&lt;p>相信我，一旦养成了每天提交代码的习惯，就像你不想中断打卡一样，你绝不想中断 commit。不止有英语打卡，健身打卡，还有开源打卡！&lt;/p>
&lt;h2 id="2-贡献代码时的一些疑难杂症">2 贡献代码时的一些疑难杂症&lt;/h2>
&lt;p>如果你是一名开源界的新手，可能会对贡献的流程心生畏惧。比如：我该怎么修改代码并提交？我的代码要是存在bug怎么办？我的代码别人会不会很 low？我该如何寻找合适的开源项目？开源社区那么多的工具和词汇都是什么意思？&lt;/p>
&lt;p>文章的第二部分将从一个&lt;strong>小白&lt;/strong>的角度，介绍一下开源中的一些常见问题。&lt;/p>
&lt;h3 id="21-git-常规操作">2.1 git 常规操作&lt;/h3>
&lt;p>一般而言，我们选择使用 git 来作为版本管理的工具，你不一定要非常熟练的使用它，在我看来掌握 clone，add，commit，pull，push 即可，遇到复杂的场景，你还有谷歌。&lt;/p>
&lt;p>&lt;strong>fork 与 clone&lt;/strong>&lt;/p>
&lt;p>如果你只是想下载源码，查看他的源码实现，使用 Clone or download 按钮即可。&lt;/p>
&lt;p>如果你想要给开源项目做改动，并且最终请求合并，让开源项目存在你贡献的代码，就应该使用 fork。&lt;/p>
&lt;p>fork 将会复制一份当前主分支的代码进入到你的仓库中，之后你所有的修改，应当基于自己的仓库进行，在功能开发/bug 修复之后，可以使用你的仓库向源仓库提交 pull request。只有源仓库的管理员才有权利合并你的请求。&lt;/p></description></item><item><title>Dubbo 外部化配置</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/05/21/dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/</link><pubDate>Mon, 21 May 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/05/21/dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/</guid><description>&lt;h1 id="dubbo-外部化配置">Dubbo 外部化配置&lt;/h1>
&lt;h2 id="外部化配置">外部化配置&lt;/h2>
&lt;p>在&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/08/07/dubbo-%E6%B3%A8%E8%A7%A3%E9%A9%B1%E5%8A%A8/">Dubbo 注解驱动&lt;/a>例子中，无论是服务提供方，还是服务消费方，均需要转配相关配置Bean：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> ApplicationConfig &lt;span style="color:#268bd2">applicationConfig&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig.setName(&lt;span style="color:#2aa198">&amp;#34;dubbo-annotation-consumer&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>虽然实现类似于&lt;code>ProviderConfiguration&lt;/code> 和 &lt;code>ConsumerConfiguration&lt;/code> 这样的 Spring &lt;code>@Configuration&lt;/code> Bean 成本并不高，不过通过 Java Code 的方式定义配置 Bean，或多或少是一种 Hard Code（硬编码）的行为，缺少弹性。&lt;/p>
&lt;p>尽管在 Spring 应用中，可以通过 &lt;code>@Value&lt;/code> 或者 &lt;code>Environment&lt;/code> 的方式获取外部配置，其代码简洁性以及类型转换灵活性存在明显的不足。因此，Spring Boot 提出了外部化配置（External Configuration）的感念，即通过程序以外的配置源，动态地绑定指定类型。&lt;/p>
&lt;p>随着 Spring Boot / Spring Cloud 应用的流行，开发人员逐渐地接受并且使用 Spring Boot 外部化配置（External Configuration），即通过 &lt;code>application.properties&lt;/code> 或者 &lt;code>bootstrap.properties&lt;/code> 装配配置 Bean。&lt;/p>
&lt;p>下列表格记录了 Dubbo 内置配置类：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: left">配置类&lt;/th>
 &lt;th style="text-align: left">标签&lt;/th>
 &lt;th style="text-align: left">用途&lt;/th>
 &lt;th style="text-align: left">解释&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ProtocolConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:protocol/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">协议配置&lt;/td>
 &lt;td style="text-align: left">用于配置提供服务的协议信息，协议由提供方指定，消费方被动接受&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ApplicationConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:application/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">应用配置&lt;/td>
 &lt;td style="text-align: left">用于配置当前应用信息，不管该应用是提供者还是消费者&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ModuleConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:module/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">模块配置&lt;/td>
 &lt;td style="text-align: left">用于配置当前模块信息，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>RegistryConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:registry/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">注册中心配置&lt;/td>
 &lt;td style="text-align: left">用于配置连接注册中心相关信息&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>MonitorConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:monitor/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">监控中心配置&lt;/td>
 &lt;td style="text-align: left">用于配置连接监控中心相关信息，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ProviderConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:provider/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">提供方配置&lt;/td>
 &lt;td style="text-align: left">当 ProtocolConfig 和 ServiceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ConsumerConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:consumer/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">消费方配置&lt;/td>
 &lt;td style="text-align: left">当 ReferenceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>MethodConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:method/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">方法配置&lt;/td>
 &lt;td style="text-align: left">用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: left">&lt;code>ArgumentConfig&lt;/code>&lt;/td>
 &lt;td style="text-align: left">&lt;code>&amp;lt;dubbo:argument/&amp;gt;&lt;/code>&lt;/td>
 &lt;td style="text-align: left">参数配置&lt;/td>
 &lt;td style="text-align: left">用于指定方法参数配置&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>通过申明对应的 Spring 扩展标签，在 Spring 应用上下文中将自动生成相应的配置 Bean。&lt;/p></description></item><item><title>Dubbo 博客文档中文排版指南</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/01/01/dubbo-%E5%8D%9A%E5%AE%A2%E6%96%87%E6%A1%A3%E4%B8%AD%E6%96%87%E6%8E%92%E7%89%88%E6%8C%87%E5%8D%97/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2018/01/01/dubbo-%E5%8D%9A%E5%AE%A2%E6%96%87%E6%A1%A3%E4%B8%AD%E6%96%87%E6%8E%92%E7%89%88%E6%8C%87%E5%8D%97/</guid><description>&lt;h1 id="dubbo-博客文档中文排版指南">Dubbo 博客文档中文排版指南&lt;/h1>
&lt;p>[TOC]&lt;/p>
&lt;h2 id="空格">空格&lt;/h2>
&lt;p>「有研究显示，打字的时候不喜欢在中文和英文之间加空格的人，感情路都走得很辛苦，有七成的比例会在 34 岁的时候跟自己不爱的人结婚，而其余三成的人最后只能把遗产留给自己的猫。毕竟爱情跟书写都需要适时地留白。与大家共勉之。」—— &lt;a href="https://github.com/vinta/pangu.js">vinta/paranoid-auto-spacing&lt;/a>&lt;/p>
&lt;h3 id="中英文之间需要增加空格">中英文之间需要增加空格&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>在 LeanCloud 上，数据存储是围绕 &lt;code>AVObject&lt;/code> 进行的。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>在LeanCloud上，数据存储是围绕&lt;code>AVObject&lt;/code>进行的。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>在 LeanCloud上，数据存储是围绕&lt;code>AVObject&lt;/code> 进行的。&lt;/p>
&lt;/blockquote>
&lt;p>完整的正确用法：&lt;/p>
&lt;blockquote>
&lt;p>在 LeanCloud 上，数据存储是围绕 &lt;code>AVObject&lt;/code> 进行的。每个 &lt;code>AVObject&lt;/code> 都包含了与 JSON 兼容的 key-value 对应的数据。数据是 schema-free 的，你不需要在每个 &lt;code>AVObject&lt;/code> 上提前指定存在哪些键，只要直接设定对应的 key-value 即可。&lt;/p>
&lt;/blockquote>
&lt;p>例外：「豆瓣FM」等产品名词，按照官方所定义的格式书写。&lt;/p>
&lt;h3 id="中文与数字之间需要增加空格">中文与数字之间需要增加空格&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>今天出去买菜花了 5000 元。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>今天出去买菜花了 5000元。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>今天出去买菜花了5000元。&lt;/p>
&lt;/blockquote>
&lt;h3 id="数字与单位之间需要增加空格">数字与单位之间需要增加空格&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>我家的光纤入户宽带有 10 Gbps，SSD 一共有 20 TB。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>我家的光纤入户宽带有 10Gbps，SSD 一共有 10TB。&lt;/p>
&lt;/blockquote>
&lt;p>例外：度／百分比与数字之间不需要增加空格：&lt;/p>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>今天是 233° 的高温。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>新 MacBook Pro 有 15% 的 CPU 性能提升。&lt;/p></description></item></channel></rss>