<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – 功能演示</title><link>https://cn.dubbo.apache.org/zh/blog/java/demos/</link><description>Recent content in 功能演示 on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://cn.dubbo.apache.org/zh/blog/java/demos/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: Proxyless Mesh在Dubbo中的实践</title><link>https://cn.dubbo.apache.org/zh/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</link><pubDate>Mon, 05 Sep 2022 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</guid><description>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>随着 Dubbo 3.1 的 release，Dubbo 在云原生的路上又迈出了重要的一步。在这个版本中添加了 Proxyless Mesh 的新特性，Dubbo Proxyless Mesh 直接实现 xDS 协议解析，
实现 Dubbo 与 Control Plane 的直接通信，进而实现控制面对流量管控、服务治理、可观测性、安全等的统一管控，规避 Sidecar 模式带来的性能损耗与部署架构复杂性。&lt;/p>
&lt;h2 id="什么是service-mesh">什么是Service Mesh&lt;/h2>
&lt;p>Service Mesh 又译作 “服务网格”，作为服务间通信的基础设施层。Buoyant 公司的 CEO Willian Morgan 在他的这篇文章 &lt;a href="https://linkerd.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/">WHAT’S A Service Mesh? AND WHY DO I NEED ONE? &lt;/a>
中解释了什么是 Service Mesh，为什么云原生应用需要 Service Mesh。&lt;/p>
&lt;p>&lt;strong>下面是 Willian Morgan 对 Service Mesh 的解释。&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>A Service Mesh is a dedicated infrastructure layer for handling service-to-service communication.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>It’s responsible for the reliable delivery of requests through the complex topology of services
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>that comprise a modern, cloud native application. In practice, the Service Mesh is typically implemented
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>as an array of lightweight network proxies that are deployed alongside application code, without the
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application needing to be aware.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>翻译成中文&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>服务网格（Service Mesh）是处理服务间通信的基础设施层。它负责构成现代云原生应用程序的复杂服务拓扑来可靠地交付请求。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>在实践中，Service Mesh 通常以轻量级网络代理阵列的形式实现，这些代理与应用程序代码部署在一起，对应用程序来说无需感知代理的存在。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说到 Service Mesh 一定离不开 Sidecar 经典架构模式。它通过在业务 Pod 中注入 Sidecar 容器，接管业务容器的通信流量，同时 Sidecar 容器与网格平台的控制平面对接，
基于控制平面下发的策略，对代理流量实施治理和管控，将原有服务框架的治理能力下层到 Sidecar 容器中，从而实现了基础框架能力的下沉，与业务系统解耦。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/1.png" alt="Service Mesh">&lt;/p>
&lt;p>经典的 Sidecar Mesh 部署架构有很多优势，如平滑升级、多语言、业务侵入小等，但也带来了一些额外的问题，比如:&lt;/p>
&lt;ul>
&lt;li>Proxy 带来的性能损耗，在复杂拓扑的网络调用中将变得尤其明显&lt;/li>
&lt;li>流量拦截带来的架构复杂性&lt;/li>
&lt;li>Sidecar 生命周期管理&lt;/li>
&lt;li>部署环境受限，并不是所有环境都满足 Sidecar 流量拦截条件&lt;/li>
&lt;/ul>
&lt;p>针对 Sidecar Mesh 模型的问题，Dubbo 社区自很早之前就做了 Dubbo 直接对接到控制面的设想与思考，并在国内开源社区率先提出了 Proxyless Mesh 的概念，当然就 Proxyless 概念的说法而言，最开始是谷歌提出来的。&lt;/p>
&lt;h2 id="dubbo-proxyless-mesh">Dubbo Proxyless Mesh&lt;/h2>
&lt;p>Dubbo Proxyless 模式是指 Dubbo 直接与 Istiod通信，通过 xDS协议实现服务发现和服务治理等能力。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/2.png" alt="Proxyless">&lt;/p>
&lt;p>Proxyless 模式使得微服务又回到了 2.x 时代的部署架构，同 Dubbo 经典服务治理模式非常相似，所以说这个模式并不新鲜， Dubbo 从最开始就是这样的设计模式。
这样做可以极大的提升应用性能，降低网络延迟。有人说这种做法又回到了原始的基于 SDK 的微服务模式，其实非也，它依然使用了 Envoy 的 xDS API，
但是因为不再需要向应用程序中注入 Sidecar 代理，因此可以减少应用程序性能的损耗。&lt;/p>
&lt;p>但相比于 Mesh 架构，Dubbo 经典服务治理模式并没有强调控制面的统一管控，而这点恰好是 Service Mesh 所强调的，强调对流量、可观测性、证书等的标准化管控与治理，也是 Mesh 理念先进的地方。&lt;/p>
&lt;p>在 Dubbo Proxyless 架构模式下，Dubbo 进程将直接与控制面通信，Dubbo 进程之间也继续保持直连通信模式，我们可以看出 Proxyless 架构的优势：&lt;/p>
&lt;ul>
&lt;li>没有额外的 Proxy 中转损耗，因此更适用于性能敏感应用&lt;/li>
&lt;li>更有利于遗留系统的平滑迁移&lt;/li>
&lt;li>架构简单，容易运维部署&lt;/li>
&lt;li>适用于几乎所有的部署环境&lt;/li>
&lt;/ul>
&lt;h2 id="服务发现">服务发现&lt;/h2>
&lt;p>xDS 接入以注册中心的模式对接，节点发现同其他注册中心的服务自省模型一样，对于 xDS 的负载均衡和路由配置通过 ServiceInstance 的动态运行时配置传出，
在构建 Invoker 的时候将配置参数传入配置地址。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/3.png" alt="服务发现">&lt;/p>
&lt;h2 id="证书管理">证书管理&lt;/h2>
&lt;p>零信任架构下，需要严格区分工作负载的识别和信任，而签发 X.509 证书是推荐的一种认证方式。在 Kubernetes 集群中，服务间是通过 DNS 名称互相访问的，而网络流量可能被 DNS 欺骗、BGP/路由劫持、ARP 欺骗等手段劫持，为了将服务名称（DNS 名称）与服务身份强关联起来，Istio 使用置于 X.509 证书中的安全命名机制。SPIFFE是 Istio 所采用的安全命名的规范，它也是云原生定义的一种标准化的、可移植的工作负载身份规范。&lt;/p>
&lt;p>Secure Production Identity Framework For Everyone (SPIFFE) 是一套服务之间相互进行身份识别的标准，主要包含以下内容：&lt;/p>
&lt;ul>
&lt;li>SPIFFE ID 标准，SPIFFE ID 是服务的唯一标识，具体实现使用 URI 资源标识符&lt;/li>
&lt;li>SPIFFE Verifiable Identity Document (SVID) 标准，将 SPIFFE ID 编码到一个加密的可验证的数据格式中&lt;/li>
&lt;li>颁发与撤销 SVID 的 API 标准（SVID 是 SPIFFE ID 的识别凭证）&lt;/li>
&lt;/ul>
&lt;p>SPIFFE ID 规定了形如 &lt;code>spiffe://&amp;lt;trust domain&amp;gt;/&amp;lt;workload identifier&amp;gt;&lt;/code> 的 URI 格式，作为工作负载（Workload）的唯一标识。
而 Istio 在自身的生态中只使用到了 SPIFFE ID 作为安全命名，其数据格式由自己实现，通信格式采用 CNCF 支持的 xDS 协议规范（证书认证通信更具体来说是 xDS 的 SDS）。&lt;/p>
&lt;p>Istio 使用形如 &lt;code>spiffe://&amp;lt;trust_domain&amp;gt;/ns/&amp;lt;namespace&amp;gt;/sa/&amp;lt;service_account&amp;gt;&lt;/code> 格式的 SPIFFE ID 作为安全命名，注入到 X.509 证书的 subjectAltName 扩展中。
其中&amp;quot;trust_domain&amp;quot;参数通过 Istiod 环境变量TRUST_DOMAIN 注入，用于在多集群环境中交互。&lt;/p>
&lt;p>以下是 Dubbo Proxyless Mesh 证书颁发的过程&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/4.png" alt="证书颁发">&lt;/p>
&lt;ul>
&lt;li>创建 RSA 私钥（Istio 还不支持 ECDSA 私钥）&lt;/li>
&lt;li>构建 CSR（Certificate signing request）模板&lt;/li>
&lt;li>自签名 CSR 生成证书&lt;/li>
&lt;li>创建 Kubernetes Secret 资源储存 CA 证书和私钥（CA Service处理）&lt;/li>
&lt;/ul>
&lt;h2 id="案例实践">案例实践&lt;/h2>
&lt;p>接下来我将带领大家通过一个例子使已有的项目快速跑在 Proxyless Mesh 模式下。&lt;/p>
&lt;h2 id="环境准备">环境准备&lt;/h2>
&lt;h3 id="安装docker">安装docker&lt;/h3>
&lt;p>&lt;a href="https://www.docker.com/">https://www.docker.com/&lt;/a>&lt;/p>
&lt;h3 id="安装minikube">安装minikube&lt;/h3>
&lt;p>墙裂推荐：&lt;a href="https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/">https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/&lt;/a>&lt;/p>
&lt;h3 id="安装istio">安装istio&lt;/h3>
&lt;p>&lt;a href="https://istio.io/latest/docs/setup/getting-started/">https://istio.io/latest/docs/setup/getting-started/&lt;/a>&lt;/p>
&lt;p>❗❗❗ 安装 Istio 的时候需要开启 first-party-jwt 支持（使用 istioctl 工具安装的时候加上 &amp;ndash;set values.global.jwtPolicy=first-party-jwt 参数），否则将导致客户端认证失败的问题。
参考命令如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>curl -L https://istio.io/downloadIstio | sh -
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">cd&lt;/span> istio-1.xx.x
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">PATH&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#268bd2">$PWD&lt;/span>/bin:&lt;span style="color:#268bd2">$PATH&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>istioctl install --set &lt;span style="color:#268bd2">profile&lt;/span>&lt;span style="color:#719e07">=&lt;/span>demo --set values.global.jwtPolicy&lt;span style="color:#719e07">=&lt;/span>first-party-jwt -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="代码准备">代码准备&lt;/h2>
&lt;h3 id="xds-provider">xds-provider&lt;/h3>
&lt;h4 id="定义一个接口">定义一个接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">GreetingService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现对应的接口">实现对应的接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@DubboService&lt;/span>&lt;span style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AnnotatedGreetingService&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> GreetingService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;greeting service received: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hello, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;! from host: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> NetUtils&lt;span style="color:#719e07">.&lt;/span>getLocalHost&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写启动类">编写启动类&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">(&lt;/span>ProviderConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo service started&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> CountDownLatch&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">).&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@EnableDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>scanBasePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.impl&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@PropertySource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;classpath:/spring/dubbo-provider.properties&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写配置信息">编写配置信息&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo.application.name=dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 由于 Dubbo 3 应用级服务发现的元数据无法从 istio 中获取，需要走服务自省模式。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 这要求了 Dubbo MetadataService 的端口在全集群的是统一的。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.metadataServicePort=20885
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 走xds协议
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address=xds://istiod.istio-system.svc:15012
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.name=tri
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.port=50051
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 对齐k8s pod生命周期，由于 Kubernetes probe 探活机制的工作原理限制，
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 探活请求的发起方不是 localhost，所以需要配置 qosAcceptForeignIp 参数开启允许全局访问
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosEnable=true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosAcceptForeignIp=true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写deploymentyml和serviceyml">编写Deployment.yml和Service.yml&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> replicas: 3
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> matchLabels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> template:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> labels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> containers:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> image: apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> livenessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /live
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> readinessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /ready
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startupProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /startup
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failureThreshold: 30
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 10
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clusterIP: None
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ports:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: grpc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol: TCP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 50051
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targetPort: 50051
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写dockerfile">编写Dockerfile&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>FROM openjdk:8-jdk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ADD ./target/dubbo-samples-xds-provider-1.0-SNAPSHOT.jar dubbo-samples-xds-provider-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-provider-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="xds-consumer">xds-consumer&lt;/h3>
&lt;h4 id="定义一个接口-1">定义一个接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public interface GreetingService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String sayHello(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现对应的接口-1">实现对应的接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>@Component(&amp;#34;annotatedConsumer&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class GreetingServiceConsumer {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 这里特别注意的是、由于当前 Dubbo 版本受限于 istio 的通信模型无法获取接口所对应的应用名，
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 因此需要配置 providedBy 参数来标记此服务来自哪个应用。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @DubboReference(version = &amp;#34;1.0.0&amp;#34;, providedBy = &amp;#34;dubbo-samples-xds-provider&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private GreetingService greetingService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public String doSayHello(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return greetingService.sayHello(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写启动类-1">编写启动类&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public class ConsumerBootstrap {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public static void main(String[] args) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> GreetingServiceConsumer greetingServiceConsumer = context.getBean(GreetingServiceConsumer.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> while (true) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> try {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String hello = greetingServiceConsumer.doSayHello(&amp;#34;xDS Consumer&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&amp;#34;result: &amp;#34; + hello);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread.sleep(100);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } catch (Throwable t) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t.printStackTrace();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Configuration
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @EnableDubbo(scanBasePackages = &amp;#34;org.apache.dubbo.samples.action&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @PropertySource(&amp;#34;classpath:/spring/dubbo-consumer.properties&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @ComponentScan(value = {&amp;#34;org.apache.dubbo.samples.action&amp;#34;})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> static class ConsumerConfiguration {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写配置信息-1">编写配置信息&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo.application.name=dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.metadataServicePort=20885
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address=xds://istiod.istio-system.svc:15012
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.consumer.timeout=3000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.consumer.check=false
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosEnable=true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosAcceptForeignIp=true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写deploymentyml和serviceyml-1">编写Deployment.yml和Service.yml&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> replicas: 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> matchLabels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> template:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> labels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> containers:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> image: apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> livenessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /live
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> readinessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /ready
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startupProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /startup
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failureThreshold: 30
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 10
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clusterIP: None
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ports:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: grpc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol: TCP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 50051
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targetPort: 50051
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写dockerfile-1">编写Dockerfile&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>FROM openjdk:8-jdk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ADD ./target/dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>✅ 到目前为止我们的环境和代码就全都准备完毕了！&lt;/p>
&lt;h2 id="构建镜像">构建镜像&lt;/h2>
&lt;h3 id="1启动docker">1、启动docker&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/5.png" alt="启动docker">&lt;/p>
&lt;h3 id="2启动minikube">2、启动minikube&lt;/h3>
&lt;p>因为minikube是一个本地的k8s，他启动需要一个虚拟引擎，这里我们用docker来管理。我们通过如下命令启动&lt;/p>
&lt;p>&lt;code>minikube start&lt;/code>&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/6.png" alt="启动minikube">&lt;/p>
&lt;p>我们可以在docker里看到minikube&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/7.png" alt="minikube">&lt;/p>
&lt;h3 id="3检查istio的状态">3、检查istio的状态&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/8.png" alt="istio的状态">&lt;/p>
&lt;h3 id="4构建镜像">4、构建镜像&lt;/h3>
&lt;p>在本地找到代码所在位置、依次执行以下命令&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到provider所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ./dubbo-samples-xds-provider/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 构建provider的镜像
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker build -t apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/9.png" alt="构建provider">&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到consumer所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ../dubbo-samples-xds-consumer/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 构建consumer的镜像
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker build -t apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/10.png" alt="构建consumer">&lt;/p>
&lt;h3 id="5检查本地镜像">5、检查本地镜像&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/11.png" alt="检查本地镜像">&lt;/p>
&lt;h3 id="6创建namespace">6、创建namespace&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 初始化命名空间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/dubbo-samples-xds/deploy/Namespace.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 切换命名空间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubens dubbo-demo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果不创建namespace，那么会看到如下错误&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/12.png" alt="错误">&lt;/p>
&lt;h2 id="部署容器">部署容器&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到provider所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ./dubbo-samples-xds-provider/src/main/resources/k8s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Deployment.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Service.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 部署provider的Deployment和Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f Deployment.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f Service.yml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/13.png" alt="部署provider">&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到consumer所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ../../../../../dubbo-samples-xds-consumer/src/main/resources/k8s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># dubbo-samples-xds/dubbo-samples-xds-consumer/src/main/resources/k8s/Deployment.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 部署consumer的Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f Deployment.yml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/14.png" alt="部署provider">&lt;/p>
&lt;p>在minikube dashboard看到我们已经部署的pod&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/15.png" alt="部署provider">&lt;/p>
&lt;h2 id="观察consumer效果">观察consumer效果&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>kubectl logs xxx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.6
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.6
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="总结展望">总结&amp;amp;展望&lt;/h2>
&lt;p>本文主要剖析了 Dubbo Proxyless Mesh 的架构、服务发现以及证书管理等核心流程，最后通过示例给大家演示了如何使用 Dubbo Proxyless。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/20220905/16.png" alt="部署provider">&lt;/p>
&lt;p>随着 Dubbo 3.1 的 release，Dubbo 在云原生的路上又迈出了重要的一步。在今年年底，Dubbo Mesh 将发布具有服务发现能力的版本，
届时将面向所有 Dubbo 用户提供从低版本平滑迁移到 Mesh 架构的能力；在明年年初春季的时候将发布带有治理能力的版本；在明年年底前发布带热插件更新能力的版本，
希望有兴趣见证 Dubbo 云原生之路的同学可以积极参与社区贡献！&lt;/p>
&lt;p>更多关于 Dubbo Mesh 的动态可以关注 Apache Dubbo 社区官方公众号（ApacheDubbo），及时获取最新消息。&lt;/p></description></item><item><title>Blog: Dubbo-Api-Docs -- Apache Dubbo文档展示&amp;测试工具</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;p>Dubbo-Api-Docs 目前通过直连服务节点的方式获取该服务的接口列表. 测试接口时可以直连也可以通过注册中心.未来会增加通过注册中心获取服务列表的方式.并根据Dubbo的升级规划增加新的功能支持.也会根据社区的需求增加功能.&lt;/p>
&lt;p>Dubbo-Api-Docs 会在服务提供者启动完毕后扫描docs相关注解并将处理结果缓存.并增加一些Dubbo-Api-Docs相关的Dubbo提供者接口. 缓存的数据在将来可能会放到Dubbo元数据中心中.&lt;/p>
&lt;h2 id="当前版本-2781">当前版本: 2.7.8.1&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-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.apache.dubbo&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-api-docs-annotations&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>${dubbo-version}&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:#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.apache.dubbo&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-api-docs-core&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>${dubbo-version}&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;h2 id="快速入门">快速入门&lt;/h2>
&lt;h3 id="1dubbo提供者项目的方法参数中加上-dubbo-api-docs-注解">1.dubbo提供者项目的方法参数中加上 Dubbo-Api-Docs 注解&lt;/h3>
&lt;ul>
&lt;li>如果 dubbo提供者的接口和方法参数在一个单独的jar项目中,则在该项目中引入: dubbo-api-docs-annotations&lt;/li>
&lt;li>dubbo提供者项目引入 dubbo-api-docs-core&lt;/li>
&lt;li>在提供者项目的项目启动类(标注了@SpringBootApplication的类)或者配制类(标注了@Configuration的类)中增加注解 @EnableDubboApiDocs 以启用Dubbo Api Docs功能&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>为避免增加生产环境中的资源占用, 建议单独创建一个配制类用于启用Dubbo-Api-Docs, 并配合 @Profile(&amp;ldquo;dev&amp;rdquo;) 注解使用
当然, Dubbo-Api-Docs 仅在项目启动时多消耗了点CPU资源, 并使用了一点点内存用于缓存, 将来会考虑将缓存中的内容放到元数据中心.&lt;/p>
&lt;/blockquote>
&lt;h4 id="下面以dubbo-api-docs-exampleshttpsgithubcomapachedubbo-spi-extensionstree27xdubbo-api-docsdubbo-api-docs-examples项目中的部分服务接口为例">下面以&lt;a href="https://github.com/apache/dubbo-spi-extensions/tree/2.7.x/dubbo-api-docs/dubbo-api-docs-examples">dubbo-api-docs-examples&lt;/a>项目中的部分服务接口为例:&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>git clone -b 2.7.x https://github.com/apache/dubbo-spi-extensions.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>进入 dubbo-spi-extensions/dubbo-api-docs/dubbo-api-docs-examples 目录&lt;/p>
&lt;p>dubbo-api-docs-examples 中有两个子模块:&lt;/p>
&lt;ul>
&lt;li>examples-api: 一个jar包项目,其中包含服务的接口和接口参数Bean&lt;/li>
&lt;li>examples-provider: 提供者服务端,包含spring boot启动器和服务的实现&lt;/li>
&lt;/ul>
&lt;p>下面我们在这两个子模块中增加Dubbo-Api-Docs&lt;/p>
&lt;blockquote>
&lt;p>examples-api:&lt;/p>
&lt;/blockquote>
&lt;p>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>org.apache.dubbo&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-api-docs-annotations&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.7.8&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>org.apache.dubbo.apidocs.examples.params 中有两个Bean,我们来为它们添加docs注解&lt;/p>
&lt;ul>
&lt;li>QuickStartRequestBean 作为参数Bean, 添加 @RequestParam&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> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">QuickStartRequestBean&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@RequestParam&lt;/span>&lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;You name&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> required &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">,&lt;/span> description &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;please enter your full name&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> example &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Zhang San&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String name&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@RequestParam&lt;/span>&lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;You age&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> defaultValue &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;18&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> age&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@RequestParam&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Are you a main?&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> man&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// getter/setter略...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>QuickStartRespBean 作为响应Bean,添加 @ResponseProperty&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> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">QuickStartRespBean&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@ResponseProperty&lt;/span>&lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Response code&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> example &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;500&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> code&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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">@ResponseProperty&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Response message&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String msg&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// getter/setter略...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由于我们只挑选了部分接口作为演示,到此这些接口涉及的docs注解添加完毕&lt;/p>
&lt;blockquote>
&lt;p>examples-provider:&lt;/p>
&lt;/blockquote>
&lt;p>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>org.apache.dubbo&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-api-docs-core&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.7.8&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>我们挑选一个接口作为演示:&lt;/p>
&lt;p>org.apache.dubbo.apidocs.examples.api.impl.QuickStartDemoImpl 中的 quickStart 方法&lt;/p>
&lt;p>QuickStartDemoImpl 实现了 api包中的 org.apache.dubbo.apidocs.examples.api.IQuickStartDemo 接口&lt;/p>
&lt;ul>
&lt;li>在 QuickStartDemoImpl 中:&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">@DubboService&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@ApiModule&lt;/span>&lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;quick start demo&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> apiInterface &lt;span style="color:#719e07">=&lt;/span> IQuickStartDemo&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;v0.1&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">QuickStartDemoImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> IQuickStartDemo &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&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">@ApiDoc&lt;/span>&lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;quick start demo&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;v0.1&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> description &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;this api is a quick start demo&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> responseClassDescription&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;A quick start response bean&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> QuickStartRespBean &lt;span style="color:#268bd2">quickStart&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">@RequestParam&lt;/span>&lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;strParam&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> required &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span> String strParam&lt;span style="color:#719e07">,&lt;/span> QuickStartRequestBean beanParam&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> QuickStartRespBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">200&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> beanParam&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> beanParam&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>到此docs相关注解已添加完毕,下面我们来开启 Dubbo-Api-Docs. 新增一个配制类,位置任意,只要能被spring boot扫描到就行.&lt;/p>
&lt;p>我们在 org.apache.dubbo.apidocs.examples.cfg 包中新增一个配制类 DubboDocConfig :&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Profile&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dev&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#586e75">// 配合 Profile 一起使用, 在 profile 为 dev 时才会加载该配制类
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@EnableDubboApiDocs&lt;/span> &lt;span style="color:#586e75">// 开启 Dubbo-Api-Docs
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboDocConfig&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>到此 Dubbo-Api-Docs 相关的东西已经添加完毕.
&lt;a href="https://github.com/apache/dubbo-spi-extensions/tree/2.7.x/dubbo-api-docs/dubbo-api-docs-examples">dubbo-api-docs-examples&lt;/a>
中有更多更为详尽的例子.下文中有注解的详细说明.下面我们来看一下增加 Dubbo-Api-Docs 后的效果图.&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/api-docs/quickStart.png" alt="demoApi2">&lt;/p>
&lt;h3 id="2启动提供者项目">2.启动提供者项目&lt;/h3>
&lt;ul>
&lt;li>示例使用nacos作为注册中心,&lt;a href="https://nacos.io">下载并启动nacos&lt;/a>&lt;/li>
&lt;li>在上面的例子中,我们启动 examples-provider 项目中的 org.apache.dubbo.apidocs.examples.ExampleApplication.
在examples-provider目录中:&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>mvn spring-boot:run
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3下载-dubbo-admin">3.下载 dubbo-admin&lt;/h3>
&lt;p>&lt;a href="https://github.com/apache/dubbo-admin">dubbo-admin仓库&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>dubbo-admin 需要下载 develop 分支源码启动&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>git clone -b develop https://github.com/apache/dubbo-admin.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/blockquote>
&lt;h3 id="4启动访问-dubbo-admin">4.启动访问 dubbo-admin&lt;/h3>
&lt;p>参考 dubbo-admin 里的说明启动:&lt;/p>
&lt;div class="highlight">&lt;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>1. 在 dubbo-admin-server/src/main/resources/application.properties 中修改注册中心地址
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2. 编译 mvn clean package
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>3. 启动:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mvn --projects dubbo-admin-server spring-boot:run
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>或者
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd dubbo-admin-distribution/target; java -jar dubbo-admin-0.1.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>4. 浏览器访问: http://localhost:8080
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>5. 默认帐号密码都是: root
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="5进入接口文档模块">5.进入&amp;quot;接口文档&amp;quot;模块&lt;/h3>
&lt;ul>
&lt;li>在&amp;quot;dubbo提供者IP&amp;quot;和&amp;quot;dubbo提供者端口&amp;quot;中分别输入提供者所在机器IP和端口, 点击右侧 &amp;quot; 加载接口列表&amp;quot; 按钮&lt;/li>
&lt;li>左侧接口列表中加载出接口列表,点击任意接口,右边展示出该接口信息及参数表单.&lt;/li>
&lt;li>填入表单内容后,点击最下方测试按钮&lt;/li>
&lt;li>响应部分展示了响应示例及实际响应结果&lt;/li>
&lt;/ul>
&lt;h2 id="源码仓库">源码仓库&lt;/h2>
&lt;p>Dubbo-Api-Docs 根据功能拆分,分别在两个仓库中:&lt;/p>
&lt;h3 id="dubbo-spi-extensions">dubbo-spi-extensions&lt;/h3>
&lt;blockquote>
&lt;p>&lt;a href="https://github.com/apache/dubbo-spi-extensions">dubbo-spi-extensions仓库地址&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>该仓库存放dubbo的一些非核心功能的扩展, Dubbo-Api-Docs 作为该仓库中的一个子模块,由于该仓库属于Dubbo 3.0中规划的一部分,而Dubbo-Api-Docs是基于Dubbo 2.7.x 开发的,所以在该仓库中增加了&lt;a href="https://github.com/apache/dubbo-spi-extensions/tree/2.7.x/dubbo-api-docs">2.7.x分支,Dubbo-Api-Docs就在该分支下&lt;/a>.
该仓库中包含了 Dubbo-Api-Docs 的文档相关注解、注解扫描能力和使用示例:&lt;/p>
&lt;ul>
&lt;li>dubbo-api-docs-annotations: 文档生成的相关注解.考虑到实际情况中 dubbo api 的接口类和接口参数会规划为一个单独的jar包, 所以注解也独立为一个jar包.本文后面会对注解做详细说明.&lt;/li>
&lt;li>dubbo-api-docs-core: 负责解析注解,生成文档信息并缓存. 前面提到的Dubbo-Api-Docs相关接口也在该包中&lt;/li>
&lt;li>dubbo-api-docs-examples: 使用示例&lt;/li>
&lt;/ul>
&lt;h3 id="dubbo-admin">Dubbo-Admin&lt;/h3>
&lt;blockquote>
&lt;p>&lt;a href="https://github.com/KeRan213539/dubbo-admin">Dubbo-Admin仓库地址&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>文档的展示及测试放在了 dubbo admin 项目中&lt;/p>
&lt;h2 id="注解说明">注解说明&lt;/h2>
&lt;ul>
&lt;li>@EnableDubboApiDocs: 配制注解, 启用 dubbo api docs 功能&lt;/li>
&lt;li>@ApiModule: 类注解, dubbo接口模块信息,用于标注一个接口类模块的用途
&lt;ul>
&lt;li>value: 模块名称&lt;/li>
&lt;li>apiInterface: 提供者实现的接口&lt;/li>
&lt;li>version: 模块版本&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>@ApiDoc: 方法注解, dubbo 接口信息,用于标注一个接口的用途
&lt;ul>
&lt;li>value: 接口名称&lt;/li>
&lt;li>description: 接口描述(可使用html标签)&lt;/li>
&lt;li>version: 接口版本&lt;/li>
&lt;li>responseClassDescription: 响应的数据的描述&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>@RequestParam: 类属性/方法参数注解,标注请求参数
&lt;ul>
&lt;li>value: 参数名&lt;/li>
&lt;li>required: 是否必传参数&lt;/li>
&lt;li>description: 参数描述&lt;/li>
&lt;li>example: 参数示例&lt;/li>
&lt;li>defaultValue: 参数默认值&lt;/li>
&lt;li>allowableValues: 允许的值,设置该属性后界面上将对参数生成下拉列表
&lt;ul>
&lt;li>注:使用该属性后将生成下拉选择框&lt;/li>
&lt;li>boolean 类型的参数不用设置该属性,将默认生成 true/false 的下拉列表&lt;/li>
&lt;li>枚举类型的参数会自动生成下拉列表,如果不想开放全部的枚举值,可以单独设置此属性.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>@ResponseProperty: 类属性注解, 标注响应参数
&lt;ul>
&lt;li>value: 参数名&lt;/li>
&lt;li>example: 示例&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="使用注意">使用注意&lt;/h2>
&lt;ul>
&lt;li>响应bean(接口的返回类型)支持自定义泛型, 但只支持一个泛型占位符&lt;/li>
&lt;li>关于Map的使用:Map的key只能用基本数据类型.如果Map的key不是基础数据类型,生成的 就不是标准json格式,会出异常&lt;/li>
&lt;li>接口的同步/异步取自 org.apache.dubbo.config.annotation.Service#async / org.apache.dubbo.config.annotation.DubboService#async&lt;/li>
&lt;/ul>
&lt;h2 id="示例说明">示例说明&lt;/h2>
&lt;p>&lt;a href="https://github.com/apache/dubbo-spi-extensions/tree/2.7.x/dubbo-api-docs">dubbo-spi-extensions / Dubbo-Api-Docs&lt;/a> 中的 dubbo-api-docs-examples 目录中为示例工程:&lt;/p>
&lt;ul>
&lt;li>examples-api: jar包项目,包含服务提供者的接口类及参数Bean&lt;/li>
&lt;li>examples-provider: 使用 dubbo-spring-boot-starter 的提供者项目, 注册中心使用 nacos&lt;/li>
&lt;li>examples-provider-sca: 使用 spring-cloud-starter-dubbo 的提供者项目, 注册中心使用 nacos&lt;/li>
&lt;/ul>
&lt;h3 id="示例使用步骤">示例使用步骤&lt;/h3>
&lt;ol>
&lt;li>示例使用nacos作为注册中心,&lt;a href="https://nacos.io">下载并启动nacos&lt;/a>&lt;/li>
&lt;li>任意启动 examples-provider 和 examples-provider-sca 中的任意一个,当然也可以两个都启动. examples-provider 使用 20881端口 examples-provider-sca 使用20882端口.两个项目都是spring boot项目,启动类在 org.apache.dubbo.apidocs.examples 包下.&lt;/li>
&lt;li>启动 &lt;a href="https://github.com/KeRan213539/dubbo-admin">Dubbo-Admin&lt;/a>, 浏览器访问: http://localhost:8080&lt;/li>
&lt;li>进入 dubbo-admin 中的 &amp;ldquo;接口文档&amp;quot;模块&lt;/li>
&lt;li>在&amp;quot;dubbo提供者IP&amp;quot;和&amp;quot;dubbo提供者端口&amp;quot;中分别输入提供者所在机器IP和端口, 点击右侧 &amp;quot; 加载接口列表&amp;rdquo; 按钮&lt;/li>
&lt;li>左侧接口列表中加载出接口列表,点击任意接口,右边展示出该接口信息及参数表单.&lt;/li>
&lt;li>填入表单内容后,点击最下方测试按钮&lt;/li>
&lt;li>响应部分展示了响应示例及实际响应结果&lt;/li>
&lt;/ol></description></item><item><title>Blog: Dubbo测试验证</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/subscribe-only.jpg" alt="subscribe-only">
禁用注册配置&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 src="https://cn.dubbo.apache.org/imgs/blog/dubbo-directly.jpg" alt="subscribe-only">&lt;/p>
&lt;p>可以通过以下几种配置来指定IP调用&lt;/p>
&lt;ul>
&lt;li>XML 配置： 如果是线上需求需要点对点，可在 &lt;a href="dubbo:reference">dubbo:reference&lt;/a> 中配置 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;a href="dubbo:reference">dubbo:reference&lt;/a> 中的配置，如：
&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>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T &lt;span style="color:#268bd2">getProxy&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]&lt;/span> interfaces &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String config &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;interfaces&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>config &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> config&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> types &lt;span style="color:#719e07">=&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>COMMA_SPLIT_PATTERN&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>types &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> types&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[&lt;/span>types&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">2&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getInterface&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> EchoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> types&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces&lt;span style="color:#719e07">[&lt;/span>i &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> ReflectUtils&lt;span style="color:#719e07">.&lt;/span>forName&lt;span style="color:#719e07">(&lt;/span>types&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>interfaces &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]{&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getInterface&lt;span style="color:#719e07">(),&lt;/span> EchoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getProxy&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> interfaces&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过这种方式，任何bean都可以被转换成EchoService的实例，但是并没有实现&lt;code>$echo&lt;/code>这个方法，这里，Dubbo使用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:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">EchoFilter&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Filter &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&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> Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> Invocation inv&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>inv&lt;span style="color:#719e07">.&lt;/span>getMethodName&lt;span style="color:#719e07">().&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>$ECHO&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> inv&lt;span style="color:#719e07">.&lt;/span>getArguments&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> inv&lt;span style="color:#719e07">.&lt;/span>getArguments&lt;span style="color:#719e07">().&lt;/span>length &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcResult&lt;span style="color:#719e07">(&lt;/span>inv&lt;span style="color:#719e07">.&lt;/span>getArguments&lt;span style="color:#719e07">()[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>inv&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在经过EchoFilter.invoke方法时，如果调用的是&lt;code>$echo&lt;/code>，会中断当前的调用过程，直接返回&lt;code>$echo&lt;/code>的入参，否则会继续执行Filter链。&lt;br>
通过动态代理和EchoFilter机制，使得回声测试的整个过程对用户透明，不需要做任何额外的配置，直接调用即可。&lt;/p></description></item><item><title>Blog: Dubbo 在 Service Mesh 下的思考和方案</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/dubbomesh/1.png" alt="1.png | center | 826x206">&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 src="https://cn.dubbo.apache.org/imgs/blog/dubbomesh/2.png" alt="2.png | center | 826x206" title="">&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 src="https://cn.dubbo.apache.org/imgs/blog/dubbomesh/3.png" alt="3.png | center | 826x206" title="">&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 src="https://cn.dubbo.apache.org/imgs/blog/dubbomesh/4.png" alt="4.png | center | 826x206" title="">&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 src="https://cn.dubbo.apache.org/imgs/blog/dubbomesh/5.png" alt="5.png | center | 826x206" title="">&lt;/p>
&lt;p>既然协议在跨语言场景下如此重要，有必要稍微回归下协议的历史轨迹。看历史一般是轻松有趣的过程，最重要的好处是能使我们头脑清晰而不迷茫。&lt;/p>
&lt;p>我们先从2008年说起，很近也就10年，阿里服务框架诞生这一年。当年各大公司还在炒作SOA思想的时候，阿里在不清楚SOA思想的情况下根据自身业务诉求实践拥抱了SOA的架构。阿里服务框架一直是从三个层面来定义，第一RPC通信 第二是提供丰富强大的治理能力 第三就是基于容器隔离的运维能力，使得中间件可以独立升级。这个理念直到今日都是非常先进，非常的赞。就像前面说的，Dubbo主要是面向Java领域的微服务架构解决方案，在以Java为主导的技术架构下是绝对首选，但因为其协议设计是私有特性，要想成为跨语言的协议标准是有一定难度。&lt;/p>
&lt;p>事实上，之前已经出现了很多通用的跨语言的服务集成规范。最早是91年的CORBA，是分布式对象访问协议，2000年的SOAP是当年webservice思想下的协议，无论是CORBA还是SOAP都是支持所有平台和语言的一套规范，但是设计地比较复杂笨重，且性能存在一定问题。&lt;/p>
&lt;p>REST是一种架构风格，相比SOAP的设计，有非常优秀的理念和最佳实践指导，并且万维网作为世界上最大型最成功的的分布式应用是REST最好的证明。但跟SOAP一样，REST跑在1上有性能瓶颈，这个也可能是当年阿里服务框架没有选择REST规范的原因。额外提下，REST思想虽然很早就有，但事实上REST的规范在Java领域JAX-RS API 直到最近两年在2.2版本下才算稳定成形，且越来越接近微服务框架。&lt;/p>
&lt;p>1996年的1在连接通道不支持多工复用，根本无法发挥TCP/UDP的网络能力；而到了2015年HTTP2则解决这些，能够最大限度的利用TCP层的网络宽带，且支持了streaming，push等交互模式，这些跟很多的私有或专有应用协议干得是一个事，但是标准化的大家都容易接受的事。这里必须提一下，伴随HTTP2而来的是grpc，原先Google早早推出了Protocolbuffer，但一直没把自家stubby开源，我猜测最大的原因是不想grpc跑在一个私有协议上，而是在等HTTP2.&lt;/p>
&lt;p>总结下来，协议技术一直在向着轻量级和标准规范化的方向发展。像SOAP，CORBA这些重量级的不跨平台的协议必然消失在历史车轮里，私有或专有的协议也会向标准协议靠拢。在面向跨语言的场景下，有两种的协议规范是大概率胜出，一种是REST，一种是grpc，两者都是以HTTP为交换通道。&lt;/p>
&lt;h2 id="面向多语言协议的三层面">面向多语言协议的三层面&lt;/h2>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbomesh/6.png" alt="6.png | center | 826x206" title="">&lt;/p>
&lt;p>展开来讲，在面向多语言的协议需要考虑三个层面。&lt;/p>
&lt;ul>
&lt;li>先从最右边的会话层，干得事是在tcp字节流的基础上形成交互模式，比如 一对一的标准请求响应模式， 以及onway， 一对多的streaming模式。Dubbo在这一层是有扩展能力的，目前除了支持自定义的Dubbo-Remoting，也支持基于HTTP通道能力，我们觉得未来的趋势是HTTP2,所以也会支持这块.这里在分享一句话跟大家一起思考，HTTP不是RPC，HTTP被翻译成超文本传输协议，但不是传输层。另外提一下，这一层是对于MQ，Streaming Compute，Cache等等都是通用的。&lt;/li>
&lt;li>再说展示层，干得事是在真正的服务调用过程中，业务对象以何种形式被格式化，比如HTTP头中的content-type就用于这个展示协议的描述，最常用的JSON,TXT,XML等。这一层对于sidecar来说，可以做透明处理，也就是说sidecar只需要解析出头部信息，前提是要求业务应用把需要在治理时用到的一些字段信息以字符串形式放到头部中。Dubbo当前是默认HEssion，跨语言能力比较弱，所以未来JSON是我们首选。&lt;/li>
&lt;li>最后，首先一个服务是干什么的，它的名字，方法，参数都是怎样的，等等基本元信息是需要统一描述的，即便像是REST这样基于URI，也是需要一种协议来定义，以前Dubbo是基于java interface来定义，现在我们在多语言的mesh环境下是考虑向OpenAPI specification方向考虑，支持swagger。
我们相信在这几个层面，尤其是会话层和应用层，用不多几年一定会是标准化的，尤其是在云原生的趋势下。&lt;/li>
&lt;/ul>
&lt;h3 id="方案之kubernetes集成span-data-typecolor-stylecolorwhiteduspan">方案之Kubernetes集成&lt;span data-type="color" style="color:white">Du&lt;/span>&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbomesh/7.png" alt="7.png | center | 826x206" title="">
&lt;span data-type="color" style="color:white">bbo Mesh方案之Kubernetes集成&lt;/span>
其实，servicemesh在最近两年流行最大的原因是云原生理念的逐渐深入人心，从广义角度看，能够融入云原生的微服务框架都能称得上servicemesh。谈云原生，肯定绕不开kubernetes，所以我们在Dubbo Mesh的方案的第一个分享是 在kubernetes下的集成，目标是复用Kubernetes的基础服务，从而使得Dubbo能解决kubernetes环境下的微服务集成问题，同时能最大限度的利用dubbo已有的功能。核心思路是两点，&lt;/p>
&lt;ul>
&lt;li>Dubbo应用在构建阶段自动生成其deployment和service的声明文件。这个主要是解决Dubbo与kubernetes的服务映射。&lt;/li>
&lt;li>Dubbo地址注册针对kubernetes的扩展实现，通过Kubernetes的APIServer来拉取并监听某个服务的podIP。这样，在kubernetes集群内，Dubbo服务就能在其podID的虚拟网络内实现服务发现。&lt;/li>
&lt;/ul>
&lt;h2 id="方案之跨语言协议支持">方案之跨语言协议支持&lt;/h2>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbomesh/8.png" alt="8.png | center | 826x206" title="">&lt;/p>
&lt;p>前面讲了很多关于协议方面的东西，也为我们在Dubbo Mesh的方案的第二点分享是做了铺垫， 第二点的目标是Dubbo 协议的多语言支持。核心思路是&lt;/p>
&lt;ul>
&lt;li>积极兼容开源社区Envoy，这个使得Envoy上兼容支持Dubbo的私有协议。&lt;/li>
&lt;li>Dubbo支持HTTP/2作为传输通道，这个是为了Dubbo的协议通道能力向更加开放更加标准规范的方向做努力。&lt;/li>
&lt;/ul>
&lt;h2 id="servicemesh之云原生的指导">ServiceMesh之云原生的指导&lt;/h2>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbomesh/9.png" alt="9.png | center | 826x206" title="">&lt;/p>
&lt;p>孤立地看待servicemesh其实和传统服务框架，价值还不算大，甚至成本相对更高。这时候，当我们把servicemesh设定到云原生的上下文中，就会发现不一样的意义。&lt;/p>
&lt;p>servicemesh是云原生理念的路径地图的第五步，如果没有前面的容器化，CICD等四部，真正拥抱servicemesh也只是空中楼阁。阿里在这方面的实践经验是，servicemesh的实施是需要结合软件开发的整个生命周期进行统筹，从软件在本地开发测试，到通过持续集成服务的自动化构建，再到以镜像方式分发到仓库并依托调度云平台的持续部署，最后持续监控。&lt;/p>
&lt;p>dubbo已经开源好多年，是非常符合云原生这个原则，正向servicemesh方向和云原生理念上努力，为企业信息化做出一点贡献。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>总结一下Dubbo Mesh是Dubbo在cloud native下的一种演进，这个演进是为了更加开放更加靠近标准协议规范的方向做的探索。通过分享希望大家能带走三点思考。&lt;/p>
&lt;ol>
&lt;li>servicemesh的多语言方案其实是走规范化标准化的协议之路，这样才能覆盖多语言的诉求。&lt;/li>
&lt;li>建议大家根据实际业务场景来慎重权衡sidecar模式下运维复杂性和收益回报。&lt;/li>
&lt;li>一定把servicemesh设定在云原生的上下文中才具意义，离开了Kubernetes谈servicemesh的实践是不建议的大跃进。
最后希望大家一起共建共享的Dubbo开源社区，谢谢。&lt;/li>
&lt;/ol></description></item><item><title>Blog: Dubbo服务端异步接口的实现背景和实践</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/dubboasyn_server/1.png" alt="workerthread-strategy.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 src="https://cn.dubbo.apache.org/imgs/blog/dubboasyn_server/2.png" alt="samethread-strategy.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 src="https://cn.dubbo.apache.org/imgs/blog/dubboasyn_server/3.png" alt="dynamic-strategy.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 src="https://cn.dubbo.apache.org/imgs/blog/dubboasyn_server/4.png" alt="leaderfollower-strategy.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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Main sayHello() method start.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> AsyncContext asyncContext &lt;span style="color:#719e07">=&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>startAsync&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Thread&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext&lt;span style="color:#719e07">.&lt;/span>signalContextSwitch&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Attachment from consumer: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">().&lt;/span>getAttachment&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;consumer-key1&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34; -- Async start.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">500&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>InterruptedException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext&lt;span style="color:#719e07">.&lt;/span>write&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34; -- Async end.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}).&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Main sayHello() method end.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="实践建议">实践建议&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>Blog: Dubbo客户端异步接口的实现背景和实践</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/dubboasyn_client/1.png" alt="image | left">&lt;/p>
&lt;p>先简单介绍下一次完整的Dubbo调用所经历的线程阶段。几个信息这里罗列下&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Biz~代表业务线程，即便是业务逻辑处理所处的线程，Biz~线程池可能是业务自己创建维护，大多数的可能是系统框架自身管理的（比如web型的业务系统跑在Tomcat容器下，Biz~线程就是Tomcat维护）；IO~代表网络数据处理线程，是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~线程，一直等到4代表的响应处理后才能唤醒。在这个短则微秒级别，长则秒级的1,2,3,4处理过程中都要阻塞Biz~线程，就会消耗线程资源，增加系统资源的开销。&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 style="color:#719e07">{&lt;/span>
&lt;/span>&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&lt;span style="color:#719e07">,&lt;/span> 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&lt;span style="color:#719e07">,&lt;/span> 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>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> id&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">,&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;onreturn: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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>&lt;span style="color:#719e07">(&lt;/span>Throwable ex&lt;span style="color:#719e07">,&lt;/span> String name&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> id&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;onthrow: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里要强调一点，自定义 Notify 接口中的三个方法的参数规则如下：&lt;/p>
&lt;ul>
&lt;li>&lt;code>oninvoke&lt;/code> 方法参数与调用方法的参数相同；&lt;/li>
&lt;li>&lt;code>onreturn&lt;/code>方法第一个参数为调用方法的返回值，其余为调用方法的参数；&lt;/li>
&lt;li>&lt;code>onthrow&lt;/code>方法第一个参数为调用异常，其余为调用方法的参数。&lt;/li>
&lt;/ul>
&lt;p>上述配置中，&lt;code>sayHello&lt;/code>方法为同步调用，因此事件通知方法的执行也是同步执行。可以配置 &lt;code>async=true&lt;/code>让方法调用为异步，这时事件通知的方法也是异步执行的。特别强调一下，&lt;code>oninvoke&lt;/code>方法不管是否异步调用，都是同步执行的。&lt;/p>
&lt;h3 id="实践建议">实践建议&lt;/h3>
&lt;ul>
&lt;li>
&lt;div data-type="alignment" data-value="justify" style="text-align:justify">
&lt;div data-type="p">RPC调用后的逻辑非强依赖结果：异步回调是在客户端&lt;strong>非强依赖服务端的结果&lt;/strong>情况下，是适用客户端的异步调用。&lt;/div>
&lt;/div>
&lt;/li>
&lt;li>
&lt;div data-type="alignment" data-value="justify" style="text-align:justify">
&lt;div data-type="p">rx场景：自从了解到reactive的编程模型后，认为只要编程思维能够拥抱reactive，并且业务模型的状态机设计能做适当的调整，任何场景下都比较适用异步来解决，从而得到更好的终端响应体验。 对于Dubbo来说，当下的异步接口模型是需要像reactive的模型接口做改进，才能使得用户更自然地适用异步接口。&lt;/div>
&lt;/div>
&lt;/li>
&lt;/ul>
&lt;h3 id="小结">小结&lt;/h3>
&lt;ul>
&lt;li>客户端异步的出发点就是请求发出和响应处理本身为两个不同的独立事件，响应如何被处理和在哪个线程中处理等都是不需要和请求发出事件的业务逻辑线程做耦合绑定。&lt;/li>
&lt;li>响应事件回调的处理逻辑在哪个线程中做处理是需要根据情况来选择。建议，如果回调逻辑比较简单，建议直接在IO线程中；如果包含了远程访问或者DB访问等IO型的__同步__操作，建议在独立的线程池做处理。&lt;/li>
&lt;/ul></description></item><item><title>Blog: Dubbo 在跨语言和协议穿透性方向上的探索：支持 HTTP/2 gRPC 和 Protobuf</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/grpc/dubbo-ptotocol.png" alt="image-20191029103919557">&lt;/p>
&lt;p>众所周知，Dubbo 协议是直接定义在 TCP 传输层协议之上，由于 TCP 高可靠全双工的特点，为 Dubbo 协议的定义提供了最大的灵活性，但同时也正是因为这样的灵活性，RPC 协议普遍都是定制化的私有协议，Dubbo 同样也面临这个问题。在这里我们着重讲一下 Dubbo 在协议通用性方面值得改进的地方，关于协议详细解析请参见&lt;a href="https://cn.dubbo.apache.org/zh/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>
&lt;p>之所有选择构建在 HTTP 之上，我认为有两个最大的优势：&lt;/p>
&lt;ol>
&lt;li>HTTP 的语义和可扩展性能很好的满足 RPC 调用需求。&lt;/li>
&lt;li>通用性，HTTP 协议几乎被网络上的所有设备所支持，具有很好的协议穿透性。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/grpc/http1.png" alt="image-20191029113404906">&lt;/p>
&lt;p>具体来说，HTTP/1 的优势和限制是：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>典型的 Request – Response 模型，一个链路上一次只能有一个等待的 Request 请求&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HTTP/1 支持 Keep-Alive 链接，避免了链接重复创建开销&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Human Readable Headers，使用更通用、更易于人类阅读的头部传输格式&lt;/p>
&lt;/li>
&lt;li>
&lt;p>无直接 Server Push 支持，需要使用 Polling Long-Polling 等变通模式&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="http2">HTTP/2&lt;/h3>
&lt;p>HTTP/2 保留了 HTTP/1 的所有语义，在保持兼容的同时，在通信模型和传输效率上做了很大的改进。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/grpc/http2.png" alt="image-20191029113416731">&lt;/p>
&lt;ul>
&lt;li>
&lt;p>支持单条链路上的 Multiplexing，相比于 Request - Response 独占链路，基于 Frame 实现更高效利用链路&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Request - Stream 语义，原生支持 Server Push 和 Stream 数据传输&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Flow Control，单条 Stream 粒度的和整个链路粒度的流量控制&lt;/p>
&lt;/li>
&lt;li>
&lt;p>头部压缩 HPACK&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Binary Frame&lt;/p>
&lt;/li>
&lt;li>
&lt;p>原生 TLS 支持&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="grpc">gRPC&lt;/h3>
&lt;p>上面提到了在 HTTP 及 TCP 协议之上构建 RPC 协议各自的优缺点，相比于 Dubbo 构建于 TPC 传输层之上，Google 选择将 gRPC 直接定义在 HTTP/2 协议之上，关于 gRPC 的 &lt;a href="https://grpc.io/docs/what-is-grpc/introduction/">基本介绍&lt;/a>和 &lt;a href="https://grpc.io/blog/principles/?spm=ata.13261165.0.0.2be55017XbUhs8">设计愿景&lt;/a> 请参考以上两篇文章，我这里仅摘取 设计愿景 中几个能反映 gRPC 设计目的特性来做简单说明。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Coverage &amp;amp; Simplicity，协议设计和框架实现要足够通用和简单，能运行在任何设备之上，甚至一些资源首先的如 IoT、Mobile 等设备。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Interoperability &amp;amp; Reach，要构建在更通用的协议之上，协议本身要能被网络上几乎所有的基础设施所支持。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>General Purpose &amp;amp; Performant，要在场景和性能间做好平衡，首先协议本身要是适用于各种场景的，同时也要尽量有高的性能。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Payload Agnostic，协议上传输的负载要保持语言和平台中立。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Streaming，要支持 Request - Response、Request - Stream、Bi-Steam 等通信模型。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Flow Control，协议自身具备流量感知和限制的能力。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Metadata Exchange，在 RPC 服务定义之外，提供额外附加数据传输的能力。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>总的来说，在这样的设计理念指导下，gRPC 最终被设计为一个跨语言、跨平台的、通用的、高性能的、基于 HTTP/2 的 RPC 协议和框架。&lt;/p>
&lt;h3 id="protobuf">Protobuf&lt;/h3>
&lt;p>&lt;a href="https://developers.google.com/protocol-buffers/docs/overview">Protocol buffers (Protobuf)&lt;/a> 是 Google 推出的一个跨平台、语言中立的结构化数据描述和序列化的产品，它定义了一套结构化数据定义的协议，同时也提供了相应的 &lt;a href="https://github.com/protocolbuffers/protobuf/releases/tag/v3.10.0">Compiler&lt;/a> 工具，用来将语言中立的描述转化为相应语言的具体描述。&lt;/p>
&lt;p>它的一些特性包括：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>跨语言 跨平台，语言中立的数据描述格式，默认提供了生成多种语言的 Compiler 工具。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>安全性，由于反序列化的范围和输出内容格式都是 Compiler 在编译时预生成的，因此绕过了类似 Java Deserialization Vulnarability 的问题。&lt;/p>
&lt;/li>
&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;div class="highlight">&lt;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>message Person {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> required string name = 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> required int32 id = 2;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> optional string email = 3;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> enum PhoneType {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MOBILE = 0;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HOME = 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> WORK = 2;
&lt;/span>&lt;/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> message PhoneNumber {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> required string number = 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> optional PhoneType type = 2 [default = HOME];
&lt;/span>&lt;/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> repeated PhoneNumber phone = 4;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>除了结构化数据描述之外，Protobuf 还支持定义 RPC 服务，它允许我们定义一个 &lt;code>.proto&lt;/code> 的服务描述文件，进而利用 Protobuf Compiler 工具生成特定语言和 RPC 框架的接口和 stub。后续将要具体讲到的 gRPC + Protobuf、Dubbo-gRPC + Protobuf 以及 Dubbo + Protobuf 都是通过定制 Compiler 类实现的。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>service SearchService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpc Search (SearchRequest) returns (SearchResponse);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="dubbo-所做的支持">Dubbo 所做的支持&lt;/h2>
&lt;p>跨语言的服务开发涉及到多个方面，从服务定义、RPC 协议到序列化协议都要做到语言中立，同时还针对每种语言有对应的 SDK 实现。虽然得益于社区的贡献，现在 Dubbo 在多语言 SDK 实现上逐步有了起色，已经提供了包括 Java, Go, PHP, C#, Python, NodeJs, C 等版本的客户端或全量实现版本，但在以上提到的跨语言友好型方面，以上三点还是有很多可改进之处。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>协议，上面我们已经分析过 Dubbo 协议既有的缺点，如果能在 HTTP/2 之上构建应用层协议，则无疑能避免这些弊端，同时最大可能的提高协议的穿透性，避免网关等协议转换组件的存在，更有利于链路上的流量管控。考虑到 gRPC 是构建在 HTTP/2 之上，并且已经是云原生领域推荐的通信协议，Dubbo 在第一阶段选择了直接支持 gRPC 协议作为当前的 HTTP/2 解决方案。我们也知道 gRPC 框架自身的弊端在于易用性不足以及服务治理能力欠缺（这也是目前绝大多数厂商不会直接裸用 gRPC 框架的原因），通过将其集成进 Dubbo 框架，用户可以方便的使用 Dubbo 编程模型 + Dubbo 服务治理 + gRPC 协议通信的组合。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>服务定义，当前 Dubbo 的服务定义和具体的编程语言绑定，没有提供一种语言中立的服务描述格式，比如 Java 就是定义 Interface 接口，到了其他语言又得重新以另外的格式定义一遍。因此 Dubbo 通过支持 Protobuf 实现了语言中立的服务定义。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>序列化，Dubbo 当前支持的序列化包括 Json、Hessian2、Kryo、FST、Java 等，而这其中支持跨语言的只有 Json、Hessian2，通用的 Json 有固有的性能问题，而 Hessian2 无论在效率还是多语言 SDK 方面都有所欠缺。为此，Dubbo 通过支持 Protobuf 序列化来提供更高效、易用的跨语言序列化方案。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="示例">示例&lt;/h2>
&lt;h3 id="示例-1使用-dubbo-开发-grpc-服务">示例 1，使用 Dubbo 开发 gRPC 服务&lt;/h3>
&lt;p>&lt;a href="https://grpc.io/">gRPC&lt;/a> 是 Google 开源的构建在 HTTP/2 之上的一个 PRC 通信协议。Dubbo 依赖其灵活的协议扩展机制，增加了对 gRPC (HTTP/2) 协议的支持。&lt;/p>
&lt;p>目前的支持限定在 Dubbo Java 语言版本，后续 Go 语言或其他语言版本将会以类似方式提供支持。下面，通过一个&lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-grpc">简单的示例&lt;/a>来演示如何在 Dubbo 中使用 gRPC 协议通信。&lt;/p>
&lt;h4 id="1-定义服务-idl">1. 定义服务 IDL&lt;/h4>
&lt;p>首先，通过标准的 Protobuf 协议定义服务如下：&lt;/p>
&lt;div class="highlight">&lt;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>syntax = &lt;span style="color:#2aa198">&amp;#34;proto3&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>option java_multiple_files = &lt;span style="color:#cb4b16">true&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>option java_package = &lt;span style="color:#2aa198">&amp;#34;io.grpc.examples.helloworld&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>option java_outer_classname = &lt;span style="color:#2aa198">&amp;#34;HelloWorldProto&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>option objc_class_prefix = &lt;span style="color:#2aa198">&amp;#34;HLW&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> helloworld;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The greeting service definition.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>service Greeter {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Sends a greeting
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> rpc &lt;span style="color:#268bd2">SayHello&lt;/span> (HelloRequest) &lt;span style="color:#268bd2">returns&lt;/span> (HelloReply) {}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The request message containing the user&amp;#39;s name.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>message HelloRequest {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">string&lt;/span> name = &lt;span style="color:#2aa198">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The response message containing the greetings
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>message HelloReply {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">string&lt;/span> message = &lt;span style="color:#2aa198">1&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>在此，我们定义了一个只有一个方法 sayHello 的 Greeter 服务，同时定义了方法的入参和出参，&lt;/p>
&lt;h4 id="2-pcompiler-生成-stub">2. PCompiler 生成 Stub&lt;/h4>
&lt;ol>
&lt;li>定义 Maven Protobuf Compiler 插件工具。这里我们扩展了 Protobuf 的 Compiler 工具，以用来生成 Dubbo 特有的 RPC stub，此当前以 Maven 插件的形式发布。&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-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;plugin&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.xolstice.maven.plugins&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>protobuf-maven-plugin&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.5.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;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;protocArtifact&amp;gt;&lt;/span>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/protocArtifact&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;pluginId&amp;gt;&lt;/span>dubbo-grpc-java&lt;span style="color:#268bd2">&amp;lt;/pluginId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;pluginArtifact&amp;gt;&lt;/span>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}&lt;span style="color:#268bd2">&amp;lt;/pluginArtifact&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;outputDirectory&amp;gt;&lt;/span>build/generated/source/proto/main/java&lt;span style="color:#268bd2">&amp;lt;/outputDirectory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;clearOutputDirectory&amp;gt;&lt;/span>false&lt;span style="color:#268bd2">&amp;lt;/clearOutputDirectory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;pluginParameter&amp;gt;&lt;/span>grpc&lt;span style="color:#268bd2">&amp;lt;/pluginParameter&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>compile&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>compile-custom&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中，&lt;/p>
&lt;p>pluginArtifact 指定了 Dubbo 定制版本的 Java Protobuf Compiler 插件，通过这个插件来在编译过程中生成 Dubbo 定制版本的 gRPC stub。&lt;/p>
&lt;div class="highlight">&lt;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;pluginArtifact&amp;gt;&lt;/span>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}&lt;span style="color:#268bd2">&amp;lt;/pluginArtifact&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由于 &lt;code>protoc-gen-dubbo-java&lt;/code> 支持 gRPC 和 Dubbo 两种协议，可生成的 stub 类型，默认值是 gRPC，关于 dubbo 协议的使用可参见 &lt;a href="https://cn.dubbo.apache.org/zh/docs3-v2/java-sdk/quick-start/idl/">使用 Protobuf 开发 Dubbo 服务&lt;/a>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;pluginParameter&amp;gt;&lt;/span>grpc&lt;span style="color:#268bd2">&amp;lt;/pluginParameter&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>
&lt;p>生成 Java Bean 和 Dubbo-gRPC stub&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># 运行以下 maven 命令&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ mvn clean compile
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>生成的 Stub 和消息类 如下：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/grpc/compiler-classes.png" alt="image-20191026130516896">&lt;/p>
&lt;p>重点关注 GreeterGrpc ，包含了所有 gRPC 标准的 stub 类/方法，同时增加了 Dubbo 特定的接口，之后 Provider 端的服务暴露和 Consumer 端的服务调用都将依赖这个接口。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Code generated for Dubbo
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">IGreeter&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> io&lt;span style="color:#719e07">.&lt;/span>grpc&lt;span style="color:#719e07">.&lt;/span>examples&lt;span style="color:#719e07">.&lt;/span>helloworld&lt;span style="color:#719e07">.&lt;/span>HelloReply &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>io&lt;span style="color:#719e07">.&lt;/span>grpc&lt;span style="color:#719e07">.&lt;/span>examples&lt;span style="color:#719e07">.&lt;/span>helloworld&lt;span style="color:#719e07">.&lt;/span>HelloRequest request&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> UnsupportedOperationException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;No need to override this method, extend XxxImplBase and override all methods it allows.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> com&lt;span style="color:#719e07">.&lt;/span>google&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>util&lt;span style="color:#719e07">.&lt;/span>concurrent&lt;span style="color:#719e07">.&lt;/span>ListenableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>io&lt;span style="color:#719e07">.&lt;/span>grpc&lt;span style="color:#719e07">.&lt;/span>examples&lt;span style="color:#719e07">.&lt;/span>helloworld&lt;span style="color:#719e07">.&lt;/span>HelloReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> io&lt;span style="color:#719e07">.&lt;/span>grpc&lt;span style="color:#719e07">.&lt;/span>examples&lt;span style="color:#719e07">.&lt;/span>helloworld&lt;span style="color:#719e07">.&lt;/span>HelloRequest request&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> UnsupportedOperationException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;No need to override this method, extend XxxImplBase and override all methods it allows.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>io&lt;span style="color:#719e07">.&lt;/span>grpc&lt;span style="color:#719e07">.&lt;/span>examples&lt;span style="color:#719e07">.&lt;/span>helloworld&lt;span style="color:#719e07">.&lt;/span>HelloRequest request&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> io&lt;span style="color:#719e07">.&lt;/span>grpc&lt;span style="color:#719e07">.&lt;/span>stub&lt;span style="color:#719e07">.&lt;/span>StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>io&lt;span style="color:#719e07">.&lt;/span>grpc&lt;span style="color:#719e07">.&lt;/span>examples&lt;span style="color:#719e07">.&lt;/span>helloworld&lt;span style="color:#719e07">.&lt;/span>HelloReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> responseObserver&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h4 id="3-业务逻辑开发">3. 业务逻辑开发&lt;/h4>
&lt;p>继承 &lt;code>GreeterGrpc.GreeterImplBase&lt;/code> （来自第 2 步），编写业务逻辑，这点和原生 gRPC 是一致的。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-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.samples.basic.impl&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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> io.grpc.examples.helloworld.GreeterGrpc&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> io.grpc.examples.helloworld.HelloReply&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> io.grpc.examples.helloworld.HelloRequest&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> io.grpc.stub.StreamObserver&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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">GrpcGreeterImpl&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> GreeterGrpc&lt;span style="color:#719e07">.&lt;/span>GreeterImplBase &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>HelloRequest request&lt;span style="color:#719e07">,&lt;/span> StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>HelloReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> responseObserver&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Received request from client.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Executing thread is &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> Thread&lt;span style="color:#719e07">.&lt;/span>currentThread&lt;span style="color:#719e07">().&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HelloReply reply &lt;span style="color:#719e07">=&lt;/span> HelloReply&lt;span style="color:#719e07">.&lt;/span>newBuilder&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setMessage&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> request&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()).&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> responseObserver&lt;span style="color:#719e07">.&lt;/span>onNext&lt;span style="color:#719e07">(&lt;/span>reply&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> responseObserver&lt;span style="color:#719e07">.&lt;/span>onCompleted&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="4-provider-端暴露-dubbo-服务">4. Provider 端暴露 Dubbo 服务&lt;/h4>
&lt;p>以 Spring 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:application&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;demo-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:#586e75">&amp;lt;!-- 指定服务暴露协议为 gRPC --&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> id=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;grpc&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:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address: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>&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;greeter&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.impl.GrpcGreeterImpl&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 指定 protoc-gen-dubbo-java 生成的接口 --&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;io.grpc.examples.helloworld.GreeterGrpc$IGreeter&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;greeter&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&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">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context &lt;span style="color:#719e07">=&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> ClassPathXmlApplicationContext&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;spring/dubbo-demo-provider.xml&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo service started&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> CountDownLatch&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">).&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="5-引用-dubbo-服务">5. 引用 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-xml" data-lang="xml">&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;demo-consumer&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:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address: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>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 指定 protoc-gen-dubbo-java 生成的接口 --&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;greeter&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;io.grpc.examples.helloworld.GreeterGrpc$IGreeter&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&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">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context &lt;span style="color:#719e07">=&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> ClassPathXmlApplicationContext&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;spring/dubbo-demo-consumer.xml&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> GreeterGrpc&lt;span style="color:#719e07">.&lt;/span>IGreeter greeter &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>GreeterGrpc&lt;span style="color:#719e07">.&lt;/span>IGreeter&lt;span style="color:#719e07">)&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;greeter&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HelloReply reply &lt;span style="color:#719e07">=&lt;/span> greeter&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>HelloRequest&lt;span style="color:#719e07">.&lt;/span>newBuilder&lt;span style="color:#719e07">().&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;world!&amp;#34;&lt;/span>&lt;span style="color:#719e07">).&lt;/span>build&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Result: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> reply&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="示例1附高级用法">示例1附：高级用法&lt;/h4>
&lt;p>&lt;strong>一、异步调用&lt;/strong>&lt;/p>
&lt;p>再来看一遍 &lt;code>protoc-gen-dubbo-java&lt;/code> 生成的接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Code generated for Dubbo
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">IGreeter&lt;/span> &lt;span style="color:#719e07">{&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:#268bd2">public&lt;/span> HelloReply &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>HelloRequest request&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ......
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> ListenableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>HelloReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>HelloRequest request&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ......
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>HelloRequest request&lt;span style="color:#719e07">,&lt;/span> StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>HelloReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> responseObserver&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里为 sayHello 方法生成了三种类型的重载方法，分别用于同步调用、异步调用和流式调用，如果消费端要进行异步调用，直接调用 sayHelloAsync() 即可：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GreeterGrpc&lt;span style="color:#719e07">.&lt;/span>IGreeter greeter &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>GreeterGrpc&lt;span style="color:#719e07">.&lt;/span>IGreeter&lt;span style="color:#719e07">)&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;greeter&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ListenableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>HelloReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greeter&lt;span style="color:#719e07">.&lt;/span>sayHAsyncello&lt;span style="color:#719e07">(&lt;/span>HelloRequest&lt;span style="color:#719e07">.&lt;/span>newBuilder&lt;span style="color:#719e07">().&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;world!&amp;#34;&lt;/span>&lt;span style="color:#719e07">).&lt;/span>build&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>二、高级配置&lt;/strong>&lt;/p>
&lt;p>由于当前实现方式是直接集成了 gRPC-java SDK，因此很多配置还没有和 Dubbo 侧对齐，或者还没有以 Dubbo 的配置形式开放，因此，为了提供最大的灵活性，我们直接把 gRPC-java 的配置接口暴露了出来。&lt;/p>
&lt;p>绝大多数场景下，你可能并不会用到以下扩展，因为它们更多的是对 gRPC 协议的拦截或者 HTTP/2 层面的配置。同时使用这些扩展点可能需要对 HTTP/2 或 gRPC 有基本的了解。&lt;/p>
&lt;p>&lt;strong>扩展点&lt;/strong>&lt;/p>
&lt;p>目前支持的扩展点如下：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>org.apache.dubbo.rpc.protocol.grpc.interceptors.ClientInterceptor&lt;/p>
&lt;/li>
&lt;li>
&lt;p>org.apache.dubbo.rpc.protocol.grpc.interceptors.GrpcConfigurator&lt;/p>
&lt;/li>
&lt;li>
&lt;p>org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerInterceptor&lt;/p>
&lt;/li>
&lt;li>
&lt;p>org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerTransportFilter&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>GrpcConfigurator 是最通用的扩展点，我们以此为例来说明一下，其基本定义如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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">GrpcConfigurator&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 用来定制 gRPC NettyServerBuilder
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">default&lt;/span> NettyServerBuilder &lt;span style="color:#268bd2">configureServerBuilder&lt;/span>&lt;span style="color:#719e07">(&lt;/span>NettyServerBuilder builder&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> builder&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 用来定制 gRPC NettyChannelBuilder
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">default&lt;/span> NettyChannelBuilder &lt;span style="color:#268bd2">configureChannelBuilder&lt;/span>&lt;span style="color:#719e07">(&lt;/span>NettyChannelBuilder builder&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> builder&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 用来定制 gRPC CallOptions, 定义某个服务在每次请求间传递数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">default&lt;/span> CallOptions &lt;span style="color:#268bd2">configureCallOptions&lt;/span>&lt;span style="color:#719e07">(&lt;/span>CallOptions options&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> options&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>以下是一个示例扩展实现：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MyGrpcConfigurator&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> GrpcConfigurator &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> ExecutorService executor &lt;span style="color:#719e07">=&lt;/span> Executors
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>newFixedThreadPool&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">200&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> NamedThreadFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Customized-grpc&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&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> NettyServerBuilder &lt;span style="color:#268bd2">configureServerBuilder&lt;/span>&lt;span style="color:#719e07">(&lt;/span>NettyServerBuilder builder&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> builder&lt;span style="color:#719e07">.&lt;/span>executor&lt;span style="color:#719e07">(&lt;/span>executor&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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> NettyChannelBuilder &lt;span style="color:#268bd2">configureChannelBuilder&lt;/span>&lt;span style="color:#719e07">(&lt;/span>NettyChannelBuilder builder&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> builder&lt;span style="color:#719e07">.&lt;/span>flowControlWindow&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">10&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&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> CallOptions &lt;span style="color:#268bd2">configureCallOptions&lt;/span>&lt;span style="color:#719e07">(&lt;/span>CallOptions options&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> options&lt;span style="color:#719e07">.&lt;/span>withOption&lt;span style="color:#719e07">(&lt;/span>CallOptions&lt;span style="color:#719e07">.&lt;/span>Key&lt;span style="color:#719e07">.&lt;/span>create&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;key&amp;#34;&lt;/span>&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#2aa198">&amp;#34;value&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置为 Dubbo SPI，`resources/META-INF/services 增加配置文件&lt;/p>
&lt;div class="highlight">&lt;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>default&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">org.apache.dubbo.samples.basic.comtomize.MyGrpcConfigurator&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>
&lt;p>指定 Provider 端线程池&lt;/p>
&lt;p>默认用的是 Dubbo 的线程池，有 fixed (默认)、cached、direct 等类型。以下演示了切换为业务自定义线程池。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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">final&lt;/span> ExecutorService executor &lt;span style="color:#719e07">=&lt;/span> Executors
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>newFixedThreadPool&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">200&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> NamedThreadFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Customized-grpc&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> NettyServerBuilder &lt;span style="color:#268bd2">configureServerBuilder&lt;/span>&lt;span style="color:#719e07">(&lt;/span>NettyServerBuilder builder&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> builder&lt;span style="color:#719e07">.&lt;/span>executor&lt;span style="color:#719e07">(&lt;/span>executor&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>指定 Consumer 端限流值&lt;/p>
&lt;p>设置 Consumer 限流值为 10&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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> NettyChannelBuilder &lt;span style="color:#268bd2">configureChannelBuilder&lt;/span>&lt;span style="color:#719e07">(&lt;/span>NettyChannelBuilder builder&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> builder&lt;span style="color:#719e07">.&lt;/span>flowControlWindow&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">10&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>传递附加参数&lt;/p>
&lt;p>DemoService 服务调用传递 key&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> CallOptions &lt;span style="color:#268bd2">configureCallOptions&lt;/span>&lt;span style="color:#719e07">(&lt;/span>CallOptions options&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getServiceInterface&lt;span style="color:#719e07">().&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;xxx.DemoService&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> options&lt;span style="color:#719e07">.&lt;/span>withOption&lt;span style="color:#719e07">(&lt;/span>CallOptions&lt;span style="color:#719e07">.&lt;/span>Key&lt;span style="color:#719e07">.&lt;/span>create&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;key&amp;#34;&lt;/span>&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#2aa198">&amp;#34;value&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> options&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>三、TLS 配置&lt;/strong>&lt;/p>
&lt;p>配置方式和 Dubbo 提供的通用的 &lt;a href="https://cn.dubbo.apache.org/zh/docs3-v2/java-sdk/advanced-features-and-usage/security/tls/">TLS 支持&lt;/a>一致，具体请参见文档&lt;/p>
&lt;h3 id="示例-2-使用-protobuf-开发-dubbo-服务">示例 2， 使用 Protobuf 开发 Dubbo 服务&lt;/h3>
&lt;p>下面，我们以一个&lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/serialization/dubbo-samples-protobuf">具体的示例&lt;/a>来看一下基于 Protobuf 的 Dubbo 服务开发流程。&lt;/p>
&lt;h4 id="1-定义服务">1. 定义服务&lt;/h4>
&lt;p>通过标准 Protobuf 定义服务&lt;/p>
&lt;div class="highlight">&lt;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>syntax = &lt;span style="color:#2aa198">&amp;#34;proto3&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>option java_multiple_files = &lt;span style="color:#cb4b16">true&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>option java_package = &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>option java_outer_classname = &lt;span style="color:#2aa198">&amp;#34;DemoServiceProto&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>option objc_class_prefix = &lt;span style="color:#2aa198">&amp;#34;DEMOSRV&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> demoservice;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The demo service definition.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>service DemoService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpc &lt;span style="color:#268bd2">SayHello&lt;/span> (HelloRequest) &lt;span style="color:#268bd2">returns&lt;/span> (HelloReply) {}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The request message containing the user&amp;#39;s name.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>message HelloRequest {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">string&lt;/span> name = &lt;span style="color:#2aa198">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The response message containing the greetings
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>message HelloReply {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">string&lt;/span> message = &lt;span style="color:#2aa198">1&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>这里定义了一个 DemoService 服务，服务只包含一个 sayHello 方法，同时定义了方法的入参和出参。&lt;/p>
&lt;h4 id="2-compiler-编译服务">2. Compiler 编译服务&lt;/h4>
&lt;ol>
&lt;li>引入 Protobuf Compiler Maven 插件，同时指定 &lt;code>protoc-gen-dubbo-java&lt;/code> RPC 扩展&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-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;plugin&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.xolstice.maven.plugins&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>protobuf-maven-plugin&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.5.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;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;protocArtifact&amp;gt;&lt;/span>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/protocArtifact&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;pluginId&amp;gt;&lt;/span>dubbo-grpc-java&lt;span style="color:#268bd2">&amp;lt;/pluginId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;pluginArtifact&amp;gt;&lt;/span>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}&lt;span style="color:#268bd2">&amp;lt;/pluginArtifact&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;outputDirectory&amp;gt;&lt;/span>build/generated/source/proto/main/java&lt;span style="color:#268bd2">&amp;lt;/outputDirectory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;clearOutputDirectory&amp;gt;&lt;/span>false&lt;span style="color:#268bd2">&amp;lt;/clearOutputDirectory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;pluginParameter&amp;gt;&lt;/span>dubbo&lt;span style="color:#268bd2">&amp;lt;/pluginParameter&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>compile&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>compile-custom&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意，这里与 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-grpc">Dubbo 对 gRPC&lt;/a> 支持部分的区别在于：
&lt;code> &amp;lt;pluginParameter&amp;gt;dubbo&amp;lt;/pluginParameter&amp;gt;&lt;/code>&lt;/p>
&lt;ol start="2">
&lt;li>
&lt;p>生成 Dubbo stub&lt;/p>
&lt;div class="highlight">&lt;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>&lt;span style="color:#586e75"># 运行以下 maven 命令&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">$mvn&lt;/span> clean compile
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>生成的 Java 类如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/grpc/compiler-protobuf.png" alt="image-20191028201240976">&lt;/p>
&lt;p>DemoServiceDubbo 为 Dubbo 定制的 stub&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceDubbo&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> AtomicBoolean registered &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AtomicBoolean&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&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> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> init&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clazz &lt;span style="color:#719e07">=&lt;/span> Class&lt;span style="color:#719e07">.&lt;/span>forName&lt;span style="color:#719e07">(&lt;/span>DemoServiceDubbo&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>registered&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>serialize&lt;span style="color:#719e07">.&lt;/span>protobuf&lt;span style="color:#719e07">.&lt;/span>support&lt;span style="color:#719e07">.&lt;/span>ProtobufUtils&lt;span style="color:#719e07">.&lt;/span>marshaller&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>demo&lt;span style="color:#719e07">.&lt;/span>HelloRequest&lt;span style="color:#719e07">.&lt;/span>getDefaultInstance&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>serialize&lt;span style="color:#719e07">.&lt;/span>protobuf&lt;span style="color:#719e07">.&lt;/span>support&lt;span style="color:#719e07">.&lt;/span>ProtobufUtils&lt;span style="color:#719e07">.&lt;/span>marshaller&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>demo&lt;span style="color:#719e07">.&lt;/span>HelloReply&lt;span style="color:#719e07">.&lt;/span>getDefaultInstance&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ClassNotFoundException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> clazz&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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">DemoServiceDubbo&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> String SERVICE_NAME &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;demoservice.DemoService&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Code generated for Dubbo
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">IDemoService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">static&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz &lt;span style="color:#719e07">=&lt;/span> init&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>demo&lt;span style="color:#719e07">.&lt;/span>HelloReply &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>demo&lt;span style="color:#719e07">.&lt;/span>HelloRequest request&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> java&lt;span style="color:#719e07">.&lt;/span>util&lt;span style="color:#719e07">.&lt;/span>concurrent&lt;span style="color:#719e07">.&lt;/span>CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>demo&lt;span style="color:#719e07">.&lt;/span>HelloReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>demo&lt;span style="color:#719e07">.&lt;/span>HelloRequest request&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>最值得注意的是 &lt;code>IDemoService&lt;/code> 接口，它会作为 Dubbo 服务定义基础接口。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h4 id="3-开发业务逻辑">3. 开发业务逻辑&lt;/h4>
&lt;p>从这一步开始，所有开发流程就和直接定义 Java 接口一样了。实现接口定义业务逻辑。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoServiceDubbo&lt;span style="color:#719e07">.&lt;/span>IDemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Logger logger &lt;span style="color:#719e07">=&lt;/span> LoggerFactory&lt;span style="color:#719e07">.&lt;/span>getLogger&lt;span style="color:#719e07">(&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> HelloReply &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>HelloRequest request&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> request&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, request from consumer: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">().&lt;/span>getRemoteAddress&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> HelloReply&lt;span style="color:#719e07">.&lt;/span>newBuilder&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setMessage&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> request&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider: &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">().&lt;/span>getLocalAddress&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&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>HelloReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>HelloRequest request&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>completedFuture&lt;span style="color:#719e07">(&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>request&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="4-配置-provider">4. 配置 Provider&lt;/h4>
&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-xml" data-lang="xml">&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;demo-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;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>&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>&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;demoService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.provider.DemoServiceImpl&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.DemoServiceDubbo$IDemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context &lt;span style="color:#719e07">=&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> ClassPathXmlApplicationContext&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;spring/dubbo-provider.xml&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="5-配置-consumer">5. 配置 Consumer&lt;/h4>
&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-xml" data-lang="xml">&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;demo-consumer&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: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>&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;org.apache.dubbo.demo.DemoServiceDubbo$IDemoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&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">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context &lt;span style="color:#719e07">=&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> ClassPathXmlApplicationContext&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;spring/dubbo-consumer.xml&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> IDemoService demoService &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> IDemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HelloRequest request &lt;span style="color:#719e07">=&lt;/span> HelloRequest&lt;span style="color:#719e07">.&lt;/span>newBuilder&lt;span style="color:#719e07">().&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello&amp;#34;&lt;/span>&lt;span style="color:#719e07">).&lt;/span>build&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HelloReply reply &lt;span style="color:#719e07">=&lt;/span> demoService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>request&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;result: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> reply&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: 本地存根和本地伪装</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> preProcess&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(...);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> mockValue&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> postProcess&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>类似的，用户也可以通过面向切面编程 &lt;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>
&lt;p>本地存根的工作方式与 &lt;em>AOP&lt;/em> 的 &lt;strong>around&lt;/strong> advice 类似，而本地伪装的工作方式等同于 &lt;em>AOP&lt;/em> 中的 &lt;strong>after-throwing&lt;/strong> advice，也就是说，只有当远程调用发生 &lt;em>exception&lt;/em> 的时候才会执行本地伪装。本地存根和本地伪装的工作流程如下图所示：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-mock-stub-flow.png" alt="dubbo-mock-stub-flow">&lt;/p>
&lt;ol>
&lt;li>服务消费者发起调用&lt;/li>
&lt;li>如果服务消费者端存在本地存根 &lt;em>Stub&lt;/em> 的话，会先执行本地存根&lt;/li>
&lt;li>本地存根 Stub 持有远程服务的 &lt;em>Proxy&lt;/em> 对象，&lt;em>Stub&lt;/em> 在执行的时候，会先执行自己的逻辑 (&lt;em>before&lt;/em>)，然后通过 &lt;em>Proxy&lt;/em> 发起远程调用，最后在返回过程之前也会执行自己的逻辑 (&lt;em>after-returning&lt;/em>)&lt;/li>
&lt;li>如果远程服务的 &lt;em>Proxy&lt;/em> 对象在执行过程中抛出了 &lt;em>exception&lt;/em>，会执行服务消费端的本地伪装 &lt;em>Mock&lt;/em> 的逻辑 (&lt;em>after-throwing&lt;/em>)，返回容错数据，从而达到服务降级的目的&lt;/li>
&lt;/ol>
&lt;h2 id="开发一个本地存根-stub">开发一个本地存根 Stub&lt;/h2>
&lt;p>本地存根 &lt;em>Stub&lt;/em> 由用户来提供，并在服务消费方部署。完整的示例可以在这里 &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> 获得。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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">DemoServiceStub&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> Logger logger &lt;span style="color:#719e07">=&lt;/span> LoggerFactory&lt;span style="color:#719e07">.&lt;/span>getLogger&lt;span style="color:#719e07">(&lt;/span>DemoServiceStub&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> DemoService demoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">DemoServiceStub&lt;/span>&lt;span style="color:#719e07">(&lt;/span>DemoService demoService&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>demoService &lt;span style="color:#719e07">=&lt;/span> demoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;before execute remote service, parameter: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String result &lt;span style="color:#719e07">=&lt;/span> demoService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;after execute remote service, result: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> result&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #6
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;fail to execute service&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #7
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>要和框架在一起工作，本地存根的实现需要遵循一些与框架事先做出的约定：&lt;/p>
&lt;ol>
&lt;li>首先本地存根 &lt;em>Stub&lt;/em> 是服务接口的一个实现&lt;/li>
&lt;li>本地存根的实现需要提供一个拷贝构造方法，方便框架将远程调用的 &lt;em>Proxy&lt;/em> 对象注入进来&lt;/li>
&lt;li>同样的，本地存根需要提供服务接口中所有方法的实现。在本例中，需要实现 &lt;em>sayHello&lt;/em> 方法&lt;/li>
&lt;li>在真正发起远程调用之前，用户可以在本地执行一些操作。在本例中，在日志中记录传入的参数&lt;/li>
&lt;li>通过框架传入的 &lt;em>Proxy&lt;/em> 对象真正发起远程调用&lt;/li>
&lt;li>在远程调用结束后，也可以加入本地代码的执行。在本例中，在日志中记录远程调用的返回结果&lt;/li>
&lt;li>如果发生错误的时候，也可以做一些错误恢复的动作。在本例中，在日志中记录异常。当然，如果提供了本地伪装的话，&lt;em>catch&lt;/em> 中的逻辑是可以省略掉的&lt;/li>
&lt;/ol>
&lt;p>其中步骤 4、步骤 6、和步骤 7 共同构建了等同于面向切面编程中的概念，分别对应于 &lt;strong>before&lt;/strong>、&lt;strong>after-returning&lt;/strong>、以及 &lt;strong>after-throwing&lt;/strong>。&lt;/p>
&lt;p>&lt;em>DemoServiceStub&lt;/em> 运行在客户端，要使用本地存根的话，还需要在 &lt;em>stub-consumer.xml&lt;/em> 中配置属性 &lt;em>stub&lt;/em>。可以简单的通过指定 &lt;em>stub=&amp;ldquo;true&amp;rdquo;&lt;/em> 来告诉 Dubbo 框架使用本地存根，这个时候，本地存根的包名需要和服务接口的包名一致，类名必须在服务接口的类名后加上 &lt;strong>Stub&lt;/strong> 的后缀。例如，当服务接口名是 &lt;em>org.apache.dubbo.samples.stub.api.DemoService&lt;/em> 时，本地存根的全类名应该是 &lt;em>org.apache.dubbo.samples.stub.api.DemoServiceStub&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;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;org.apache.dubbo.samples.stub.api.DemoService&amp;#34;&lt;/span> stub=&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>如果不希望使用默认的命名规则，也可以直接通过 &lt;em>stub&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;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;org.apache.dubbo.samples.stub.api.DemoService&amp;#34;&lt;/span> stub=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.stub.impl.DemoStub&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>StubProvider&lt;/em> 后，再运行客户端 &lt;em>StubConsumer&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>09/04/19 11:52:21:021 CST&lt;span style="color:#719e07">]&lt;/span> main INFO api.DemoServiceStub: before execute remote service, parameter: dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>09/04/19 11:52:21:021 CST&lt;span style="color:#719e07">]&lt;/span> main INFO api.DemoServiceStub: after execute remote service, result: greeting dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>09/04/19 11:52:21:021 CST&lt;span style="color:#719e07">]&lt;/span> main INFO stub.StubConsumer: result: greeting dubbo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="开发一个本地伪装-mock">开发一个本地伪装 Mock&lt;/h2>
&lt;p>上面说了本地伪装通常用于在远程调用出错的情况下服务降级。完整的示例可以在这里 &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> 获得。&lt;/p>
&lt;p>这里通过在服务提供方的代码中睡眠来模拟调用端超时，从而执行本地伪装来做容错处理。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">5000&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>InterruptedException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>Dubbo 默认的超时时间是 &lt;em>1000 ms&lt;/em>，这里通过睡眠 &lt;em>5000ms&lt;/em> 来达到触发超时异常的发生&lt;/li>
&lt;li>由于超时的发生，这个结果并不会被返回给客户端，取而代之的是 &lt;em>org.apache.dubbo.remoting.TimeoutException&lt;/em>&lt;/li>
&lt;/ol>
&lt;p>在客户端提供本地伪装的实现。当远程调用发生错误的时候，返回给调用方的不是服务端的 &amp;ldquo;hello name&amp;rdquo;，取而代之的是 &amp;ldquo;mock name&amp;rdquo;。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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">DemoServiceMock&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> Logger logger &lt;span style="color:#719e07">=&lt;/span> LoggerFactory&lt;span style="color:#719e07">.&lt;/span>getLogger&lt;span style="color:#719e07">(&lt;/span>DemoServiceMock&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;about to execute mock: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> DemoServiceMock&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getSimpleName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;mock &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样的，要使用本地伪装的话，还需要在 &lt;em>mock-consumer.xml&lt;/em> 中配置属性 &lt;em>mock&lt;/em>。可以简单的通过指定 &lt;em>mock=&amp;ldquo;true&amp;rdquo;&lt;/em> 来告诉 Dubbo 框架使用本地伪装，这个时候，本地伪装的包名需要和服务接口的包名一致，类名必须在服务接口的类名后加上 &lt;strong>Mock&lt;/strong> 的后缀。例如，当服务接口名是 &lt;em>org.apache.dubbo.samples.stub.api.DemoService&lt;/em> 时，本地存根的全类名应该是 &lt;em>org.apache.dubbo.samples.stub.api.DemoServiceMock&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;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;org.apache.dubbo.samples.mock.api.DemoService&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mock=&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>如果不希望使用默认的命名规则，也可以直接通过 &lt;em>mock&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;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;org.apache.dubbo.samples.mock.api.DemoService&amp;#34;&lt;/span> mock=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.mock.impl.DemoMock&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>通过提供一个本地伪装的类，可以最大限度的控制出错之后的容错逻辑。有的时候，业务上并不需要这样灵活的机制，只有返回一个默认值的诉求，这个时候提供一个完整的本地伪装的实现就显得有点重了。或者线上出错的时候，应用并没有打包本地伪装，需要通过推送规则的方式临时对服务降级。Dubbo 框架为上面的这两种诉求都提供了快捷方式，帮助用户快速配置服务降级。&lt;/p>
&lt;p>启动服务端 &lt;em>MockProvider&lt;/em> 之后，然后再执行 &lt;em>MockConsumer&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>Caused by: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer. start time: 2019-04-09 14:20:48.061, end time: 2019-04-09 14:20:49.077, client elapsed: &lt;span style="color:#2aa198">0&lt;/span> ms, server elapsed: &lt;span style="color:#2aa198">1015&lt;/span> ms, timeout: &lt;span style="color:#2aa198">1000&lt;/span> ms, request: Request &lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#268bd2">id&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2, &lt;span style="color:#268bd2">version&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.0.2, &lt;span style="color:#268bd2">twoway&lt;/span>&lt;span style="color:#719e07">=&lt;/span>true, &lt;span style="color:#268bd2">event&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false, &lt;span style="color:#268bd2">broken&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false, &lt;span style="color:#268bd2">data&lt;/span>&lt;span style="color:#719e07">=&lt;/span>RpcInvocation &lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#268bd2">methodName&lt;/span>&lt;span style="color:#719e07">=&lt;/span>sayHello, &lt;span style="color:#268bd2">parameterTypes&lt;/span>&lt;span style="color:#719e07">=[&lt;/span>class java.lang.String&lt;span style="color:#719e07">]&lt;/span>, &lt;span style="color:#268bd2">arguments&lt;/span>&lt;span style="color:#719e07">=[&lt;/span>world&lt;span style="color:#719e07">]&lt;/span>, &lt;span style="color:#268bd2">attachments&lt;/span>&lt;span style="color:#719e07">={&lt;/span>&lt;span style="color:#268bd2">path&lt;/span>&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.samples.mock.api.DemoService, &lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.samples.mock.api.DemoService, &lt;span style="color:#268bd2">version&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0.0.0&lt;span style="color:#719e07">}]]&lt;/span>, channel: /30.5.125.99:56433 -&amp;gt; /30.5.125.99:20880
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.returnFromResponse&lt;span style="color:#719e07">(&lt;/span>DefaultFuture.java:295&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get&lt;span style="color:#719e07">(&lt;/span>DefaultFuture.java:191&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get&lt;span style="color:#719e07">(&lt;/span>DefaultFuture.java:164&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke&lt;span style="color:#719e07">(&lt;/span>DubboInvoker.java:108&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.AbstractInvoker.invoke&lt;span style="color:#719e07">(&lt;/span>AbstractInvoker.java:157&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.monitor.support.MonitorFilter.invoke&lt;span style="color:#719e07">(&lt;/span>MonitorFilter.java:88&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:73&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke&lt;span style="color:#719e07">(&lt;/span>FutureFilter.java:49&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:73&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke&lt;span style="color:#719e07">(&lt;/span>ConsumerContextFilter.java:54&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:73&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke&lt;span style="color:#719e07">(&lt;/span>ListenerInvokerWrapper.java:78&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke&lt;span style="color:#719e07">(&lt;/span>InvokerWrapper.java:56&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke&lt;span style="color:#719e07">(&lt;/span>FailoverClusterInvoker.java:80&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ... &lt;span style="color:#2aa198">5&lt;/span> more
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>09/04/19 02:20:49:049 CST&lt;span style="color:#719e07">]&lt;/span> main WARN api.DemoServiceMock: about to execute mock: DemoServiceMock
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>09/04/19 02:20:49:049 CST&lt;span style="color:#719e07">]&lt;/span> main INFO mock.MockConsumer: result: mock world
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下面通过规则推送为例展示这种快捷方式的用法，更多的用法请参考 Dubbo 官方用户手册 &lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>。通过向配置中心推送指定服务的配置，就可以做到动态服务降级的目的。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>--- &lt;span style="color:#586e75"># 1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">configVersion&lt;/span>: v2.7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">scope&lt;/span>: service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">key&lt;/span>: org.apache.dubbo.samples.mock.api.DemoService &lt;span style="color:#586e75">#2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">enabled&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">configs&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">addresses&lt;/span>: [&lt;span style="color:#2aa198">0.0.0.0&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">side&lt;/span>: consumer &lt;span style="color:#586e75">#3&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">parameters&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">mock&lt;/span>: return configured-mock-value &lt;span style="color:#586e75">#4&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>
&lt;li>以 &lt;em>Zookeeper&lt;/em> 为例，规则的路径是 /dubbo/config/org.apache.dubbo.samples.mock.api.DemoService/configurators&lt;/li>
&lt;li>该规则作用在 &lt;em>org.apache.dubbo.samples.mock.api.DemoService&lt;/em> 服务上&lt;/li>
&lt;li>该规则作用在客户端&lt;/li>
&lt;li>当错误发送时，对服务的调用返回默认值 &lt;em>configured-mock-value&lt;/em>&lt;/li>
&lt;/ol>
&lt;p>启动服务端 &lt;em>MockProvider&lt;/em> 之后，再执行例子&lt;sup id="fnref1:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>中的 &lt;em>Configurator&lt;/em> 完成对服务降级规则的配置，然后再执行 &lt;em>MockConsumer&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>09/04/19 02:19:01:001 CST&lt;span style="color:#719e07">]&lt;/span> main INFO integration.AbstractConfiguratorListener: &lt;span style="color:#719e07">[&lt;/span>DUBBO&lt;span style="color:#719e07">]&lt;/span> Notification of overriding rule, change &lt;span style="color:#b58900">type&lt;/span> is: MODIFIED, raw config content is:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>configVersion: v2.7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>scope: service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>key: org.apache.dubbo.samples.mock.api.DemoService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>enabled: &lt;span style="color:#b58900">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>configs:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- addresses: &lt;span style="color:#719e07">[&lt;/span>0.0.0.0&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> side: consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> parameters:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mock: &lt;span style="color:#719e07">return&lt;/span> configured-mock-value
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>, dubbo version: 2.7.1, current host: 30.5.125.99
&lt;/span>&lt;/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>Caused by: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout. start time: 2019-04-09 14:19:03.737, end time: 2019-04-09 14:19:04.741, client elapsed: &lt;span style="color:#2aa198">1&lt;/span> ms, server elapsed: &lt;span style="color:#2aa198">1002&lt;/span> ms, timeout: &lt;span style="color:#2aa198">1000&lt;/span> ms, request: Request &lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#268bd2">id&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2, &lt;span style="color:#268bd2">version&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.0.2, &lt;span style="color:#268bd2">twoway&lt;/span>&lt;span style="color:#719e07">=&lt;/span>true, &lt;span style="color:#268bd2">event&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false, &lt;span style="color:#268bd2">broken&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false, &lt;span style="color:#268bd2">data&lt;/span>&lt;span style="color:#719e07">=&lt;/span>RpcInvocation &lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#268bd2">methodName&lt;/span>&lt;span style="color:#719e07">=&lt;/span>sayHello, &lt;span style="color:#268bd2">parameterTypes&lt;/span>&lt;span style="color:#719e07">=[&lt;/span>class java.lang.String&lt;span style="color:#719e07">]&lt;/span>, &lt;span style="color:#268bd2">arguments&lt;/span>&lt;span style="color:#719e07">=[&lt;/span>world&lt;span style="color:#719e07">]&lt;/span>, &lt;span style="color:#268bd2">attachments&lt;/span>&lt;span style="color:#719e07">={&lt;/span>&lt;span style="color:#268bd2">path&lt;/span>&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.samples.mock.api.DemoService, &lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.samples.mock.api.DemoService, &lt;span style="color:#268bd2">version&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0.0.0&lt;span style="color:#719e07">}]]&lt;/span>, channel: /30.5.125.99:56412 -&amp;gt; /30.5.125.99:20880
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get&lt;span style="color:#719e07">(&lt;/span>DefaultFuture.java:188&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.remoting.exchange.support.DefaultFuture.get&lt;span style="color:#719e07">(&lt;/span>DefaultFuture.java:164&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke&lt;span style="color:#719e07">(&lt;/span>DubboInvoker.java:108&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.AbstractInvoker.invoke&lt;span style="color:#719e07">(&lt;/span>AbstractInvoker.java:157&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.monitor.support.MonitorFilter.invoke&lt;span style="color:#719e07">(&lt;/span>MonitorFilter.java:88&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:73&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke&lt;span style="color:#719e07">(&lt;/span>FutureFilter.java:49&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:73&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.filter.ConsumerContextFilter.invoke&lt;span style="color:#719e07">(&lt;/span>ConsumerContextFilter.java:54&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;span style="color:#268bd2">$1&lt;/span>.invoke&lt;span style="color:#719e07">(&lt;/span>ProtocolFilterWrapper.java:73&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke&lt;span style="color:#719e07">(&lt;/span>ListenerInvokerWrapper.java:78&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke&lt;span style="color:#719e07">(&lt;/span>InvokerWrapper.java:56&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke&lt;span style="color:#719e07">(&lt;/span>FailoverClusterInvoker.java:80&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ... &lt;span style="color:#2aa198">5&lt;/span> more
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>09/04/19 02:19:04:004 CST&lt;span style="color:#719e07">]&lt;/span> main INFO mock.MockConsumer: result: configured-mock-value
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="总结">总结&lt;/h2>
&lt;p>本文介绍了 Dubbo 中本地存根和本地伪装的概念和用法。从本质来讲，本地存根和本地伪装等同于面向切面编程中的概念，通过诸如 Spring 框架提供的 &lt;em>AOP&lt;/em> 编程可以达到同样的目的。通过本文如何开发一个本地存根和本地伪装的示例，读者可以直观的感受到通过框架提供的机制更加方便快捷。同时，对于很多简单的场景和动态配置推送的场景，框架提供了仅通过配置而无需编码的方式来满足，进一步提升了框架使用者的效率。&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-stub">https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-stub&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-mock">https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-mock&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref1:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>/zh/docsv2.7/user/examples/local-mock/&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Blog: 研究 Dubbo 网卡地址注册时的一点思考</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&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 style="color:#719e07">;&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>&lt;span style="color:#586e75">&lt;/span> localAddress &lt;span style="color:#719e07">=&lt;/span> InetAddress&lt;span style="color:#719e07">.&lt;/span>getLocalHost&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 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&lt;span style="color:#719e07">(&lt;/span>localAddress&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>addressOp&lt;span style="color:#719e07">.&lt;/span>isPresent&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> addressOp&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 没有找到适合注册的 IP，则开始轮询网卡
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Enumeration&lt;span style="color:#719e07">&amp;lt;&lt;/span>NetworkInterface&lt;span style="color:#719e07">&amp;gt;&lt;/span> interfaces &lt;span style="color:#719e07">=&lt;/span> NetworkInterface&lt;span style="color:#719e07">.&lt;/span>getNetworkInterfaces&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">==&lt;/span> interfaces&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> localAddress&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">(&lt;/span>interfaces&lt;span style="color:#719e07">.&lt;/span>hasMoreElements&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NetworkInterface network &lt;span style="color:#719e07">=&lt;/span> interfaces&lt;span style="color:#719e07">.&lt;/span>nextElement&lt;span style="color:#719e07">();&lt;/span>
&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&lt;span style="color:#719e07">.&lt;/span>getInetAddresses&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">(&lt;/span>addresses&lt;span style="color:#719e07">.&lt;/span>hasMoreElements&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 返回第一个匹配的适合注册的 IP
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Optional&lt;span style="color:#719e07">&amp;lt;&lt;/span>InetAddress&lt;span style="color:#719e07">&amp;gt;&lt;/span> addressOp &lt;span style="color:#719e07">=&lt;/span> toValidAddress&lt;span style="color:#719e07">(&lt;/span>addresses&lt;span style="color:#719e07">.&lt;/span>nextElement&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>addressOp&lt;span style="color:#719e07">.&lt;/span>isPresent&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> addressOp&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> localAddress&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo 这段选取本地地址的逻辑大致分成了两步&lt;/p>
&lt;ol>
&lt;li>先去 /etc/hosts 文件中找 hostname 对应的 IP 地址，找到则返回；找不到则转 2&lt;/li>
&lt;li>轮询网卡，寻找合适的 IP 地址，找到则返回；找不到返回 null，在 getLocalAddress0 外侧还有一段逻辑，如果返回 null，则注册 127.0.0.1 这个本地回环地址&lt;/li>
&lt;/ol>
&lt;p>首先强调下，这段逻辑并没有太大的问题，先别急着挑刺，让我们来分析下其中的一些细节，并进行验证。&lt;/p>
&lt;h3 id="21-尝试获取-hostname-映射-ip">2.1 尝试获取 hostname 映射 IP&lt;/h3>
&lt;p>Dubbo 首先选取的是 hostname 对应的 IP，在源码中对应的 &lt;code>InetAddress.getLocalHost();&lt;/code> 在 &lt;code>*nix&lt;/code> 系统实际部署 Dubbo 应用时，可以首先使用 &lt;code>hostname&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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>xujingfengdeMacBook-Pro:~ xujingfeng$ hostname
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>xujingfengdeMacBook-Pro.local
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>紧接着在 &lt;code>/etc/hosts&lt;/code> 配置 IP 映射，为了验证 Dubbo 的机制，我们随意为 hostname 配置一个 IP 地址&lt;/p>
&lt;div class="highlight">&lt;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>127.0.0.1 localhost
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>1.2.3.4 xujingfengdeMacBook-Pro.local
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>接着调用 &lt;code>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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>xujingfengdeMacBook-Pro.local/1.2.3.4
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="22-判定有效的-ip-地址">2.2 判定有效的 IP 地址&lt;/h3>
&lt;p>在 toValidAddress 逻辑中，Dubbo 存在以下逻辑判定一个 IP 地址是否有效&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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> Optional&lt;span style="color:#719e07">&amp;lt;&lt;/span>InetAddress&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">toValidAddress&lt;/span>&lt;span style="color:#719e07">(&lt;/span>InetAddress address&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>address &lt;span style="color:#719e07">instanceof&lt;/span> Inet6Address&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Inet6Address v6Address &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Inet6Address&lt;span style="color:#719e07">)&lt;/span> address&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isValidV6Address&lt;span style="color:#719e07">(&lt;/span>v6Address&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> Optional&lt;span style="color:#719e07">.&lt;/span>ofNullable&lt;span style="color:#719e07">(&lt;/span>normalizeV6Address&lt;span style="color:#719e07">(&lt;/span>v6Address&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isValidV4Address&lt;span style="color:#719e07">(&lt;/span>address&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> Optional&lt;span style="color:#719e07">.&lt;/span>of&lt;span style="color:#719e07">(&lt;/span>address&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> Optional&lt;span style="color:#719e07">.&lt;/span>empty&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>依次校验其符合 Ipv6 或者 Ipv4 的 IP 规范，对于 Ipv6 的地址，见如下代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isValidV6Address&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Inet6Address address&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> preferIpv6 &lt;span style="color:#719e07">=&lt;/span> Boolean&lt;span style="color:#719e07">.&lt;/span>getBoolean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;java.net.preferIPv6Addresses&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>preferIpv6&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> address&lt;span style="color:#719e07">.&lt;/span>isReachable&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">100&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>IOException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>首先获取 &lt;code>java.net.preferIPv6Addresses&lt;/code> 参数，其默认值为 false，鉴于大多数应用并没有使用 Ipv6 地址作为理想的注册 IP，这问题不大，紧接着通过 isReachable 判断网卡的连通性。例如一些网卡可能是 VPN/虚拟网卡的地址，如果没有配置路由表，往往无法连通，可以将之过滤。&lt;/p>
&lt;p>对于 Ipv4 的地址，见如下代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isValidV4Address&lt;/span>&lt;span style="color:#719e07">(&lt;/span>InetAddress address&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>address &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> address&lt;span style="color:#719e07">.&lt;/span>isLoopbackAddress&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String name &lt;span style="color:#719e07">=&lt;/span> address&lt;span style="color:#719e07">.&lt;/span>getHostAddress&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>name &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">&amp;amp;&amp;amp;&lt;/span> IP_PATTERN&lt;span style="color:#719e07">.&lt;/span>matcher&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">).&lt;/span>matches&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>ANYHOST_VALUE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&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&lt;span style="color:#719e07">.&lt;/span>LOCALHOST_VALUE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对比 Ipv6 的判断，这里我们已经发现前后不对称的情况了&lt;/p>
&lt;ul>
&lt;li>Ipv4 相比 Ipv6 的逻辑多了 Ipv4 格式的正则校验、本地回环地址校验、ANYHOST 校验&lt;/li>
&lt;li>Ipv4 相比 Ipv6 的逻辑少了网卡连通性的校验&lt;/li>
&lt;/ul>
&lt;p>大家都知道，Ipv4 将 127.0.0.1 定为本地回环地址， Ipv6 也存在回环地址：0:0:0:0:0:0:0:1 或者表示为 ::1。改进建议也很明显，我们放到文末统一总结。&lt;/p>
&lt;h3 id="23-轮询网卡">2.3 轮询网卡&lt;/h3>
&lt;p>如果上述地址获取为 null 则进入轮询网卡的逻辑（例如 hosts 未指定 hostname 的映射或者 hostname 配置成了 127.0.0.1 之类的地址便会导致获取到空的网卡地址），轮询网卡对应的源码是 &lt;code>NetworkInterface.getNetworkInterfaces()&lt;/code> ，这里面涉及的知识点就比较多了，支撑起了我写这篇文章的素材，Dubbo 的逻辑并不复杂，进行简单的校验，返回第一个可用的 IP 即可。&lt;/p>
&lt;p>性子急的读者可能忍不住了，多网卡！合适的网卡可能不止一个，Dubbo 怎么应对呢？按道理说，我们也替 Dubbo 说句公道话，客官要不你自己指定下？我们首先得对多网卡的场景达成一致看法，才能继续把这篇文章完成下去：我们只能&lt;strong>尽可能&lt;/strong>过滤那些“&lt;strong>不对&lt;/strong>”的网卡。Dubbo 看样子对所有网卡是一视同仁了，那么是不是可以尝试优化一下其中的逻辑呢？&lt;/p>
&lt;p>许多开源的服务治理框架在 stackoverflow 或者其 issue 中，注册错 IP 相关的问题都十分高频，大多数都是轮询网卡出了问题。既然事情发展到这儿，势必需要了解一些网络、网卡的知识，我们才能过滤掉那些明显不适合 RPC 服务注册的 IP 地址了。&lt;/p>
&lt;h2 id="3-ifconfig-介绍">3 Ifconfig 介绍&lt;/h2>
&lt;p>我并没有想要让大家对后续的内容望而却步，特地选择了这个大家最熟悉的 Linux 命令！对于那些吐槽：“天呐，都 2019 年了，你怎么还在用 net-tools/ifconfig，iproute2/ip 了解一下”的言论，请大家视而不见。无论你使用的是 mac，还是 linux，都可以使用它去 CRUD 你的网卡配置。&lt;/p>
&lt;h3 id="31-常用指令">3.1 常用指令&lt;/h3>
&lt;p>&lt;strong>启动关闭指定网卡：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>ifconfig eth0 up
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ifconfig eth0 down
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>ifconfig eth0 up&lt;/code> 为启动网卡 eth0，&lt;code>ifconfig eth0 down&lt;/code> 为关闭网卡 eth0。ssh 登陆 linux 服务器操作的用户要小心执行这个操作了，千万不要蠢哭自己。不然你下一步就需要去 google：“禁用 eth0 网卡后如何远程连接 Linux 服务器” 了。&lt;/p>
&lt;p>&lt;strong>为网卡配置和删除IPv6地址：&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>ifconfig eth0 add 33ffe:3240:800:1005::2/64 #为网卡eth0配置IPv6地址
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ifconfig eth0 del 33ffe:3240:800:1005::2/64 #为网卡eth0删除IPv6地址
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>用 ifconfig 修改 MAC 地址：&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>ifconfig eth0 hw ether 00:AA:BB:CC:dd:EE
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>配置 IP 地址：&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>[root@localhost ~]# ifconfig eth0 192.168.2.10
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[root@localhost ~]# ifconfig eth0 192.168.2.10 netmask 255.255.255.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[root@localhost ~]# ifconfig eth0 192.168.2.10 netmask 255.255.255.0 broadcast 192.168.2.255
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>启用和关闭arp协议：&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>ifconfig eth0 arp #开启网卡eth0 的arp协议
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ifconfig eth0 -arp #关闭网卡eth0 的arp协议
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>设置最大传输单元：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>ifconfig eth0 mtu 1500 #设置能通过的最大数据包大小为 1500 bytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="32-查看网卡信息">3.2 查看网卡信息&lt;/h3>
&lt;p>在一台 ubuntu 上执行 &lt;code>ifconfig -a&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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>ubuntu@VM-30-130-ubuntu:~$ ifconfig -a
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>eth0 Link encap:Ethernet HWaddr 52:54:00:a9:5f:ae
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet addr:10.154.30.130 Bcast:10.154.63.255 Mask:255.255.192.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX packets:149673 errors:0 dropped:0 overruns:0 frame:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TX packets:152271 errors:0 dropped:0 overruns:0 carrier:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collisions:0 txqueuelen:1000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX bytes:15205083 &lt;span style="color:#719e07">(&lt;/span>15.2 MB&lt;span style="color:#719e07">)&lt;/span> TX bytes:21386362 &lt;span style="color:#719e07">(&lt;/span>21.3 MB&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>lo Link encap:Local Loopback
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet addr:127.0.0.1 Mask:255.0.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UP LOOPBACK RUNNING MTU:65536 Metric:1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collisions:0 txqueuelen:1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span> TX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker0 Link encap:Ethernet HWaddr 02:42:58:45:c1:15
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UP BROADCAST MULTICAST MTU:1500 Metric:1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collisions:0 txqueuelen:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span> TX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UP POINTOPOINT NOARP MULTICAST MTU:1500 Metric:1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collisions:0 txqueuelen:100
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span> TX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>为了防止黑客对我的 Linux 发起攻击，我还是偷偷对 IP 做了一点“改造”，请不要为难一个趁着打折+组团购买廉价云服务器的小伙子。对于部分网卡的详细解读:&lt;/p>
&lt;p>eth0 表示第一块网卡， 其中 HWaddr 表示网卡的物理地址，可以看到目前这个网卡的物理地址(MAC 地址）是 02:42:38:52:70:54&lt;/p>
&lt;p>inet addr 用来表示网卡的 IP 地址，此网卡的 IP 地址是 10.154.30.130，广播地址， Bcast: 172.18.255.255，掩码地址 Mask:255.255.0.0&lt;/p>
&lt;p>lo 是表示主机的回环地址，这个一般是用来测试一个网络程序，但又不想让局域网或外网的用户能够查看，只能在此台主机上运行和查看所用的网络接口。比如把 HTTPD 服务器的指定到回坏地址，在浏览器输入 127.0.0.1 就能看到你所架构的 WEB 网站了。但只有你能看得到，局域网的其它主机或用户则无从知晓。&lt;/p>
&lt;p>第一行：连接类型：Ethernet（以太网）HWaddr（硬件mac地址）&lt;/p>
&lt;p>第二行：网卡的IP地址、子网、掩码&lt;/p>
&lt;p>第三行：UP（代表网卡开启状态）RUNNING（代表网卡的网线被接上）MULTICAST（支持组播）MTU:1500（最大传输单元）：1500字节（ifconfig 不加 -a 则无法看到 DOWN 的网卡）&lt;/p>
&lt;p>第四、五行：接收、发送数据包情况统计&lt;/p>
&lt;p>第七行：接收、发送数据字节数统计信息。&lt;/p>
&lt;p>紧接着的两个网卡 docker0，tun0 是怎么出来的呢？我在我的 ubuntu 上装了 docker 和 openvpn。这两个东西应该是日常干扰我们做服务注册时的罪魁祸首了，当然，也有可能存在 eth1 这样的第二块网卡。ifconfig -a 看到的东西就对应了 JDK 的 api ：&lt;code>NetworkInterface.getNetworkInterfaces()&lt;/code> 。我们简单做个总结，大致有三个干扰因素&lt;/p>
&lt;ul>
&lt;li>以 docker 网桥为首的虚拟网卡地址，毕竟这东西这么火，怎么也得单独列出来吧？&lt;/li>
&lt;li>以 TUN/TAP 为代表的虚拟网卡地址，多为 VPN 场景&lt;/li>
&lt;li>以 eth1 为代表的多网卡场景，有钱就可以装多网卡了！&lt;/li>
&lt;/ul>
&lt;p>我们后续的篇幅将针对这些场景做分别的介绍，力求让大家没吃过猪肉，起码看下猪怎么跑的。&lt;/p>
&lt;h2 id="4-干扰因素一docker-网桥">4 干扰因素一：Docker 网桥&lt;/h2>
&lt;p>熟悉 docker 的朋友应该知道 docker 会默认创建一个 docker0 的网桥，供容器实例连接。如果嫌默认的网桥不够直观，我们可以使用 bridge 模式自定义创建一个新的网桥：&lt;/p>
&lt;div class="highlight">&lt;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>ubuntu@VM-30-130-ubuntu:~$ docker network create kirito-bridge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>a38696dbbe58aa916894c674052c4aa6ab32266dcf6d8111fb794b8a344aa0d9
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ubuntu@VM-30-130-ubuntu:~$ ifconfig -a
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>br-a38696dbbe58 Link encap:Ethernet HWaddr 02:42:6e:aa:fd:0c
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet addr:172.19.0.1 Bcast:172.19.255.255 Mask:255.255.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UP BROADCAST MULTICAST MTU:1500 Metric:1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collisions:0 txqueuelen:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span> TX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>使用 docker network 指令创建网桥之后，自动创建了对应的网卡，我只给出了 &lt;code>ifconfig -a&lt;/code> 的增量返回部分，可以看出多了一个 br-a38696dbbe58 的网卡。&lt;/p>
&lt;p>我有意区分了“网桥”和“网卡”，可以使用 bridge-utils/brctl 来查看网桥信息：&lt;/p>
&lt;div class="highlight">&lt;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>ubuntu@VM-30-130-ubuntu:~$ sudo brctl show
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>bridge name bridge id STP enabled interfaces
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>br-a38696dbbe58 8000.02426eaafd0c no
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker0 8000.02425845c215 no
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>网桥是一个虚拟设备，这个设备只有 brctl show 能看到，网桥创建之后，会自动创建一个同名的网卡，并将这个网卡加入网桥。&lt;/p>
&lt;h2 id="5-干扰因素二tuntap-虚拟网络设备">5 干扰因素二：TUN/TAP 虚拟网络设备&lt;/h2>
&lt;p>平时我们所说的虚拟网卡、虚拟机，大致都跟 TUN/TAP 有关。我的读者大多数是 Java 从业者，相信我下面的内容并没有太超纲，不要被陌生的名词唬住。对于被唬住的读者，也可以直接跳过 5.1~5.3，直接看 5.4 的实战。&lt;/p>
&lt;h3 id="51-真实网卡工作原理">5.1 真实网卡工作原理&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/network/01.png" alt="1918847-496d0e96c237f25a">&lt;/p>
&lt;p>上图中的 &lt;strong>eth0&lt;/strong> 表示我们主机已有的真实的网卡接口 (&lt;strong>interface&lt;/strong>)。&lt;/p>
&lt;p>网卡接口 &lt;strong>eth0&lt;/strong> 所代表的真实网卡通过网线(&lt;strong>wire&lt;/strong>)和外部网络相连，该物理网卡收到的数据包会经由接口 &lt;strong>eth0&lt;/strong> 传递给内核的网络协议栈(&lt;strong>Network Stack&lt;/strong>)。然后协议栈对这些数据包进行进一步的处理。&lt;/p>
&lt;p>对于一些错误的数据包,协议栈可以选择丢弃；对于不属于本机的数据包，协议栈可以选择转发；而对于确实是传递给本机的数据包,而且该数据包确实被上层的应用所需要，协议栈会通过 &lt;strong>Socket API&lt;/strong> 告知上层正在等待的应用程序。&lt;/p>
&lt;h3 id="52-tun-工作原理">5.2 TUN 工作原理&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/network/02.png" alt="1918847-85ea08bc89d9427e">&lt;/p>
&lt;p>我们知道，普通的网卡是通过网线来收发数据包的话，而 &lt;strong>TUN&lt;/strong> 设备比较特殊，它通过一个文件收发数据包。&lt;/p>
&lt;p>如上图所示，&lt;strong>tunX&lt;/strong> 和上面的 &lt;strong>eth0&lt;/strong> 在逻辑上面是等价的， &lt;strong>tunX&lt;/strong> 也代表了一个网络接口,虽然这个接口是系统通过软件所模拟出来的.&lt;/p>
&lt;p>网卡接口 &lt;strong>tunX 所代表的虚拟网卡通过文件 /dev/tunX 与我们的应用程序(App)相连&lt;/strong>，应用程序每次使用 &lt;strong>write&lt;/strong> 之类的系统调用将数据写入该文件，这些数据会以网络层数据包的形式，通过该虚拟网卡，经由网络接口 &lt;strong>tunX&lt;/strong> 传递给网络协议栈，同时该应用程序也可以通过 &lt;strong>read&lt;/strong> 之类的系统调用，经由文件 &lt;strong>/dev/tunX&lt;/strong> 读取到协议栈向 &lt;strong>tunX&lt;/strong> 传递的&lt;strong>所有&lt;/strong>数据包。&lt;/p>
&lt;p>此外，协议栈可以像操纵普通网卡一样来操纵 &lt;strong>tunX&lt;/strong> 所代表的虚拟网卡。比如说，给 &lt;strong>tunX&lt;/strong> 设定 &lt;strong>IP&lt;/strong> 地址，设置路由，总之，在协议栈看来，&lt;strong>tunX&lt;/strong> 所代表的网卡和其他普通的网卡区别不大，当然，硬要说区别，那还是有的,那就是 &lt;strong>tunX&lt;/strong> 设备不存在 &lt;strong>MAC&lt;/strong> 地址，这个很好理解，&lt;strong>tunX&lt;/strong> 只模拟到了网络层，要 &lt;strong>MAC&lt;/strong>地址没有任何意义。当然，如果是 &lt;strong>tapX&lt;/strong> 的话，在协议栈的眼中，&lt;strong>tapX&lt;/strong> 和真实网卡没有任何区别。&lt;/p>
&lt;p>是不是有些懵了？我是谁，为什么我要在这篇文章里面学习 TUN！因为我们常用的 VPN 基本就是基于 TUN/TAP 搭建的，如果我们使用 &lt;strong>TUN&lt;/strong> 设备搭建一个基于 &lt;strong>UDP&lt;/strong> 的 &lt;strong>VPN&lt;/strong> ，那么整个处理过程可能是这幅样子：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/network/03.png" alt="1918847-ac4155ec7e9489b2">&lt;/p>
&lt;h3 id="53-tap-工作原理">5.3 TAP 工作原理&lt;/h3>
&lt;p>&lt;strong>TAP&lt;/strong> 设备与 &lt;strong>TUN&lt;/strong> 设备工作方式完全相同，区别在于：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>TUN&lt;/strong> 设备是一个三层设备，它只模拟到了 &lt;strong>IP&lt;/strong> 层，即网络层 我们可以通过 &lt;strong>/dev/tunX&lt;/strong> 文件收发 &lt;strong>IP&lt;/strong> 层数据包，它无法与物理网卡做 &lt;strong>bridge&lt;/strong>，但是可以通过三层交换（如 &lt;strong>ip_forward&lt;/strong>）与物理网卡连通。可以使用&lt;code>ifconfig&lt;/code>之类的命令给该设备设定 &lt;strong>IP&lt;/strong> 地址。&lt;/li>
&lt;li>&lt;strong>TAP&lt;/strong> 设备是一个二层设备，它比 &lt;strong>TUN&lt;/strong> 更加深入，通过 &lt;strong>/dev/tapX&lt;/strong> 文件可以收发 &lt;strong>MAC&lt;/strong> 层数据包，即数据链路层，拥有 &lt;strong>MAC&lt;/strong> 层功能，可以与物理网卡做 &lt;strong>bridge&lt;/strong>，支持 &lt;strong>MAC&lt;/strong> 层广播。同样的，我们也可以通过&lt;code>ifconfig&lt;/code>之类的命令给该设备设定 &lt;strong>IP&lt;/strong> 地址，你如果愿意，我们可以给它设定 &lt;strong>MAC&lt;/strong> 地址。&lt;/li>
&lt;/ol>
&lt;p>关于文章中出现的二层，三层，我这里说明一下，第一层是物理层，第二层是数据链路层，第三层是网络层，第四层是传输层。&lt;/p>
&lt;h3 id="54-openvpn-实战">5.4 openvpn 实战&lt;/h3>
&lt;p>openvpn 是 Linux 上一款开源的 vpn 工具，我们通过它来复现出影响我们做网卡选择的场景。&lt;/p>
&lt;p>安装 openvpn&lt;/p>
&lt;div class="highlight">&lt;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>sudo apt-get install openvpn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>安装一个 TUN 设备：&lt;/p>
&lt;div class="highlight">&lt;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>ubuntu@VM-30-130-ubuntu:~$ sudo openvpn --mktun --dev tun0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Mon Apr &lt;span style="color:#2aa198">29&lt;/span> 22:23:31 &lt;span style="color:#2aa198">2019&lt;/span> TUN/TAP device tun0 opened
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Mon Apr &lt;span style="color:#2aa198">29&lt;/span> 22:23:31 &lt;span style="color:#2aa198">2019&lt;/span> Persist state &lt;span style="color:#b58900">set&lt;/span> to: ON
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>安装一个 TAP 设备：&lt;/p>
&lt;div class="highlight">&lt;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>ubuntu@VM-30-130-ubuntu:~$ sudo openvpn --mktun --dev tap0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Mon Apr &lt;span style="color:#2aa198">29&lt;/span> 22:24:36 &lt;span style="color:#2aa198">2019&lt;/span> TUN/TAP device tap0 opened
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Mon Apr &lt;span style="color:#2aa198">29&lt;/span> 22:24:36 &lt;span style="color:#2aa198">2019&lt;/span> Persist state &lt;span style="color:#b58900">set&lt;/span> to: ON
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>执行 &lt;code>ifconfig -a&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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>tap0 Link encap:Ethernet HWaddr 7a:a2:a8:f1:6b:df
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> BROADCAST MULTICAST MTU:1500 Metric:1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collisions:0 txqueuelen:100
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span> TX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet addr:10.154.30.131 P-t-P:10.154.30.131 Mask:255.255.255.255
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UP POINTOPOINT NOARP MULTICAST MTU:1500 Metric:1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX packets:0 errors:0 dropped:0 overruns:0 frame:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collisions:0 txqueuelen:100
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span> TX bytes:0 &lt;span style="color:#719e07">(&lt;/span>0.0 B&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样就解释了文章一开始为什么会有 tun0 这样的网卡了。这里读者可能会有疑惑，使用 ifconfig 不是也可以创建 tap 和 tun 网卡吗？当然啦，openvpn 是一个 vpn 工具，只能创建名为 tunX/tapX 的网卡，其遵守着一定的规范，ifconfig 可以随意创建，但没人认那些随意创建的网卡。&lt;/p>
&lt;h2 id="6-干扰因素三多网卡">6 干扰因素三：多网卡&lt;/h2>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/network/04.png" alt="image-20190429223515625">&lt;/p>
&lt;p>这个没有太多好说的，有多张真实的网卡，从普哥那儿搞到如上的 IP 信息。&lt;/p>
&lt;h2 id="7-mac-下的差异">7 MAC 下的差异&lt;/h2>
&lt;p>虽然 ifconfig 等指令是 &lt;code>*nux&lt;/code> 通用的，但是其展示信息，网卡相关的属性和命名都有较大的差异。例如这是我 MAC 下执行 &lt;code>ifconfig -a&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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>xujingfengdeMacBook-Pro:dubbo-in-action xujingfeng$ ifconfig -a
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>lo0: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8049&amp;lt;UP,LOOPBACK,RUNNING,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">16384&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1203&amp;lt;RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet 127.0.0.1 netmask 0xff000000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet6 ::1 prefixlen &lt;span style="color:#2aa198">128&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet6 fe80::1%lo0 prefixlen &lt;span style="color:#2aa198">64&lt;/span> scopeid 0x1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> nd6 &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>201&amp;lt;PERFORMNUD,DAD&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>gif0: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8010&amp;lt;POINTOPOINT,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">1280&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>stf0: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0&amp;lt;&amp;gt; mtu &lt;span style="color:#2aa198">1280&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>XHC0: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0&amp;lt;&amp;gt; mtu &lt;span style="color:#2aa198">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>XHC20: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0&amp;lt;&amp;gt; mtu &lt;span style="color:#2aa198">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>en0: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8863&amp;lt;UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">1500&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ether 88:e9:fe:88:a0:76
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet6 fe80::1cab:f689:60d1:bacb%en0 prefixlen &lt;span style="color:#2aa198">64&lt;/span> secured scopeid 0x6
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet 30.130.11.242 netmask 0xffffff80 broadcast 30.130.11.255
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> nd6 &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>201&amp;lt;PERFORMNUD,DAD&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> media: autoselect
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> status: active
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>p2p0: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8843&amp;lt;UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">2304&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ether 0a:e9:fe:88:a0:76
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> media: autoselect
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> status: inactive
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>awdl0: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8943&amp;lt;UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">1484&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ether 66:d2:8c:8c:dd:85
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet6 fe80::64d2:8cff:fe8c:dd85%awdl0 prefixlen &lt;span style="color:#2aa198">64&lt;/span> scopeid 0x8
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> nd6 &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>201&amp;lt;PERFORMNUD,DAD&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> media: autoselect
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> status: active
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>en1: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8963&amp;lt;UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">1500&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>60&amp;lt;TSO4,TSO6&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ether aa:00:d0:13:0e:01
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> media: autoselect &amp;lt;full-duplex&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> status: inactive
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>en2: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8963&amp;lt;UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">1500&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>60&amp;lt;TSO4,TSO6&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ether aa:00:d0:13:0e:00
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> media: autoselect &amp;lt;full-duplex&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> status: inactive
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>bridge0: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8863&amp;lt;UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">1500&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>63&amp;lt;RXCSUM,TXCSUM,TSO4,TSO6&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ether aa:00:d0:13:0e:01
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Configuration:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> id 0:0:0:0:0:0 priority &lt;span style="color:#2aa198">0&lt;/span> hellotime &lt;span style="color:#2aa198">0&lt;/span> fwddelay &lt;span style="color:#2aa198">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> maxage &lt;span style="color:#2aa198">0&lt;/span> holdcnt &lt;span style="color:#2aa198">0&lt;/span> proto stp maxaddr &lt;span style="color:#2aa198">100&lt;/span> timeout &lt;span style="color:#2aa198">1200&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> root id 0:0:0:0:0:0 priority &lt;span style="color:#2aa198">0&lt;/span> ifcost &lt;span style="color:#2aa198">0&lt;/span> port &lt;span style="color:#2aa198">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ipfilter disabled flags 0x2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> member: en1 &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>3&amp;lt;LEARNING,DISCOVER&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ifmaxaddr &lt;span style="color:#2aa198">0&lt;/span> port &lt;span style="color:#2aa198">9&lt;/span> priority &lt;span style="color:#2aa198">0&lt;/span> path cost &lt;span style="color:#2aa198">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> member: en2 &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>3&amp;lt;LEARNING,DISCOVER&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ifmaxaddr &lt;span style="color:#2aa198">0&lt;/span> port &lt;span style="color:#2aa198">10&lt;/span> priority &lt;span style="color:#2aa198">0&lt;/span> path cost &lt;span style="color:#2aa198">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> nd6 &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>201&amp;lt;PERFORMNUD,DAD&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> media: &amp;lt;unknown type&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> status: inactive
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>utun0: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8051&amp;lt;UP,POINTOPOINT,RUNNING,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">2000&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet6 fe80::3fe0:3e8b:384:9968%utun0 prefixlen &lt;span style="color:#2aa198">64&lt;/span> scopeid 0xc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> nd6 &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>201&amp;lt;PERFORMNUD,DAD&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>utun1: &lt;span style="color:#268bd2">flags&lt;/span>&lt;span style="color:#719e07">=&lt;/span>8051&amp;lt;UP,POINTOPOINT,RUNNING,MULTICAST&amp;gt; mtu &lt;span style="color:#2aa198">1380&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inet6 fe80::7894:3abc:5abd:457d%utun1 prefixlen &lt;span style="color:#2aa198">64&lt;/span> scopeid 0xd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> nd6 &lt;span style="color:#268bd2">options&lt;/span>&lt;span style="color:#719e07">=&lt;/span>201&amp;lt;PERFORMNUD,DAD&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>内容很多，我挑几点差异简述下：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>内容展示形式不一样，没有 Linux 下的接收、发送数据字节数等统计信息&lt;/p>
&lt;/li>
&lt;li>
&lt;p>真实网卡的命名不一样：eth0 -&amp;gt; en0&lt;/p>
&lt;/li>
&lt;li>
&lt;p>虚拟网卡的命名格式不一样：tun/tap -&amp;gt; utun&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>对于这些常见网卡命名的解读，我摘抄一部分来自 stackoverflow 的回答：&lt;/p>
&lt;blockquote>
&lt;p>In arbitrary order of my familarity / widespread relevance:&lt;/p>
&lt;p>&lt;code>lo0&lt;/code> is loopback.&lt;/p>
&lt;p>&lt;code>en0&lt;/code> at one point &amp;ldquo;ethernet&amp;rdquo;, now is WiFi (and I have no idea what extra &lt;code>en1&lt;/code> or &lt;code>en2&lt;/code> are used for).&lt;/p>
&lt;p>&lt;code>fw0&lt;/code> is the FireWire network interface.&lt;/p>
&lt;p>&lt;code>stf0&lt;/code> is an &lt;a href="https://www.freebsd.org/cgi/man.cgi?gif(4)">IPv6 to IPv4 tunnel interface&lt;/a> to support &lt;a href="http://en.wikipedia.org/wiki/6to4">the transition&lt;/a> from IPv4 to the IPv6 standard.&lt;/p>
&lt;p>&lt;code>gif0&lt;/code> is a more &lt;a href="https://www.freebsd.org/cgi/man.cgi?gif(4)">generic tunneling interface&lt;/a> [46]-to-[46].&lt;/p>
&lt;p>&lt;code>awdl0&lt;/code> is &lt;a href="https://stackoverflow.com/questions/19587701/what-is-awdl-apple-wireless-direct-link-and-how-does-it-work">Apple Wireless Direct Link&lt;/a>&lt;/p>
&lt;p>&lt;code>p2p0&lt;/code> is related to AWDL features. Either as an old version, or virtual interface with different semantics than &lt;code>awdl&lt;/code>.&lt;/p>
&lt;p>the &amp;ldquo;Network&amp;rdquo; panel in System Preferences to see what network devices &amp;ldquo;exist&amp;rdquo; or &amp;ldquo;can exist&amp;rdquo; with current configuration.&lt;/p>
&lt;p>many VPNs will add additional devices, often &amp;ldquo;utun#&amp;rdquo; or &amp;ldquo;utap#&amp;rdquo; following &lt;a href="https://en.wikipedia.org/wiki/TUN/TAP">TUN/TAP (L3/L2)&lt;/a>virtual networking devices.&lt;/p>
&lt;p>use &lt;code>netstat -nr&lt;/code> to see how traffic is currently routed via network devices according to destination.&lt;/p>
&lt;p>interface naming conventions started in BSD were retained in OS X / macOS, and now there also additions.&lt;/p>
&lt;/blockquote>
&lt;h2 id="8-dubbo-改进建议">8 Dubbo 改进建议&lt;/h2>
&lt;p>我们进行了以上探索，算是对网卡有一点了解了。回过头来看看 Dubbo 获取网卡的逻辑，是否可以做出改进呢？&lt;/p>
&lt;p>&lt;strong>Dubbo Action 1:&lt;/strong>&lt;/p>
&lt;p>保持 Ipv4 和 Ipv6 的一致性校验。为 Ipv4 增加连通性校验；为 Ipv6 增加 LoopBack 和 ANYHOST 等校验。&lt;/p>
&lt;p>&lt;strong>Dubbo Action 2:&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>NetworkInterface network &lt;span style="color:#719e07">=&lt;/span> interfaces&lt;span style="color:#719e07">.&lt;/span>nextElement&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>network&lt;span style="color:#719e07">.&lt;/span>isLoopback&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">||&lt;/span> network&lt;span style="color:#719e07">.&lt;/span>isVirtual&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">||&lt;/span> &lt;span style="color:#719e07">!&lt;/span>network&lt;span style="color:#719e07">.&lt;/span>isUp&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">continue&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>JDK 提供了以上的 API，我们可以利用起来，过滤一部分一定不正确的网卡。&lt;/p>
&lt;p>&lt;strong>Dubbo Action 3:&lt;/strong>&lt;/p>
&lt;p>我们本文花了较多的篇幅介绍了 docker 和 TUN/TAP 两种场景导致的虚拟网卡的问题，算是较为常见的一个影响因素，虽然他们的命名具有固定性，如 docker0、tunX、tapX，但我觉得通过网卡名称的判断方式去过滤注册 IP 有一些 hack，所以不建议 dubbo contributor 提出相应的 pr 去增加这些 hack 判断，尽管可能会对判断有所帮助。&lt;/p>
&lt;p>对于真实多网卡、内外网 IP 共存的场景，不能仅仅是框架侧在做努力，用户也需要做一些事，就像爱情一样，我可以主动一点，但你也得反馈，才能发展出故事。&lt;/p>
&lt;p>&lt;strong>Dubbo User Action 1:&lt;/strong>&lt;/p>
&lt;p>可以配置 &lt;code>/etc/hosts&lt;/code> 文件，将 hostname 对应的 IP 显式配置进去。&lt;/p>
&lt;p>&lt;strong>Dubbo User Action 2:&lt;/strong>&lt;/p>
&lt;p>可以使用启动参数去显式指定注册的 IP：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">-&lt;/span>DDUBBO_IP_TO_REGISTRY&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1.2.3.4&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&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:#719e07">-&lt;/span>DDUBBO_IP_TO_BIND&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1.2.3.4&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="9-参考文章">9 参考文章&lt;/h2>
&lt;p>&lt;a href="https://www.jianshu.com/p/09f9375b7fa7">TUN/TAP 设备浅析&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://stackoverflow.com/questions/29958143/what-are-en0-en1-p2p-and-so-on-that-are-displayed-after-executing-ifconfig">what-are-en0-en1-p2p-and-so-on-that-are-displayed-after-executing-ifconfig&lt;/a>&lt;/p></description></item><item><title>Blog: Dubbo Admin服务测试功能</title><link>https://cn.dubbo.apache.org/zh/blog/2019/08/26/service-test/</link><pubDate>Mon, 26 Aug 2019 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/admin/testSearch.jpg" alt="testSearch">&lt;br>
这里的信息和元数据类似，包含方法名，参数类型和返回值信息，点击右边的标签就可以进入服务测试页面&lt;/li>
&lt;li>服务测试：
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/admin/testSuccess.jpg" alt="testSuccess">&lt;br>
服务测试页面包含了两个json编辑器，参数类型的信息都是以json格式保存，这里需要填入对应的参数值(本例中数类型时&lt;code>String&lt;/code>)，填写完成后点击&lt;code>执行&lt;/code>即可对服务端发起调用，调用结果展示在右边的编辑器中，如果调用失败，会显示详细的失败原因，下面来看一下调用失败的例子：&lt;br>
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/admin/testFail.jpg" alt="testFail">
本例中，先关掉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>&lt;span style="color:#586e75">&lt;/span>Result &lt;span style="color:#268bd2">getUser&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">,&lt;/span> UserInfoDO userInfoDO&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">UserInfoDO&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&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:#268bd2">private&lt;/span> LocationDO locationDO&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DepartmentDO departmentDO&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;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&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">()&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;, departmentDO=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> departmentDO&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">()&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;#39;}&amp;#39;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DepartmentDO&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String departName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> LocationDO departLocation&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;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&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">()&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;#39;}&amp;#39;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">LocationDO&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String address&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> postNum&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;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 style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>参数是比较复杂的符合类型参数，服务测试的时候，会逐层展开填写每一个field的值，如下图所示：&lt;br>
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/admin/complex.jpg" alt="complex">
同样可以调用成功并且返回结果&lt;/p>
&lt;h2 id="原理数据来源">原理：数据来源&lt;/h2>
&lt;p>服务测试中，最重要的就是完整的方法签名信息，和参数的类型信息，有了这些信息才能够一步步填入每个参数的值，拼装出完整的服务消费者。在Dubbo2.7中，新增了元数据中心，Dubbo Admin的方法签名和参数类型信息就是从这里来的：&lt;br>
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/admin/metadata.png" alt="medatada">
如图所示，服务端在运行的时候会将服务的元数据信息注册到元数据中心，格式如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;methods&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;parameterTypes&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.model.User&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;returnType&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.model.Result&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;types&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;char&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;long&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.model.Result&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;msg&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;value&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;char[]&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;hash&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;int&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;userName&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.String&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;value&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;char[]&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;hash&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;int&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.model.User&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;id&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.Long&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;value&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;long&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;username&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;java.lang.Sring&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;properties&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;value&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;char[]&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;hash&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;int&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>与服务测试相关的就是&lt;code>methods&lt;/code>和&lt;code>types&lt;/code>所包含的方法和类型信息，Dubbo Admin根据这些信息，将参数渲染到服务测试页面的Json Editor中，由用户来输入每个参数，每个成员变量的值。&lt;/p>
&lt;h2 id="原理-泛化调用">原理： 泛化调用&lt;/h2>
&lt;p>有了参数类型，下一个问题就是怎么能够调用到服务端，在传统的Dubbo RPC调用中，客户端需要依赖服务端的API jar包(参考前文demo中的&lt;a href="https://github.com/nzomkxia/dubbo-demo/tree/master/dubbo-basic-consumer">dubbo-basic-consumer&lt;/a>)，这对于Dubbo Admin来说不太可能，因为服务的上下线是动态的，Dubbo Admin无法动态增加jar包依赖，因此需要用到Dubbo中的&lt;strong>泛化调用&lt;/strong>，指的是在没有服务端API接口的情况下，客户端直接通过 &lt;code>GenericService&lt;/code> 接口来发起服务调用，返回值中的数据对象都用Map来表示。泛化调用在服务端不需要做特殊处理，只需要客户端发起即可。&lt;/p>
&lt;h2 id="总结和展望">总结和展望&lt;/h2>
&lt;p>本文简单介绍了服务测试的用法和原理，后续会进一步针对该功能进行增强，比如处理抽象类的参数类型，支持从json文件导入参数值，支持对参数值的保存等等，方便对服务接口进行回归测试。&lt;/p></description></item><item><title>Blog: 本地调用</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/dubbo-local-call-filter.png" alt="filter-chain">&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>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 服务消费者指定 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: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> 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;/code>&lt;/pre>&lt;/div>&lt;p>如果同时服务提供方限定了 &lt;em>scope=&amp;ldquo;local&amp;rdquo;&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:#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:#586e75">&amp;lt;!-- 服务消费者指定 scope=&amp;#34;local&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: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> scope=&lt;span style="color:#2aa198">&amp;#34;local&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>那么该程序中的 dubbo 调用将会失败，原因是服务提供方只暴露了远程服务到注册中心上，并没有暴露 &lt;em>injvm&lt;/em> 协议的服务，而出于同一个进程中的服务消费者查找不到 &lt;em>injvm&lt;/em> 协议的服务，也不会去远程的注册中心上订阅服务地址。同样的，当服务提供者限定 &lt;em>scope=&amp;ldquo;local&amp;rdquo;&lt;/em> 而服务消费者限定 &lt;em>scope=&amp;ldquo;remote&amp;rdquo;&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-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>20/03/19 05:03:18:018 CST&lt;span style="color:#719e07">]&lt;/span> main INFO config.AbstractConfig: &lt;span style="color:#719e07">[&lt;/span>DUBBO&lt;span style="color:#719e07">]&lt;/span> Using injvm service org.apache.dubbo.samples.local.api.DemoService, dubbo version: 2.7.1, current host: 169.254.146.168
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Exception in thread &lt;span style="color:#2aa198">&amp;#34;main&amp;#34;&lt;/span> org.springframework.beans.factory.BeanCreationException: Error creating bean with name &lt;span style="color:#2aa198">&amp;#39;demoService&amp;#39;&lt;/span>: FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Failed to check the status of the service org.apache.dubbo.samples.local.api.DemoService. No provider available &lt;span style="color:#719e07">for&lt;/span> the service org.apache.dubbo.samples.local.api.DemoService from the url injvm://127.0.0.1/org.apache.dubbo.samples.local.api.DemoService?application&lt;span style="color:#719e07">=&lt;/span>demo-provider&amp;amp;default.lazy&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;default.sticky&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;&lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.0.2&amp;amp;&lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.samples.local.api.DemoService&amp;amp;&lt;span style="color:#268bd2">lazy&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;&lt;span style="color:#268bd2">methods&lt;/span>&lt;span style="color:#719e07">=&lt;/span>sayHello&amp;amp;&lt;span style="color:#268bd2">pid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>76198&amp;amp;register.ip&lt;span style="color:#719e07">=&lt;/span>169.254.146.168&amp;amp;&lt;span style="color:#268bd2">release&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.7.1-SNAPSHOT&amp;amp;&lt;span style="color:#268bd2">scope&lt;/span>&lt;span style="color:#719e07">=&lt;/span>local&amp;amp;&lt;span style="color:#268bd2">side&lt;/span>&lt;span style="color:#719e07">=&lt;/span>consumer&amp;amp;&lt;span style="color:#268bd2">sticky&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1553072598838&lt;/span> to the consumer 169.254.146.168 use dubbo version 2.7.1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="何时无法使用本地调用">何时无法使用本地调用&lt;/h3>
&lt;p>默认情况下，本地调用是自动开启的，不需要做额外的配置。只有当需要关闭的时候，才需要通过 &lt;em>scope&lt;/em> 的配置来显式的关闭。&lt;/p>
&lt;p>但是，特别需要指出的是，在下面的几种情况下，本地调用是无法使用的：&lt;/p>
&lt;p>第一，泛化调用的时候无法使用本地调用。&lt;/p>
&lt;p>第二，消费者明确指定 URL 发起直连调用。当然，如果消费者指定的是 &lt;em>injvm&lt;/em> 的 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-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.samples.local.api.DemoService&amp;#34;&lt;/span> url=&lt;span style="color:#2aa198">&amp;#34;injvm://127.0.0.1/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;h3 id="强制打开本地调用">强制打开本地调用&lt;/h3>
&lt;p>除了通过 &lt;em>scope&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;dubbo:consumer&lt;/span> injvm=&lt;span style="color:#2aa198">&amp;#34;false&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:provider&lt;/span> injvm=&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>但是通过 &lt;em>injvm&lt;/em> 来配置本地调用的方式已经被废弃。通过 &lt;em>scope&lt;/em> 的方式来控制是官方推荐的。&lt;/p>
&lt;h3 id="总结">总结&lt;/h3>
&lt;p>本文介绍了本地调用的概念以及带来的好处，并进一步的揭示了 dubbo 本地调用实际上是在当前进程中暴露了 &lt;em>injvm&lt;/em> 的协议，而该协议并不会对外暴露端口，然后讨论了如何通过 &lt;em>scope&lt;/em> 来细粒度的控制本地调用的行为，并强调了通过 &lt;em>invjm&lt;/em> 来配置的方式已经被废弃，在未来版本中可能会被删除。&lt;/p></description></item><item><title>Blog: 在 Dubbo 中使用 REST</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/rest-sample.png" alt="micro-image">&lt;/p>
&lt;p>&lt;em>source: /imgs/blog/2019/07/26/rest/micro-image.png&lt;/em>&lt;/p>
&lt;p>这里需要注意的是，按照 Roy 的定义，RESTful 架构风格与 HTTP 协议并没有什么强关联关系。但是，基于 HTTP 的 RESTful 架构风格是实现起来最自然，也是目前使用最广泛的一种实现，我们称之为 RESTful HTTP。同样的，在下文中将会专注在 HTTP 的场景下介绍如何在 Dubbo 框架中将服务暴露成 Restful 架构。&lt;/p>
&lt;h2 id="在-dubbo-中使用-rest">在 Dubbo 中使用 REST&lt;/h2>
&lt;h3 id="背景">背景&lt;/h3>
&lt;p>随着微服务的流行以及多语言互操作诉求日益增多，在 Dubbo 中暴露 REST 服务变成了一个不容忽视的诉求。为了在 Dubbo 中暴露 REST 服务，通常有两种做法，一种是直接依赖 Sprng REST 或者其他 REST 框架来直接暴露，另一种是通过 Dubbo 框架内置的 REST 能力暴露。两种做法各有优缺点，主要体现在前者与微服务体系中的服务发现组件能够更好的工作，而后者可以无缝的享受到 Dubbo 体系中的服务发现以及服务治理的能力。本文关注的是如何使用后者来暴露 REST 服务。&lt;/p>
&lt;p>自 &lt;code>2.6.0&lt;/code> 开始，Dubbo 合并了当当网捐献的 DubboX &lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup> 中的主要特性，其中就包括了基于 RESTeasy &lt;code>3.0.19.Final&lt;/code> 的 REST 支持，具备 JAXRS 2.0 规范中所有的能力。&lt;/p>
&lt;h3 id="基本用法">基本用法&lt;/h3>
&lt;p>在以下的例子中，展示了如何通过最传统的 Spring XML 配置的方式来快速的暴露和调用一个 REST 服务。其中底层的 server 使用的是 netty，服务注册发现基于 Zookeeper。&lt;/p>
&lt;blockquote>
&lt;p>注：本章节讨论的示例可以通过 &lt;a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/basic">https://github.com/beiwei30/dubbo-rest-samples/tree/master/basic&lt;/a> 来获得&lt;/p>
&lt;/blockquote>
&lt;h4 id="1-maven-依赖">1. Maven 依赖&lt;/h4>
&lt;p>首先需要在项目中引入 dubbo all-in-one 的依赖以及 RESTEasy 相关的必要依赖。因为在本例中使用 Zookeeper 作为服务发现，还需要引入 Zookeeper client 相关的依赖。为了方便使用，第三方的依赖可以通过框架提供的 BOM 文件 &lt;code>dubbo-dependencies-bom&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;properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo.version&amp;gt;&lt;/span>2.6.5&lt;span style="color:#268bd2">&amp;lt;/dubbo.version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/properties&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;dependencyManagement&amp;gt;&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;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-dependencies-bom&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>${dubbo.version}&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;type&amp;gt;&lt;/span>pom&lt;span style="color:#268bd2">&amp;lt;/type&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;scope&amp;gt;&lt;/span>import&lt;span style="color:#268bd2">&amp;lt;/scope&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;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dependencyManagement&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;dependencies&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>${dubbo.version}&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;!-- REST support dependencies --&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>io.netty&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>netty-all&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;/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:#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.jboss.resteasy&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>resteasy-jaxrs&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;/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:#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.jboss.resteasy&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>resteasy-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;/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:#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.jboss.resteasy&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>resteasy-netty4&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;/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:#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>javax.validation&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>validation-api&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;/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:#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.jboss.resteasy&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>resteasy-jackson-provider&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;/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:#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.jboss.resteasy&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>resteasy-jaxb-provider&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;/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:#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>javax.servlet&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>javax.servlet-api&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;/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;!-- zookeeper client 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>org.apache.curator&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>curator-framework&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;/dependency&amp;gt;&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;h4 id="2-定义服务接口">2. 定义服务接口&lt;/h4>
&lt;p>定义一个服务接口 &lt;code>UserService&lt;/code>，该接口提供两个功能，一个是获取指定 User 的详细信息，另一个是新注册一个用户。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Path&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;users&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@Consumes&lt;/span>&lt;span style="color:#719e07">({&lt;/span>MediaType&lt;span style="color:#719e07">.&lt;/span>APPLICATION_JSON&lt;span style="color:#719e07">,&lt;/span> MediaType&lt;span style="color:#719e07">.&lt;/span>TEXT_XML&lt;span style="color:#719e07">})&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@Produces&lt;/span>&lt;span style="color:#719e07">({&lt;/span>MediaType&lt;span style="color:#719e07">.&lt;/span>APPLICATION_JSON&lt;span style="color:#719e07">,&lt;/span> MediaType&lt;span style="color:#719e07">.&lt;/span>TEXT_XML&lt;span style="color:#719e07">})&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">UserService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@GET&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">@Path&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;{id: \\d+}&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> User &lt;span style="color:#268bd2">getUser&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">@PathParam&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;id&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> Long id&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&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">@POST&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">@Path&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;register&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Long &lt;span style="color:#268bd2">registerUser&lt;/span>&lt;span style="color:#719e07">(&lt;/span>User user&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过在接口上用 JaxRS 标准的 annotation 来修饰，我们规定了该服务在 REST 下的访问形式：&lt;/p>
&lt;ol>
&lt;li>&lt;code>@Path(&amp;quot;users&amp;quot;)&lt;/code> 定义了 UserService 通过 &amp;lsquo;/users&amp;rsquo; 来访问&lt;/li>
&lt;li>在类级别上定义 &lt;code>@Consumers&lt;/code> 和 &lt;code>@Produces&lt;/code> 来规定参数以及返回值的类型为 XML 和 JSON。在类级别上定义之后，就可以不用在方法级别上进一步定义了&lt;/li>
&lt;li>getUser 方法上通过 &lt;code>@GET&lt;/code> 定义了接受的 HTTP 方法为 GET，通过 &lt;code>@Path&lt;/code> 来规定参数是来自于 URL 中的 path。&amp;lsquo;GET /users/1&amp;rsquo; 等同于调用 &amp;lsquo;getUser(1)&amp;rsquo;&lt;/li>
&lt;li>registerUser 方法上通过 &lt;code>@POST&lt;/code> 定义了接受的 HTTP 方法为 POST，通过将 JSON 或 XML 格式的 User 数据 POST 到 &amp;lsquo;/users/register&amp;rsquo; 上来创建一个 User&lt;/li>
&lt;/ol>
&lt;p>在 Dubbo 中，将 REST 相关的 annotation 定义在接口或者实现上都是可以的。这个在设计上是个权衡问题。Annotation 定义在实现类上可以保证接口的纯净，否则对于不需要通过 REST 方式调用的 Dubbo 调用方来说将需要强制依赖 JaxRS 的库，但是同时，对于需要通过 REST 方式调用的 Dubbo 调用方来说，就需要自己来处理 REST 调用相关的细节了。Annotation 定义在接口上，框架会自动处理掉 REST 调用相关的细节，并和 Dubbo 的服务发现以及服务治理功能能够很好的结合起来。在本例中采用了在接口上定义 JaxRS annotation 的形式。&lt;/p>
&lt;h4 id="3-实现服务接口">3. 实现服务接口&lt;/h4>
&lt;p>为了简洁，这里给出的接口的实现只是简单的返回了接口需要的类型的示例，在真实的系统中，逻辑可能会比较复杂。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> AtomicLong id &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AtomicLong&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&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> User &lt;span style="color:#268bd2">getUser&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Long id&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> User&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;username-&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:#719e07">}&lt;/span>
&lt;/span>&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> Long &lt;span style="color:#268bd2">registerUser&lt;/span>&lt;span style="color:#719e07">(&lt;/span>User user&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> id&lt;span style="color:#719e07">.&lt;/span>incrementAndGet&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="4-装配服务">4. 装配服务&lt;/h4>
&lt;p>如上所述，本例展示的是如何通过传统的 Spring XML 的方式来装配并暴露 Dubbo 服务。需要指出的是，这里展示了如何同时暴露两种不同的协议，一种是 REST，另一种是原生的 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-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=&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> 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> xsi:schemaLocation=&lt;span style="color:#2aa198">&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd&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:application&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;rest-provider&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #1 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #2 --&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;rest&amp;#34;&lt;/span> port=&lt;span style="color:#2aa198">&amp;#34;8080&amp;#34;&lt;/span> server=&lt;span style="color:#2aa198">&amp;#34;netty&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #3 --&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> server=&lt;span style="color:#2aa198">&amp;#34;netty4&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #4 --&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.rest.api.UserService&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;rest,dubbo&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;userService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #5 --&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;userService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.rest.impl.UserServiceImpl&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;/beans&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #6 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>定义了该应用的名字为 &lt;code>rest-provider&lt;/code>&lt;/li>
&lt;li>定义了服务注册通过 Zookeeper，并且 URL 为 &amp;ldquo;zookeeper://127.0.0.1:2181&amp;rdquo;&lt;/li>
&lt;li>在端口 8080 上以 REST 方式暴露服务，底层的传输使用的是 netty&lt;/li>
&lt;li>在默认端口 20880 上以原生 Dubbo 方式暴露服务，底层的传输方式是 netty&lt;/li>
&lt;li>将 ‘userService&amp;rsquo; 的 Spring bean （也就是 UserServiceImpl）暴露为 UserService 服务，支持的协议既包括了 REST 也包括了 Dubbo&lt;/li>
&lt;li>将 UserServiceImpl 注册成 &amp;lsquo;userService&amp;rsquo; 的 Spring bean&lt;/li>
&lt;/ol>
&lt;h4 id="5-服务提供方的启动类">5. 服务提供方的启动类&lt;/h4>
&lt;p>简单的通过 ClassPathXmlApplicationContext 来加载刚刚配置的 Spring XML 配置 &amp;lsquo;rest-provider.xml&amp;rsquo; 即可启动 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">class&lt;/span> &lt;span style="color:#268bd2">RestProvider&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ClassPathXmlApplicationContext&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;spring/rest-provider.xml&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="6-启动服务端">6. 启动服务端&lt;/h4>
&lt;p>由于本例依赖 Zookeeper 做服务注册发现，在启动 RestProvider 之前，需要先启动一个 Zookeeper 服务器。之后就可以之间运行 RestProvider 了。通过以下的输出日志，我们可以知道 UserService 以两种方式对外暴露了同一个服务，其中：&lt;/p>
&lt;ul>
&lt;li>REST: rest://192.168.2.132:8080/org.apache.dubbo.samples.rest.api.UserService&lt;/li>
&lt;li>Dubbo: dubbo://192.168.2.132:20880/org.apache.dubbo.samples.rest.api.UserServic&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>01/01/19 07:18:56:056 CST&lt;span style="color:#719e07">]&lt;/span> main INFO config.AbstractConfig: &lt;span style="color:#719e07">[&lt;/span>DUBBO&lt;span style="color:#719e07">]&lt;/span> Export dubbo service org.apache.dubbo.samples.rest.api.UserService to url rest://192.168.2.132:8080/org.apache.dubbo.samples.rest.api.UserService?anyhost&lt;span style="color:#719e07">=&lt;/span>true&amp;amp;&lt;span style="color:#268bd2">application&lt;/span>&lt;span style="color:#719e07">=&lt;/span>rest-provider&amp;amp;bean.name&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.samples.rest.api.UserService&amp;amp;bind.ip&lt;span style="color:#719e07">=&lt;/span>192.168.2.132&amp;amp;bind.port&lt;span style="color:#719e07">=&lt;/span>8080&amp;amp;&lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.0.2&amp;amp;&lt;span style="color:#268bd2">generic&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;&lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.samples.rest.api.UserService&amp;amp;&lt;span style="color:#268bd2">methods&lt;/span>&lt;span style="color:#719e07">=&lt;/span>getUser,registerUser&amp;amp;&lt;span style="color:#268bd2">pid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>27386&amp;amp;&lt;span style="color:#268bd2">server&lt;/span>&lt;span style="color:#719e07">=&lt;/span>netty&amp;amp;&lt;span style="color:#268bd2">side&lt;/span>&lt;span style="color:#719e07">=&lt;/span>provider&amp;amp;&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1546341536194, dubbo version: 2.6.5, current host: 192.168.2.132
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>01/01/19 07:18:57:057 CST&lt;span style="color:#719e07">]&lt;/span> main INFO config.AbstractConfig: &lt;span style="color:#719e07">[&lt;/span>DUBBO&lt;span style="color:#719e07">]&lt;/span> Export dubbo service org.apache.dubbo.samples.rest.api.UserService to url dubbo://192.168.2.132:20880/org.apache.dubbo.samples.rest.api.UserService?anyhost&lt;span style="color:#719e07">=&lt;/span>true&amp;amp;&lt;span style="color:#268bd2">application&lt;/span>&lt;span style="color:#719e07">=&lt;/span>rest-provider&amp;amp;bean.name&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.samples.rest.api.UserService&amp;amp;bind.ip&lt;span style="color:#719e07">=&lt;/span>192.168.2.132&amp;amp;bind.port&lt;span style="color:#719e07">=&lt;/span>20880&amp;amp;&lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.0.2&amp;amp;&lt;span style="color:#268bd2">generic&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;&lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>org.apache.dubbo.samples.rest.api.UserService&amp;amp;&lt;span style="color:#268bd2">methods&lt;/span>&lt;span style="color:#719e07">=&lt;/span>getUser,registerUser&amp;amp;&lt;span style="color:#268bd2">pid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>27386&amp;amp;&lt;span style="color:#268bd2">server&lt;/span>&lt;span style="color:#719e07">=&lt;/span>netty4&amp;amp;&lt;span style="color:#268bd2">side&lt;/span>&lt;span style="color:#719e07">=&lt;/span>provider&amp;amp;&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1546341537392, dubbo version: 2.6.5, current host: 192.168.2.132
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>也可以通过 zkCli 访问 Zookeeper 服务器来验证。&amp;rsquo;/dubbo/org.apache.dubbo.samples.rest.api.UserService/providers&amp;rsquo; 路径下返回了一个数组 [dubbo://&amp;hellip;, rest:.//&amp;hellip;]。数组的第一个元素是 ’dubbo‘ 打头的，而第二个元素是 &amp;lsquo;rest&amp;rsquo; 打头的。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>zk: localhost:2181&lt;span style="color:#719e07">(&lt;/span>CONNECTED&lt;span style="color:#719e07">)&lt;/span> 10&lt;span style="color:#719e07">]&lt;/span> ls /dubbo/org.apache.dubbo.samples.rest.api.UserService/providers
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>dubbo%3A%2F%2F192.168.2.132%3A20880%2Forg.apache.dubbo.samples.rest.api.UserService%3Fanyhost%3Dtrue%26application%3Drest-provider%26bean.name%3Dorg.apache.dubbo.samples.rest.api.UserService%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.rest.api.UserService%26methods%3DgetUser%2CregisterUser%26pid%3D27386%26server%3Dnetty4%26side%3Dprovider%26timestamp%3D1546341537392, rest%3A%2F%2F192.168.2.132%3A8080%2Forg.apache.dubbo.samples.rest.api.UserService%3Fanyhost%3Dtrue%26application%3Drest-provider%26bean.name%3Dorg.apache.dubbo.samples.rest.api.UserService%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.rest.api.UserService%26methods%3DgetUser%2CregisterUser%26pid%3D27386%26server%3Dnetty%26side%3Dprovider%26timestamp%3D1546341536194&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以简单的通过 &amp;lsquo;curl&amp;rsquo; 在命令行验证刚才暴露出来的 REST 服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ curl http://localhost:8080/users/1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;id&amp;#34;&lt;/span>:1,&lt;span style="color:#2aa198">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;username-1&amp;#34;&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ curl -X POST -H &lt;span style="color:#2aa198">&amp;#34;Content-Type: application/json&amp;#34;&lt;/span> -d &lt;span style="color:#2aa198">&amp;#39;{&amp;#34;id&amp;#34;:1,&amp;#34;name&amp;#34;:&amp;#34;Larry Page&amp;#34;}&amp;#39;&lt;/span> http://localhost:8080/users/register
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="7-装配调用端">7. 装配调用端&lt;/h4>
&lt;p>Dubbo 调用方只需要依赖服务的接口，通过以下方式装配好 Dubbo 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:#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=&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> 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> xsi:schemaLocation=&lt;span style="color:#2aa198">&amp;#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd&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:application&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;rest-consumer&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;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;userService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.rest.api.UserService&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;rest&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #1 --&amp;gt;&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;ol>
&lt;li>&amp;lsquo;userService&amp;rsquo; 配置的 protocol 为 “rest&amp;quot;，将通过 REST 协议调用服务端&lt;/li>
&lt;/ol>
&lt;p>需要特别指出的是，这里显示的指定 protocol=&amp;ldquo;rest&amp;rdquo; 在通常情况下不是必须的。这里需要显示指定的原因是我们例子中服务端同时暴露了多种协议，这里指定使用 rest 是为了确保调用方走 REST 协议。&lt;/p>
&lt;h4 id="8-发起调用">8. 发起调用&lt;/h4>
&lt;p>简单的通过 ClassPathXmlApplicationContext 来加载刚刚配置的 Spring XML 配置 &amp;lsquo;rest-consumer.xml&amp;rsquo; 即可发起对 RestProvider 所提供的 UserService 的 REST 服务的调用。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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">RestConsumer&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ClassPathXmlApplicationContext&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;spring/rest-consumer.xml&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UserService userService &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;userService&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> UserService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;gt;&amp;gt;&amp;gt; &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> userService&lt;span style="color:#719e07">.&lt;/span>getUser&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1L&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> User user &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> User&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">2L&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Larry Page&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;gt;&amp;gt;&amp;gt; &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> userService&lt;span style="color:#719e07">.&lt;/span>registerUser&lt;span style="color:#719e07">(&lt;/span>user&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里分别展示了对 &amp;lsquo;getUser&amp;rsquo; 和 &amp;lsquo;registerUser&amp;rsquo; 的调用，输出结果如下：&lt;/p>
&lt;div class="highlight">&lt;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>&amp;gt;&amp;gt;&amp;gt; User&lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#268bd2">id&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1, &lt;span style="color:#268bd2">name&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#39;username-1&amp;#39;&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;gt;&amp;gt;&amp;gt; &lt;span style="color:#2aa198">2&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="进阶">进阶&lt;/h3>
&lt;h4 id="a-在-rest-中使用-annotation">A. 在 REST 中使用 Annotation&lt;/h4>
&lt;p>在 Dubbo 中使用 annotation 而不是 Spring XML 来暴露和引用服务，对于 REST 协议来说并没有什么不同。有关如何使用 annotation 更详细的用法，请参阅《在 Dubbo 中使用注解》章节。这里主要展示一下与上面基于 Spring XML 配置的例子不同之处。&lt;/p>
&lt;blockquote>
&lt;p>注：本章节讨论的示例可以通过 &lt;a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/annotation">https://github.com/beiwei30/dubbo-rest-samples/tree/master/annotation&lt;/a> 来获得&lt;/p>
&lt;/blockquote>
&lt;h5 id="1-使用-java-configuration-来配置服务提供方的-protocolregistryapplication">1. 使用 Java Configuration 来配置服务提供方的 protocol、registry、application&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@EnableDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>scanBasePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.rest.impl&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> ProtocolConfig &lt;span style="color:#268bd2">protocolConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ProtocolConfig protocolConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;rest&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">8080&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>setServer&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;netty&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocolConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> RegistryConfig &lt;span style="color:#268bd2">registryConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RegistryConfig registryConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;localhost&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">2181&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> registryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;rest-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>通过 &lt;code>@EnableDubbo&lt;/code> 来指定需要扫描 Dubbo 服务的包名，在本例中，UserServiceImpl 在 &amp;ldquo;org.apache.dubbo.samples.rest.impl&amp;rdquo; 下&lt;/li>
&lt;li>通过提供一个 ProtocolConfig 的 Spring Bean 来指定服务提供方按照 REST 来暴露服务&lt;/li>
&lt;li>通过提供一个 RegistryConfig 的 Spring Bean 来指定服务提供方所使用的服务注册机制&lt;/li>
&lt;/ol>
&lt;h5 id="2-使用-service-来申明-dubbo-服务">2. 使用 @Service 来申明 Dubbo 服务&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>简单的使用 &lt;code>@Service&lt;/code> 或者 &lt;code>@Service(protocol = &amp;quot;rest&amp;quot;)&lt;/code> 修饰 &amp;ldquo;UserServiceImpl&amp;rdquo; 来申明一个 Dubbo 服务，这里 &lt;code>protocol = &amp;quot;rest&amp;quot;&lt;/code> 不是必须提供的，原因是通过 Java Configuration 只配置了一个 ProtocolConfig 的示例，在这种情况下，Dubbo 会自动装配该协议到服务中&lt;/li>
&lt;/ol>
&lt;h5 id="3-服务提供方启动类">3. 服务提供方启动类&lt;/h5>
&lt;p>通过使用 &lt;code>ProviderConfiguration&lt;/code> 来初始化一个 &lt;code>AnnotationConfigApplicationContext&lt;/code> 实例，就可以完全摆脱 Spring XML 的配置文件，完全借助 annotation 来装配好一个 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">class&lt;/span> &lt;span style="color:#268bd2">RestProvider&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">(&lt;/span>ProviderConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="4-使用-java-configuration-来配置服务消费方的-registryapplication">4. 使用 Java Configuration 来配置服务消费方的 registry、application&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@EnableDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>scanBasePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.rest.comp&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">@ComponentScan&lt;/span>&lt;span style="color:#719e07">({&lt;/span>&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.rest.comp&amp;#34;&lt;/span>&lt;span style="color:#719e07">})&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ConsumerConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> RegistryConfig &lt;span style="color:#268bd2">registryConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RegistryConfig registryConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;localhost&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">2181&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> registryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;rest-consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>通过 &lt;code>@EnableDubbo&lt;/code> 来指定需要扫描 Dubbo 服务引用 &lt;code>@Reference&lt;/code> 的包名。在本例中，UserService 的引用在 &amp;ldquo;org.apache.dubbo.samples.rest.comp&amp;rdquo; 下&lt;/li>
&lt;li>通过 &lt;code>@ComponentScan&lt;/code> 来指定需要扫描的 Spring Bean 的包名。在本例中，包含 UserService 引用的类 UserServiceComponent 本身需要是一个 Spring Bean，以方便调用，所以，这里指定的包名也是 &amp;ldquo;org.apache.dubbo.samples.rest.comp&amp;rdquo;&lt;/li>
&lt;li>通过提供一个 RegistryConfig 的 Spring Bean 来指定服务消费方所使用的服务发现机制&lt;/li>
&lt;/ol>
&lt;p>这里提到的 UserServiceComponent 的 Spring Bean 定义如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Component&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">UserServiceComponent&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> UserService &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &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> UserService userService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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> User &lt;span style="color:#268bd2">getUser&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Long id&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> userService&lt;span style="color:#719e07">.&lt;/span>getUser&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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> Long &lt;span style="color:#268bd2">registerUser&lt;/span>&lt;span style="color:#719e07">(&lt;/span>User user&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> userService&lt;span style="color:#719e07">.&lt;/span>registerUser&lt;span style="color:#719e07">(&lt;/span>user&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>这里比较好的实践是让这个 Spring Bean 也继承 &lt;code>UserService&lt;/code> 接口，这样在调用的时候也可以面向接口编程&lt;/li>
&lt;/ol>
&lt;h5 id="5-服务调用方启动类">5. 服务调用方启动类&lt;/h5>
&lt;p>通过使用 &lt;code>ConsumerConfiguration&lt;/code> 来初始化一个 &lt;code>AnnotationConfigApplicationContext&lt;/code> 实例，就可以完全摆脱 Spring XML 的配置文件，完全借助 annotation 来装配好一个 Dubbo 的服务消费方。然后就可以通过查找 &lt;code>UserServiceComponent&lt;/code> 类型的 Spring Bean 来发起远程调用。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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">RestConsumer&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">(&lt;/span>ConsumerConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> UserService userService &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>UserServiceComponent&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;gt;&amp;gt;&amp;gt; &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> userService&lt;span style="color:#719e07">.&lt;/span>getUser&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1L&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> User user &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> User&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">2L&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Larry Page&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;gt;&amp;gt;&amp;gt; &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> userService&lt;span style="color:#719e07">.&lt;/span>registerUser&lt;span style="color:#719e07">(&lt;/span>user&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="b-让协议跑在不同的服务器上">B. 让协议跑在不同的服务器上&lt;/h4>
&lt;p>目前 REST 协议在 Dubbo 中可以跑在五种不同的 server 上，分别是：&lt;/p>
&lt;ul>
&lt;li>&amp;ldquo;netty&amp;rdquo;: 直接基于 netty 框架的 rest server，通过 &lt;code>&amp;lt;dubbo:protocol name=&amp;quot;rest&amp;quot; server=&amp;quot;netty&amp;quot;/&amp;gt;&lt;/code> 来配置&lt;/li>
&lt;li>&amp;ldquo;tomcat&amp;rdquo;: 基于嵌入式 tomcat 的 rest server，通过 &lt;code>&amp;lt;dubbo:protocol name=&amp;quot;rest&amp;quot; server=&amp;quot;tomcat&amp;quot;/&amp;gt;&lt;/code> 来配置&lt;/li>
&lt;li>&amp;ldquo;jetty&amp;rdquo;: &lt;strong>默认选项&lt;/strong>，基于嵌入式 jetty 的 rest server，通过 &lt;code>&amp;lt;dubbo:protocol name=&amp;quot;rest&amp;quot; server=&amp;quot;jetty&amp;quot;/&amp;gt;&lt;/code> 来配置&lt;/li>
&lt;li>&amp;ldquo;sunhttp&amp;rdquo;: 使用 JDK 内置的 Sun HTTP server 作为 rest server，通过 &lt;code>&amp;lt;dubbo:protocol name=&amp;quot;rest&amp;quot; server=&amp;quot;sunhttp&amp;quot;/&amp;gt;&lt;/code> 来配置，仅推荐在开发环境中使用&lt;/li>
&lt;li>&amp;ldquo;servlet”: 采用外部应用服务器的 servlet 容器来做 rest server，这个时候，除了配置 &lt;code>&amp;lt;dubbo:protocol name=&amp;quot;rest&amp;quot; server=&amp;quot;servlet&amp;quot;/&amp;gt;&lt;/code> 之外，还需要在 web.xml 中做额外的配置&lt;/li>
&lt;/ul>
&lt;p>由于以上的例子展示了 &amp;ldquo;netty&amp;rdquo; 作为 rest server，下面演示一下使用嵌入式 tomcat 的 rest server 的用法。&lt;/p>
&lt;blockquote>
&lt;p>注：本章节讨论的示例可以通过 &lt;a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/tomcat">https://github.com/beiwei30/dubbo-rest-samples/tree/master/tomcat&lt;/a> 来获得&lt;/p>
&lt;/blockquote>
&lt;h5 id="1-增加-tomcat-相关的依赖">1. 增加 Tomcat 相关的依赖&lt;/h5>
&lt;div class="highlight">&lt;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.apache.tomcat.embed&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>tomcat-embed-core&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;/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>org.apache.tomcat.embed&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>tomcat-embed-logging-juli&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;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="2-配置-protocol-使用-tomcat-作为-rest-server">2. 配置 protocol 使用 tomcat 作为 REST server&lt;/h5>
&lt;div class="highlight">&lt;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:protocol&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;rest&amp;#34;&lt;/span> port=&lt;span style="color:#2aa198">&amp;#34;8080&amp;#34;&lt;/span> server=&lt;span style="color:#2aa198">&amp;#34;tomcat&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>启动服务提供方之后，在以下的输出将会出现与嵌入式 Tomcat 相关的日志信息：&lt;/p>
&lt;div class="highlight">&lt;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>Jan 01, &lt;span style="color:#2aa198">2019&lt;/span> 10:15:12 PM org.apache.catalina.core.StandardContext setPath
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>WARNING: A context path must either be an empty string or start with a &lt;span style="color:#2aa198">&amp;#39;/&amp;#39;&lt;/span> and &lt;span style="color:#719e07">do&lt;/span> not end with a &lt;span style="color:#2aa198">&amp;#39;/&amp;#39;&lt;/span>. The path &lt;span style="color:#719e07">[&lt;/span>/&lt;span style="color:#719e07">]&lt;/span> does not meet these criteria and has been changed to &lt;span style="color:#719e07">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Jan 01, &lt;span style="color:#2aa198">2019&lt;/span> 10:15:13 PM org.apache.coyote.AbstractProtocol init
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO: Initializing ProtocolHandler &lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">&amp;#34;http-nio-8080&amp;#34;&lt;/span>&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Jan 01, &lt;span style="color:#2aa198">2019&lt;/span> 10:15:13 PM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO: Using a shared selector &lt;span style="color:#719e07">for&lt;/span> servlet write/read
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Jan 01, &lt;span style="color:#2aa198">2019&lt;/span> 10:15:13 PM org.apache.catalina.core.StandardService startInternal
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO: Starting service &lt;span style="color:#719e07">[&lt;/span>Tomcat&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Jan 01, &lt;span style="color:#2aa198">2019&lt;/span> 10:15:13 PM org.apache.catalina.core.StandardEngine startInternal
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO: Starting Servlet Engine: Apache Tomcat/8.5.31
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Jan 01, &lt;span style="color:#2aa198">2019&lt;/span> 10:15:13 PM org.apache.coyote.AbstractProtocol start
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO: Starting ProtocolHandler &lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">&amp;#34;http-nio-8080&amp;#34;&lt;/span>&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="c-使用外部的-servlet-容器">C. 使用外部的 Servlet 容器&lt;/h4>
&lt;p>进一步的，还可以使用外部的 servlet 容器来启动 Dubbo 的 REST 服务。&lt;/p>
&lt;blockquote>
&lt;p>注：本章节讨论的示例可以通过 &lt;a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet">https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet&lt;/a> 来获得&lt;/p>
&lt;/blockquote>
&lt;h5 id="1-修改-pomxml-改变打包方式">1. 修改 pom.xml 改变打包方式&lt;/h5>
&lt;p>因为使用的是外部的 servlet 容器，需要将打包方式修改为 &amp;ldquo;war&amp;rdquo;&lt;/p>
&lt;div class="highlight">&lt;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;packaging&amp;gt;&lt;/span>war&lt;span style="color:#268bd2">&amp;lt;/packaging&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="2-修改-rest-providerxml">2. 修改 rest-provider.xml&lt;/h5>
&lt;p>配置 &amp;ldquo;server&amp;rdquo; 为 &amp;ldquo;servlet&amp;rdquo; 表示将使用外部的 servlet 容器。并配置 &amp;ldquo;contextpath&amp;rdquo; 为 &amp;ldquo;&amp;quot;，原因是在使用外部 servlet 容器时，Dubbo 的 REST 支持需要知道被托管的 webapp 的 contextpath 是什么。这里我们计划通过 root context path 来部署应用，所以配置其为 &amp;ldquo;&amp;quot;。&lt;/p>
&lt;div class="highlight">&lt;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:protocol&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;rest&amp;#34;&lt;/span> port=&lt;span style="color:#2aa198">&amp;#34;8080&amp;#34;&lt;/span> server=&lt;span style="color:#2aa198">&amp;#34;servlet&amp;#34;&lt;/span> contextpath=&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="3-配置-web-infwebxml">3. 配置 WEB-INF/web.xml&lt;/h5>
&lt;div class="highlight">&lt;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;web-app&lt;/span> xmlns=&lt;span style="color:#2aa198">&amp;#34;http://xmlns.jcp.org/xml/ns/javaee&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&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> xsi:schemaLocation=&lt;span style="color:#2aa198">&amp;#34;http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version=&lt;span style="color:#2aa198">&amp;#34;3.1&amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;context-param&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #1 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;param-name&amp;gt;&lt;/span>contextConfigLocation&lt;span style="color:#268bd2">&amp;lt;/param-name&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;param-value&amp;gt;&lt;/span>/WEB-INF/classes/spring/rest-provider.xml&lt;span style="color:#268bd2">&amp;lt;/param-value&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/context-param&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;listener&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;listener-class&amp;gt;&lt;/span>com.alibaba.dubbo.remoting.http.servlet.BootstrapListener&lt;span style="color:#268bd2">&amp;lt;/listener-class&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/listener&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;listener&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;listener-class&amp;gt;&lt;/span>org.springframework.web.context.ContextLoaderListener&lt;span style="color:#268bd2">&amp;lt;/listener-class&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/listener&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;servlet&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #2 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;servlet-name&amp;gt;&lt;/span>dispatcher&lt;span style="color:#268bd2">&amp;lt;/servlet-name&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;servlet-class&amp;gt;&lt;/span>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet&lt;span style="color:#268bd2">&amp;lt;/servlet-class&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;load-on-startup&amp;gt;&lt;/span>1&lt;span style="color:#268bd2">&amp;lt;/load-on-startup&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/servlet&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;servlet-mapping&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;servlet-name&amp;gt;&lt;/span>dispatcher&lt;span style="color:#268bd2">&amp;lt;/servlet-name&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;url-pattern&amp;gt;&lt;/span>/api/*&lt;span style="color:#268bd2">&amp;lt;/url-pattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/servlet-mapping&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>配置 Dubbo 和 Spring 相关的 ContextListener，打开 Dubbo HTTP 支持，以及通过 rest-provider.xml 来装配 Dubbo 服务&lt;/li>
&lt;li>配置 Dubbo HTTP 所需的 DiapatcherServlet&lt;/li>
&lt;/ol>
&lt;p>这样做之后，不再需要 RestProvider 来启动 Dubbo 服务，可以将其从工程中删掉。对应的，现在 Dubbo 的服务将会随着 Servlet 容器的启动而启动。启动完毕之后，可以通过类似 &amp;ldquo;http://localhost:8080/api/users/1&amp;rdquo; 来访问暴露出的 REST 服务。需要注意的是，这个例子里假定了服务提供方的 WAR 包部署在 root context path 上，所以当该应用通过 IDE 配置的 tomcat server 启动时，需要指定 Application Context 为 &amp;ldquo;/&amp;quot;。&lt;/p>
&lt;h4 id="d-增加-swagger-支持">D. 增加 Swagger 支持&lt;/h4>
&lt;p>在上面使用外部 Servlet 容器的例子的基础上，讨论如何暴露 Swagger OpenApi 以及如何继承 Swagger UI。&lt;/p>
&lt;blockquote>
&lt;p>注：本章节讨论的示例可以通过 &lt;a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet">https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet&lt;/a> 来获得&lt;/p>
&lt;/blockquote>
&lt;h5 id="1-暴露-swagger-openapi">1. 暴露 Swagger OpenApi&lt;/h5>
&lt;p>增加 swagger 相关依赖，以便通过 &amp;ldquo;http://localhost:8080/openapi.json&amp;rdquo; 来访问 REST 服务的描述&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;swagger.version&amp;gt;&lt;/span>2.0.6&lt;span style="color:#268bd2">&amp;lt;/swagger.version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/properties&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;dependencies&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>io.swagger.core.v3&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>swagger-jaxrs2&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>${swagger.version}&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 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>io.swagger.core.v3&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>swagger-jaxrs2-servlet-initializer&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>${swagger.version}&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 style="color:#268bd2">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>修改 WEB-INF/web.xml，增加 openapi servlet 的配置&lt;/p>
&lt;div class="highlight">&lt;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;web-app&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;servlet&amp;gt;&lt;/span> &lt;span style="color:#586e75">&amp;lt;!-- #3 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;servlet-name&amp;gt;&lt;/span>openapi&lt;span style="color:#268bd2">&amp;lt;/servlet-name&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;servlet-class&amp;gt;&lt;/span>io.swagger.v3.jaxrs2.integration.OpenApiServlet&lt;span style="color:#268bd2">&amp;lt;/servlet-class&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/servlet&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;servlet-mapping&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;servlet-name&amp;gt;&lt;/span>openapi&lt;span style="color:#268bd2">&amp;lt;/servlet-name&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;url-pattern&amp;gt;&lt;/span>/openapi.json&lt;span style="color:#268bd2">&amp;lt;/url-pattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;url-pattern&amp;gt;&lt;/span>/openapi.yaml&lt;span style="color:#268bd2">&amp;lt;/url-pattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/servlet-mapping&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/web-app&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>重新启动应用之后，可以通过访问 &amp;ldquo;http://localhost:8080/openapi.json&amp;rdquo; 或者 &amp;ldquo;http://localhost:8080/openapi.yaml&amp;rdquo; 来访问暴露出的 openapi 的契约，以下是 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">openapi&lt;/span>: &lt;span style="color:#2aa198">3.0.1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">paths&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> /api/users/{id}:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">get&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">operationId&lt;/span>: getUser
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">parameters&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: id
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">in&lt;/span>: path
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">required&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">schema&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: integer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">format&lt;/span>: int64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">responses&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">description&lt;/span>: default response
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">content&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">application/json&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">schema&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">$ref&lt;/span>: &lt;span style="color:#2aa198">&amp;#39;#/components/schemas/User&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">text/xml&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">schema&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">$ref&lt;/span>: &lt;span style="color:#2aa198">&amp;#39;#/components/schemas/User&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">/api/users/register&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">post&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">operationId&lt;/span>: registerUser
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">requestBody&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">description&lt;/span>: a user to register
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">content&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">application/json&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">schema&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">$ref&lt;/span>: &lt;span style="color:#2aa198">&amp;#39;#/components/schemas/User&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">text/xml&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">schema&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">$ref&lt;/span>: &lt;span style="color:#2aa198">&amp;#39;#/components/schemas/User&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">responses&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">description&lt;/span>: default response
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">content&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">application/json&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">schema&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: integer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">format&lt;/span>: int64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">text/xml&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">schema&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: integer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">format&lt;/span>: int64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">components&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">schemas&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">User&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: object
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">properties&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">id&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: integer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">format&lt;/span>: int64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: string
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="2-集成-swagger-ui">2. 集成 Swagger UI&lt;/h5>
&lt;p>在 pom.xml 中继续增加 swagger-ui 的依赖，这里使用的是 webjars 的版本，从集成的角度来说更加简洁。webjars 的工作机制可以参阅 webjars 官网 &lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup>&lt;/p>
&lt;div class="highlight">&lt;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;properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;swagger.webjar.version&amp;gt;&lt;/span>3.20.3&lt;span style="color:#268bd2">&amp;lt;/swagger.webjar.version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/properties&amp;gt;&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;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.webjars&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>swagger-ui&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>${swagger.webjar.version}&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 style="color:#268bd2">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在工程的 webapp/WEB-INF 根目录下增加一个 HTML 文件，内容如下。HTML 文件名可以为任何名字，没有硬性要求，如果该文件被命名为 &amp;ldquo;swagger-ui.html&amp;rdquo;，那么你可以通过访问 “http://localhost:8080/swagger-ui.html&amp;rdquo; 来访问 swagger UI。本例为了演示方便起见，将其命名为 &amp;ldquo;index.html&amp;rdquo;，这样当访问 &amp;ldquo;http://localhost:8080&amp;rdquo; 时，就可以很方便的得到 swagger UI 的页面。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-html" data-lang="html">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">&amp;lt;!DOCTYPE html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#268bd2">html&lt;/span> lang&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;en&amp;#34;&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#268bd2">head&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#268bd2">meta&lt;/span> charset&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;UTF-8&amp;#34;&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#268bd2">title&lt;/span>&amp;gt;API UI&amp;lt;/&lt;span style="color:#268bd2">title&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#268bd2">link&lt;/span> rel&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;stylesheet&amp;#34;&lt;/span> type&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;text/css&amp;#34;&lt;/span> href&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;webjars/swagger-ui/3.20.3/swagger-ui.css&amp;#34;&lt;/span> &amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#268bd2">link&lt;/span> rel&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;icon&amp;#34;&lt;/span> type&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;image/png&amp;#34;&lt;/span> href&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;webjars/swagger-ui/3.20.3/favicon-32x32.png&amp;#34;&lt;/span> sizes&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;32x32&amp;#34;&lt;/span> /&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#268bd2">link&lt;/span> rel&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;icon&amp;#34;&lt;/span> type&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;image/png&amp;#34;&lt;/span> href&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;webjars/swagger-ui/3.20.3/favicon-16x16.png&amp;#34;&lt;/span> sizes&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;16x16&amp;#34;&lt;/span> /&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#268bd2">style&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">html&lt;/span>
&lt;/span>&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">box-sizing&lt;/span>: &lt;span style="color:#cb4b16">border-box&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">overflow&lt;/span>: &lt;span style="color:#719e07">-moz-&lt;/span>scrollbars-vertical;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">overflow-y&lt;/span>: &lt;span style="color:#cb4b16">scroll&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">*,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">*&lt;/span>:&lt;span style="color:#268bd2">before&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">*&lt;/span>:&lt;span style="color:#268bd2">after&lt;/span>
&lt;/span>&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">box-sizing&lt;/span>: &lt;span style="color:#cb4b16">inherit&lt;/span>;
&lt;/span>&lt;/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">body&lt;/span>
&lt;/span>&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">margin&lt;/span>:&lt;span style="color:#2aa198">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">background&lt;/span>: &lt;span style="color:#2aa198">#fafafa&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#268bd2">style&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/&lt;span style="color:#268bd2">head&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#268bd2">body&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#268bd2">div&lt;/span> id&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;swagger-ui&amp;#34;&lt;/span>&amp;gt;&amp;lt;/&lt;span style="color:#268bd2">div&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#268bd2">script&lt;/span> src&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;webjars/swagger-ui/3.20.3/swagger-ui-bundle.js&amp;#34;&lt;/span>&amp;gt; &amp;lt;/&lt;span style="color:#268bd2">script&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#268bd2">script&lt;/span> src&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;webjars/swagger-ui/3.20.3/swagger-ui-standalone-preset.js&amp;#34;&lt;/span>&amp;gt; &amp;lt;/&lt;span style="color:#268bd2">script&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#268bd2">script&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">window&lt;/span>.onload &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">function&lt;/span> () {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">window&lt;/span>.ui &lt;span style="color:#719e07">=&lt;/span> SwaggerUIBundle({
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;openapi.json&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dom_id&lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#39;#swagger-ui&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> deepLinking&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> presets&lt;span style="color:#719e07">:&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SwaggerUIBundle.presets.apis,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SwaggerUIStandalonePreset
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> plugins&lt;span style="color:#719e07">:&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SwaggerUIBundle.plugins.DownloadUrl
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> layout&lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;StandaloneLayout&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>&amp;lt;/&lt;span style="color:#268bd2">script&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/&lt;span style="color:#268bd2">body&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/&lt;span style="color:#268bd2">html&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>再次重启服务器，并访问 &amp;ldquo;http://localhost:8080&amp;rdquo; 时，将会看到 swagger UI 页面的展示：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/swagger-ui.png" alt="swagger-ui">&lt;/p>
&lt;p>通过 Swagger UI 可以很方便的浏览当前服务器提供的 REST 服务的文档信息，甚至可以直接调用来做服务测试。以 &amp;lsquo;/api/users/{id}&amp;rsquo; 为例，测试结果如下图所示：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/swagger-ui-execute.png" alt="swagger-ui-execute">&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>本文主要关注了在 Dubbo 中支持 REST 协议的情况。首先探索了 REST 概念的起源，澄清了 REST 是一种适合互联网的软件架构风格，进一步的说明了 REST 风格的架构可以与 HTTP 协议无关，但是 HTTP 协议的确是 REST 风格架构的最常用甚至是最佳的组合和搭档。然后讨论了如何在 Dubbo 中开发 REST HTTP 的几种典型用法，其中包括了通过不同的配置，如传统的 Spring XML，完全通过 annotation 来配置两种典型的用法，本文中没有涉及到的还有纯 API 编程方式，Spring Boot 配置方式也是完全可以的，因为篇幅原因没有提及；还讨论了如何通过不同的 REST server 来暴露 REST HTTP 服务，包括了 embedded tomcat，netty，以及外置的 servlet 容器等几种用法。最后，在外置的 servlet 容器的基础上，进一步的讨论了如何通过 Swagger 暴露 openAPI 以及集成 Swagger UI 的方法。&lt;/p>
&lt;p>本文没有涉及的内容包含但不限于国际化支持、Dubbo REST 更高阶的注入扩展的用法、以及 Dubbo REST 支持未来的规划。其中 Dubbo REST 扩展的支持可以参考 &lt;a href="https://github.com/beiwei30/dubbo-rest-samples/tree/master/extensions">https://github.com/beiwei30/dubbo-rest-samples/tree/master/extensions&lt;/a> 中的演示。以后有机会会开专门的篇幅来探讨更高级的 Swagger 的支持、以及对未来的展望。&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="http://en.wikipedia.org/wiki/Roy_Fielding">http://en.wikipedia.org/wiki/Roy_Fielding&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm">http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>&lt;a href="https://martinfowler.com/articles/richardsonMaturityModel.html">https://martinfowler.com/articles/richardsonMaturityModel.html&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>&lt;a href="https://github.com/dangdangdotcom/dubbox">https://github.com/dangdangdotcom/dubbox&lt;/a>&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>&lt;a href="https://www.webjars.org/documentation#servlet3">https://www.webjars.org/documentation#servlet3&lt;/a>&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Blog: 使用 Dubbo 连接异构微服务体系</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;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:#586e75">&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 style="color:#719e07">;&lt;/span>
&lt;/span>&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>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greetingService&lt;span style="color:#719e07">.&lt;/span>sayHi&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello world!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下图是 Dubbo 的基本工作原理图，服务提供者与服务消费者之间通过注册中心协调地址，通过约定的协议实现数据交换。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/architecture.png" alt="Dubbo basic work flow">&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>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/microservices.png" alt="2">&lt;/p>
&lt;p>&lt;strong>2. Dubbo 体系内部&lt;/strong>&lt;/p>
&lt;p>&lt;strong>多协议、多注册中心集群的问题在同构的微服务体系中也可能存在，尤其是当一个组织内部的微服务规模增长到一定量级的时候。&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>我们可能要在不同的服务之间采用不同的通信协议，因为不同的服务面临不同的业务场景，而这也进一步导致了数据传输特点的不同，我们需要分别采用更适合各类业务特点的协议。比如典型的场景：我们可能对于普通的业务服务采用 Dubbo 协议，对于和 FrontEnd 交互的服务需要 HTTP 协议，而对于需要流式数据传输的业务则采用 gRPC 协议等等。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Dubbo 体系内部另一个常出现的问题是，在大规模分布式部署的场景下，微服务系统会做跨区域、跨注册中心的部署，这个时候就会出现多集群间地址同步和流量调度的问题。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>总结起来，**不论是同构体系还是异构体系，都面临对多协议通信、多注册中心集群地址发现的问题。**Dubbo 目前是支持多协议、多注册中心的，可以说就是为解决我们上面分析的 Dubbo 同构体系内的场景而设计的，因此下面我们从同构体系的多协议、多注册中心场景讲起，先了解 Dubbo 多协议、多注册中心的基本支持情况以及它们是如何工作的。而在后面的一章再进一步探索怎么扩展这个能力来支持异构微服务体系的互联互通。&lt;/p>
&lt;h2 id="dubbo-体系内的多协议多注册中心机制">Dubbo 体系内的多协议、多注册中心机制&lt;/h2>
&lt;p>我们将通过两个场景示例，来分别具体的讲一下 Dubbo 的多协议、多注册中心机制的使用方式和工作原理。&lt;/p>
&lt;h3 id="多协议">多协议&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/multiregistries.png" alt="multiregistries.png">&lt;/p>
&lt;p>以上是使用 Dubbo 开发的一套微服务，服务间通信使用到了不同的协议，根据我们的调研发现，公司内部启用多协议其实是非常普遍需求，具体场景在此我们暂不做解释。&lt;/p>
&lt;p>应用 B 作为服务提供者，发布了 5 个服务，其中：&lt;/p>
&lt;ul>
&lt;li>&lt;code>DemoService1&lt;/code> &lt;code>DemoService2&lt;/code> 通过 &lt;code>dubbo&lt;/code> 协议发布&lt;/li>
&lt;li>&lt;code>DemoService3&lt;/code> &lt;code>DemoService4&lt;/code> 通过 &lt;code>gRPC&lt;/code> 协议发布&lt;/li>
&lt;li>&lt;code>DemoService0&lt;/code> 通过 &lt;code>dubbo&lt;/code> 、&lt;code>gRPC&lt;/code> 双协议发布&lt;/li>
&lt;/ul>
&lt;p>应用 A 作为消费者，使用 dubbo 协议消费 &lt;code>DemoService1&lt;/code> &lt;code>DemoService2&lt;/code>，使用 gRPC 协议消费 &lt;code>DemoService0&lt;/code>。&lt;/p>
&lt;p>应用 B 作为消费者，使用 gRPC 协议消费 &lt;code>DemoService2&lt;/code> &lt;code>DemoService4&lt;/code>，使用 dubbo 协议消费 &lt;code>DemoService0&lt;/code>。&lt;/p>
&lt;p>以下是具体的代码配置：&lt;/p>
&lt;ol>
&lt;li>提供端应用 B&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-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.DemoService1&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&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.DemoService2&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&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.basic.api.DemoService3&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&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.DemoService4&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&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.basic.api.DemoService0&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo, grpc&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>消费端应用 A&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-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService1&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> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService2&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> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService0&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>消费端应用 C&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-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService3&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService4&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> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService0&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="dubbo-多协议支持现状">Dubbo 多协议支持现状&lt;/h4>
&lt;p>Dubbo 目前所支持的协议包括 Dubbo、REST、Thrift、gRPC、JsonRPC、Hessian 等，基本涵盖了业界大多数主流的 RPC 通信协议。需要注意的是，这些协议的支持都是以直接集成官方 Release 实现的形式来做的，我认为这是一个很好的选择，既保证了协议解析自身的稳定性，又能使 Dubbo 社区更专注的将更多的精力放在 Dubbo 外围服务治理能力的改善上。试想如果 Dubbo 社区自己为每个协议提供实现，那是要花费多少精力和时间才能使每种协议达到稳定的生产可用。&lt;/p>
&lt;p>除了以上官方提供支持的协议之外，得益于 Dubbo 灵活的扩展机制，想要为 Dubbo 扩展协议非常容易，开发者可以随时为 Dubbo 增加更多的协议支持，包括自有协议扩展。&lt;/p>
&lt;p>关于对 gRPC (HTTP/2) 协议的支持，请参阅上期文档&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-screen.png" alt="dubbo-screen.png">&lt;/p>
&lt;h4 id="多协议能解决的问题">多协议能解决的问题&lt;/h4>
&lt;ul>
&lt;li>
&lt;p>将 RPC 框架无缝地接入 Dubbo 的服务治理体系。&lt;/p>
&lt;p>通过协议扩展将 RPC 协议纳入 Dubbo 服务开发体系，从而复用 Dubbo 的编程模型和服务发现、流量管控等能力。比如 gRPC，其服务治理体系相对比较弱、编程 API 不够友好，很难直接用于微服务开发。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>满足不同场景的调用需求。&lt;/p>
&lt;p>各个服务可能是为了满足不同业务需求而开发，同时外围消费端应用的技术栈也可能多种多样，通过启用不同的通信协议，可以最优化不同场景的通信需求。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>实现协议间的迁移。&lt;/p>
&lt;p>通过支持多种协议，借助注册中心的协调，可以快速满足公司内协议迁移的需求。如从自有协议升级到 Dubbo 协议，Dubbo 协议自身升级，从 Dubbo 协议迁移到 gRPC，从 REST 迁移到 Dubbo 协议等。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="多注册中心">多注册中心&lt;/h3>
&lt;p>当服务集群规模小的时候，一个中心化的集群部署方案能很好的解决我们的业务问题。但是随着应用规模的增长、用户流量的增加，我们就不得不考虑要为业务系统引入跨区域、多集群的部署方案，而此时同业务系统密切相关的注册中心集群也面临部署方案的选型：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>继续维持全局共享的注册中心集群。这种架构方案的优点是简单；缺点是注册中心集群由于要保存全量的地址数据，存储和推送压力会变得很大，另外对于一些注册中心产品（如 Zookeeper 等）在跨集群网络部署的场景下稳定性和性能可能都会面临挑战。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>每个业务集群部署独立的注册中心集群。多注册中心集群的优点是能解决跨集群网络可用性的问题，同时也能够减轻注册中心的存储和推送压力；缺点则是要求服务框架（如 Dubbo 等）能有同时发布/监听多个注册中心集群的能力。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>下面我们具体看一下，Dubbo 为多注册中心集群场景提供的解决方案。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/multisubscribe.png" alt="multisubscribe.png">&lt;/p>
&lt;p>上图有两个业务集群，分别部署在北京和上海，每个业务集群有自己独立的注册中心集群，要解决两个业务集群间服务的透明 RPC 通信问题。&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-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;beijingRegistry&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> default=&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address2}&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.multi.registry.api.HelloService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;helloService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry,beijingRegistry&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.multi.registry.api.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry,beijingRegistry&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&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-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;beijingRegistry&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> default=&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span> preferred=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> weight=&lt;span style="color:#2aa198">&amp;#34;100&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address2}&amp;#34;&lt;/span> default=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> weight=&lt;span style="color:#2aa198">&amp;#34;20&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> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.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>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.DemoService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;beijingRegistry, shanghaiRegistry&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> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.HelloService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;beijingRegistry&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> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.HelloService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry,shanghaiRegistry&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="dubbo-对异构注册中心集群的支持">Dubbo 对异构注册中心集群的支持&lt;/h4>
&lt;p>虽然我们会做多注册中心集群部署，但通常情况下，我们部署的都是相同的注册中心产品，如都是 Zookeeper、Nacos；而对于注册中心迁移的场景，则要求 Dubbo 能提供对更多的注册中心产品的支持，或者最重要的要有很好的扩展能力。Dubbo 官方目前支持的注册中心实现有：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-screen2.png" alt="dubbo-screen2.png">&lt;/p>
&lt;p>这里需要特别提到的一点是，当前 Dubbo 的服务注册/发现模型是以接口为粒度的，而从 2.7.5 版本开始，Dubbo 新引入了应用粒度的服务注册/发现模型。这一方面有助于优化 Dubbo 当前服务发现机制、提升服务容量，另一方面对于联通以 SpringCloud 为代表的微服务体系也非常重要（关于这点在下一章中有进一步提及）。更多关于《应用粒度服务发现：服务自省》的介绍，我们将在接下来的文章或文档中予以补充，请持续关注。&lt;/p>
&lt;h4 id="多订阅带来的流量调度问题">多订阅带来的流量调度问题&lt;/h4>
&lt;p>在引入多注册中心集群后，Dubbo 在流量选址时的多了一层注册中心集群间的负载均衡：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/cluster-lb.png" alt="cluster-lb.png">&lt;/p>
&lt;p>在 Cluster Invoker 这一级，我们支持的选址策略有（2.7.5+ 版本，具体使用请参见文档）：&lt;/p>
&lt;ul>
&lt;li>
&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:#586e75">&amp;lt;!-- 来自 preferred=“true” 注册中心的地址将被优先选择，只有该中心无可用地址时才 Fallback 到其他注册中心 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> preferred=&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;/li>
&lt;li>
&lt;p>同 zone 优先&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 选址时会和流量中的 zone key 做匹配，流量会优先派发到相同 zone 的地址 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> zone=&lt;span style="color:#2aa198">&amp;#34;beijing&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&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:#586e75">&amp;lt;!-- 来自北京和上海集群的地址，将以 10:1 的比例来分配流量 --&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> id=&lt;span style="color:#2aa198">&amp;#34;beijing&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> weight=&lt;span style="color:#2aa198">”100“&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> id=&lt;span style="color:#2aa198">&amp;#34;shanghai&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address2}&amp;#34;&lt;/span> weight=&lt;span style="color:#2aa198">”10“&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>默认，stick to 任意可用&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 id="多注册中心适用的场景">多注册中心适用的场景&lt;/h4>
&lt;ul>
&lt;li>
&lt;p>同区域流量优先调度&lt;/p>
&lt;p>出于容灾或者服务伸缩性需求，服务/应用往往需要部署在多个独立的机房/区域，在每个区域有独立注册中心集群的场景下，实现同区域的流量优先调度就能很好的解决延迟和可用性问题。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>注册中心迁移&lt;/p>
&lt;p>公司的服务一直以来可能是存储在某一个注册中心，如 Zookeeper，但到了某个时间节点，因为各种各样的原因，当我们要迁移到另外的注册中心时，多注册中心模型能够保证平滑的迁移。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>异构系统互通&lt;/p>
&lt;p>不同微服务体系开发的服务，都封闭在各自的服务发现体系中，而通过统一的多注册中心模型，可以实现不同体系的服务互相发现。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="借助-dubbo-联通异构的微服务体系">借助 Dubbo 联通异构的微服务体系&lt;/h2>
&lt;p>上文我们提到了在组织内存在异构微服务体系的各种合理可能性，现在我们来具体看一下异构微服务体系的实际场景，以及使用 Dubbo 实现互联互通的解决方法。首先我们先通过一张图来看一下，联通异构的微服务体系具体是一个什么样的场景。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/heterogeneous.png" alt="heterogeneous.png">&lt;/p>
&lt;p>如上图所示，我们有部分微服务可以是基于 SpringCloud、gRPC、K8S 或者是自建体系构建的，他们各自之间默认是相互隔离无法联通的。当我们再构建一套基于 Dubbo 的微服务体系时，则利用 Dubbo 的多协议、多服务发现模型，我们就可以做到和各个微服务体系间的两两之间的互联互通。进一步的，如图中橙色箭头所示，依赖 Dubbo 体系作为桥接层，我们还可以实现两个异构微服务体系间的打通。&lt;/p>
&lt;p>对于以下几个示例场景，由于在地址发现层面目前没有统一的标准，我们暂且假设地址发现层面不同的体系建是没有障碍的，我们将重点关注迁移的基本流程以及通信协议环节。（关于地址发现部分，我们将在后续《服务自省：基于应用粒度的服务发现》之后再深入探讨）&lt;/p>
&lt;h3 id="dubbo-体系内的协议迁移共存">Dubbo 体系内的协议迁移（共存）&lt;/h3>
&lt;p>绝大多数开发者对 Dubbo 有这么一个固有认知：使用 Dubbo 开发微服务系统，则就要用 Dubbo 协议来作为服务间的通信协议才是最优方案。实际上，我们完全没有必要只束缚在 Dubbo RPC 协议上。Dubbo 作为微服务开发框架和 Dubbo 作为 RPC 协议这是两个概念，其实是完全可以分开来看待的，比如我们用 Dubbo 框架开发的业务系统，选用 rest、gRPC 通信是完全没有问题的（参加 Dubbo 支持的协议列表），具体用什么协议根据业务特点和技术规划才是最适合的。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/grpcrest.png" alt="grpcrest.png">&lt;/p>
&lt;p>当前在云原生、Mesh 的大背景下， HTTP1/2、gRPC 协议开始受到越来越多的关注，一方面原因自然是因为它们在标准化方面做的更好，得到的更多的网络设备和基础设施的支持，具备更好的通用性和穿透性。对于很多有云原生迁移意愿的企业来说，往此类协议迁移无疑将对之后的架构升级有更多的帮助。&lt;/p>
&lt;p>下图演示了在 Dubbo 体系内，从 Dubbo 协议向 gRPC 协议迁移的一个中间状态。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/migrate.png" alt="migrate.png">&lt;/p>
&lt;ul>
&lt;li>最左边的代表尚未迁移的老应用，这类应用在迁移过程中仍然要消费和提供 Dubbo 协议的服务。&lt;/li>
&lt;li>中间的代表处于迁移中的应用，他们中间可能有些是服务提供者，既要为左边的老系统提供提供 Dubbo 协议服务；又要为右边的新系统提供 gRPC 服务；因此他们都是双协议暴露服务。&lt;/li>
&lt;li>最右边则代表是新开发的或者已经迁移完成的应用，这个体系内已能完全用 gRPC 协议通信。&lt;/li>
&lt;li>最终度过中间态后，我们期望所有的应用都达到最左边应用的状态，实现完全的 gRPC 协议通信。&lt;/li>
&lt;/ul>
&lt;h3 id="spring-cloud-体系迁移到-dubbo-体系共存">Spring Cloud 体系迁移到 Dubbo 体系（共存）&lt;/h3>
&lt;p>如前文所述，由于 SpringCloud 和 Dubbo 间服务发现模型的问题，要两个体系间的地址互通需要 Dubbo 侧作相应的适配，关于这部分内容将在接下来的 2.7.5 版本《服务自省》部分发布，在此我们暂且认为已经打通。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/migrate-final.png" alt="migrate-final.png">&lt;/p>
&lt;p>Dubbo 体系内的部分应用作为透明的联通两个体系的关键节点，部分服务提供者应用要双协议发布、部分消费者应用要做到选定协议消费。由于老的 Spring Cloud 体系不允许做任何改动，因此联通两套体系的关键是 REST 协议，对 Dubbo 侧的应用来说：&lt;/p>
&lt;ul>
&lt;li>部分应用可能要以 REST 协议消费 SpringCloud 的服务；&lt;/li>
&lt;li>部分应用可能要暴露 REST 协议共 SpringCloud 消费；&lt;/li>
&lt;li>Dubbo 自有体系内则通过自己选定的协议通信，这里就比较灵活了，可以是 Dubbo、REST、gRPC 等其中的任一种。而如果选定 REST 协议则对于与 SpringCloud 体系的联通就变得更加自然了，因为两端的协议都是统一的。&lt;/li>
&lt;/ul>
&lt;p>对于消费 Spring Cloud 服务的应用，要配置服务 ：&lt;/p>
&lt;div class="highlight">&lt;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> interface =&lt;span style="color:#2aa198">&amp;#34;xxx.SpringService&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;rest&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>对于提供服务给 Spring Cloud 侧消费的应用，则指定服务暴露为 rest 协议，或者双协议暴露（因如果这个服务还要被新体系内的应用调用到）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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;xxx.NewService&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;rest,dubbo&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>作为 Dubbo 的维护者，虽然我们这里有明显的偏向性，讲的是从如何从 SpringCloud 体系迁移到 Dubbo 体系。但是反过来考虑，如果你已经或者即将选型 Dubbo 来开发微服务，则未来从 Dubbo 迁移到 SpringCloud 也是同样的思路，Dubbo 的多协议、多注册模型为双向迁移都提供了同样的灵活性。&lt;/p>
&lt;h3 id="自建体系迁移到-dubbo-体系共存">自建体系迁移到 Dubbo 体系（共存）&lt;/h3>
&lt;p>这个场景和上一节中讲到的的 SpringCloud 迁移有些类似，最大的区别在于 rest 协议是 Dubbo 官方默认提供支持的，而对于已有的微服务体系内的私有通信协议，则需要先要自己去扩展 Dubbo Protocol 来提供协议层面的支持，关于 Protocol 如何扩展请参见以下官方文档：&lt;/p>
&lt;p>/zh/docs/dev/impls/protocol.html&lt;/p>
&lt;h2 id="总结与展望">总结与展望&lt;/h2>
&lt;p>要实现异构微服务体系间的共存或迁移，关键点在打通异构体系间的&lt;code>协议&lt;/code>与&lt;code>服务发现&lt;/code>，得益于 Dubbo 自身对多协议、多注册模型的支持，我们可以很容易的使 Dubbo 成为桥接异构微服务体系的中间层。熟悉 Dubbo 多协议实现细节的同学，可能会担心在服务数量较多的场景下，多协议注册会导致地址数量翻倍从而影响地址推送性能；另外在文中《借助 Dubbo 联通异构的微服务体系》一节，关于如何实现异构体系间的透明服务发现部分我们没有做详细的说明。关于涉及服务发现的这部分，我们将在接下来的文章中做具体阐述，看看 Dubbo 2.7.5 版本引入新的服务发现机制是如何解决这个问题的，请持续关注后续文章及 Dubbo 官方文档。&lt;/p></description></item><item><title>Blog: Dubbo可扩展机制源码解析</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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/" title="">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>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 扩展点必须是接口
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>type&lt;span style="color:#719e07">.&lt;/span>isInterface&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Extension type(&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) is not interface!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 必须要有@SPI注解
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>withExtensionAnnotation&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Extension type without @SPI Annotation!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 从缓存中根据接口获取对应的ExtensionLoader
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 每个扩展只会被加载一次
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> loader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> EXTENSION_LOADERS&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>loader &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 初始化扩展
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> EXTENSION_LOADERS&lt;span style="color:#719e07">.&lt;/span>putIfAbsent&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>type&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> EXTENSION_LOADERS&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> loader&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> type&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>type &lt;span style="color:#719e07">=&lt;/span> type&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> objectFactory &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>type &lt;span style="color:#719e07">==&lt;/span> ExtensionFactory&lt;span style="color:#719e07">.&lt;/span>class &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">:&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ExtensionFactory&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;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>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 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&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>holder &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cachedInstances&lt;span style="color:#719e07">.&lt;/span>putIfAbsent&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">,&lt;/span> &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&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object instance &lt;span style="color:#719e07">=&lt;/span> holder&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 从缓存中获取，如果不存在就创建
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>holder&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> holder&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> createExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> holder&lt;span style="color:#719e07">.&lt;/span>set&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>getExtension 方法中做了一些判断和缓存，主要的逻辑在 createExtension 方法中。我们继续看 createExtension 方法。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> T &lt;span style="color:#268bd2">createExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 根据扩展点名称得到扩展类，比如对于LoadBalance，根据random得到RandomLoadBalance类
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> clazz &lt;span style="color:#719e07">=&lt;/span> getExtensionClasses&lt;span style="color:#719e07">().&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> T instance &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> EXTENSION_INSTANCES&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 使用反射调用nesInstance来创建扩展类的一个示例
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> EXTENSION_INSTANCES&lt;span style="color:#719e07">.&lt;/span>putIfAbsent&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> clazz&lt;span style="color:#719e07">.&lt;/span>newInstance&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> EXTENSION_INSTANCES&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 对扩展类示例进行依赖注入
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> injectExtension&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 如果有wrapper，添加wrapper
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> wrapperClasses &lt;span style="color:#719e07">=&lt;/span> cachedWrapperClasses&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>wrapperClasses &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>wrapperClasses&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> wrapperClass &lt;span style="color:#719e07">:&lt;/span> wrapperClasses&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> injectExtension&lt;span style="color:#719e07">((&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> wrapperClass&lt;span style="color:#719e07">.&lt;/span>getConstructor&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">).&lt;/span>newInstance&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>createExtension方法做了以下事情:&lt;/p>
&lt;ol>
&lt;li>先根据name来得到对应的扩展类。从ClassPath下&lt;code>META-INF&lt;/code>文件夹下读取扩展点配置文件。&lt;/li>
&lt;li>使用反射创建一个扩展类的实例&lt;/li>
&lt;li>对扩展类实例的属性进行依赖注入，即IOC。&lt;/li>
&lt;li>如果有wrapper，添加wrapper。即AOP。&lt;/li>
&lt;/ol>
&lt;p>下面我们来重点看下这4个过程&lt;/p>
&lt;ol>
&lt;li>根据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">private&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> getExtensionClasses&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> classes &lt;span style="color:#719e07">=&lt;/span> cachedClasses&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>classes &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedClasses&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classes &lt;span style="color:#719e07">=&lt;/span> cachedClasses&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>classes &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classes &lt;span style="color:#719e07">=&lt;/span> loadExtensionClasses&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cachedClasses&lt;span style="color:#719e07">.&lt;/span>set&lt;span style="color:#719e07">(&lt;/span>classes&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> classes&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// synchronized in getExtensionClasses
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">private&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> loadExtensionClasses&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> SPI defaultAnnotation &lt;span style="color:#719e07">=&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getAnnotation&lt;span style="color:#719e07">(&lt;/span>SPI&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>defaultAnnotation &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String value &lt;span style="color:#719e07">=&lt;/span> defaultAnnotation&lt;span style="color:#719e07">.&lt;/span>value&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> value&lt;span style="color:#719e07">.&lt;/span>trim&lt;span style="color:#719e07">()).&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> names &lt;span style="color:#719e07">=&lt;/span> NAME_SEPARATOR&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>names&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;more than 1 default extension name on extension &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>names&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> cachedDefaultName &lt;span style="color:#719e07">=&lt;/span> names&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> extensionClasses &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadFile&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> DUBBO_INTERNAL_DIRECTORY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadFile&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> DUBBO_DIRECTORY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadFile&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> SERVICES_DIRECTORY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extensionClasses&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>过程很简单，先从缓存中获取，如果没有，就从配置文件中加载。配置文件的路径就是之前提到的:&lt;/p>
&lt;ul>
&lt;li>&lt;code>META-INF/dubbo/internal&lt;/code>&lt;/li>
&lt;li>&lt;code>META-INF/dubbo&lt;/code>&lt;/li>
&lt;li>&lt;code>META-INF/services&lt;/code>&lt;/li>
&lt;/ul>
&lt;ol start="2">
&lt;li>使用反射创建扩展实例
这个过程很简单，使用&lt;code>clazz.newInstance())&lt;/code>来完成。创建的扩展实例的属性都是空值。&lt;/li>
&lt;li>扩展实例自动装配
在实际的场景中，类之间都是有依赖的。扩展实例中也会引用一些依赖，比如简单的Java类，另一个Dubbo的扩展或一个Spring Bean等。依赖的情况很复杂，Dubbo的处理也相对复杂些。我们稍后会有专门的章节对其进行说明，现在，我们只需要知道，Dubbo可以正确的注入扩展点中的普通依赖，Dubbo扩展依赖或Spring依赖等。&lt;/li>
&lt;li>扩展实例自动包装
自动包装就是要实现类似于Spring的AOP功能。Dubbo利用它在内部实现一些通用的功能，比如日志，监控等。关于扩展实例自动包装的内容，也会在后面单独讲解。&lt;/li>
&lt;/ol>
&lt;p>经过上面的4步，Dubbo就创建并初始化了一个扩展实例。这个实例的依赖被注入了，也根据需要被包装了。到此为止，这个扩展实例就可以被使用了。&lt;/p>
&lt;h2 id="dubbo-spi高级用法之自动装配">Dubbo SPI高级用法之自动装配&lt;/h2>
&lt;p>自动装配的相关代码在injectExtension方法中:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> T &lt;span style="color:#268bd2">injectExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>T instance&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Method method &lt;span style="color:#719e07">:&lt;/span> instance&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">().&lt;/span>getMethods&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>method&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">().&lt;/span>startsWith&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;set&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> method&lt;span style="color:#719e07">.&lt;/span>getParameterTypes&lt;span style="color:#719e07">().&lt;/span>length &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> Modifier&lt;span style="color:#719e07">.&lt;/span>isPublic&lt;span style="color:#719e07">(&lt;/span>method&lt;span style="color:#719e07">.&lt;/span>getModifiers&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> pt &lt;span style="color:#719e07">=&lt;/span> method&lt;span style="color:#719e07">.&lt;/span>getParameterTypes&lt;span style="color:#719e07">()[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String property &lt;span style="color:#719e07">=&lt;/span> method&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">().&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">3&lt;/span> &lt;span style="color:#719e07">?&lt;/span> method&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">().&lt;/span>substring&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">4&lt;/span>&lt;span style="color:#719e07">).&lt;/span>toLowerCase&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> method&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">().&lt;/span>substring&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">4&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object object &lt;span style="color:#719e07">=&lt;/span> objectFactory&lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>pt&lt;span style="color:#719e07">,&lt;/span> property&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>object &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> method&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">,&lt;/span> object&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>要实现对扩展实例的依赖的自动装配，首先需要知道有哪些依赖，这些依赖的类型是什么。Dubbo的方案是查找Java标准的setter方法。即方法名以set开始，只有一个参数。如果扩展类中有这样的set方法，Dubbo会对其进行依赖注入，类似于Spring的set方法注入。
但是Dubbo中的依赖注入比Spring要复杂，因为Spring注入的都是Spring bean，都是由Spring容器来管理的。而Dubbo的依赖注入中，需要注入的可能是另一个Dubbo的扩展，也可能是一个Spring Bean，或是Google guice的组件，或其他任何一个框架中的组件。Dubbo需要能够从任何一个场景中加载扩展。在injectExtension方法中，是用&lt;code>Object object = objectFactory.getExtension(pt, property)&lt;/code>来实现的。objectFactory是ExtensionFactory类型的，在创建ExtensionLoader时被初始化:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">ExtensionLoader&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> type&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>type &lt;span style="color:#719e07">=&lt;/span> type&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> objectFactory &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>type &lt;span style="color:#719e07">==&lt;/span> ExtensionFactory&lt;span style="color:#719e07">.&lt;/span>class &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">:&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ExtensionFactory&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>objectFacory本身也是一个扩展，通过&lt;code>ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())&lt;/code>来获取。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-extensionfactory.png" alt="Dubbo-ExtensionFactory" title="">&lt;/p>
&lt;p>ExtensionFactory 有三个实现：&lt;/p>
&lt;ol>
&lt;li>SpiExtensionFactory：Dubbo自己的Spi去加载Extension&lt;/li>
&lt;li>SpringExtensionFactory：从Spring容器中去加载Extension&lt;/li>
&lt;li>AdaptiveExtensionFactory: 自适应的AdaptiveExtensionLoader&lt;/li>
&lt;/ol>
&lt;p>这里要注意 AdaptiveExtensionFactory，源码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Adaptive&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">AdaptiveExtensionFactory&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> ExtensionFactory &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&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> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ExtensionFactory&lt;span style="color:#719e07">&amp;gt;&lt;/span> factories&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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">AdaptiveExtensionFactory&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ExtensionFactory&lt;span style="color:#719e07">&amp;gt;&lt;/span> loader &lt;span style="color:#719e07">=&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ExtensionFactory&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ExtensionFactory&lt;span style="color:#719e07">&amp;gt;&lt;/span> list &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&lt;/span>ExtensionFactory&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">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String name &lt;span style="color:#719e07">:&lt;/span> loader&lt;span style="color:#719e07">.&lt;/span>getSupportedExtensions&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> list&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>loader&lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> factories &lt;span style="color:#719e07">=&lt;/span> Collections&lt;span style="color:#719e07">.&lt;/span>unmodifiableList&lt;span style="color:#719e07">(&lt;/span>list&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T &lt;span style="color:#268bd2">getExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ExtensionFactory factory &lt;span style="color:#719e07">:&lt;/span> factories&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> T extension &lt;span style="color:#719e07">=&lt;/span> factory&lt;span style="color:#719e07">.&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>extension &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extension&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>AdaptiveExtensionLoader类有@Adaptive注解。前面提到了，Dubbo会为每一个扩展创建一个自适应实例。如果扩展类上有@Adaptive，会使用该类作为自适应类。如果没有，Dubbo会为我们创建一个。所以&lt;code>ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())&lt;/code>会返回一个AdaptiveExtensionLoader实例，作为自适应扩展实例。
AdaptiveExtensionLoader会遍历所有的ExtensionFactory实现，尝试着去加载扩展。如果找到了，返回。如果没有，在下一个ExtensionFactory中继续找。Dubbo内置了两个ExtensionFactory，分别从Dubbo自身的扩展机制和Spring容器中去寻找。由于ExtensionFactory本身也是一个扩展点，我们可以实现自己的ExtensionFactory，让Dubbo的自动装配支持我们自定义的组件。比如，我们在项目中使用了Google的guice这个 IOC 容器。我们可以实现自己的GuiceExtensionFactory，让Dubbo支持从guice容器中加载扩展。&lt;/p>
&lt;h2 id="dubbo-spi高级用法之-aop">Dubbo SPI高级用法之 AOP&lt;/h2>
&lt;p>在用Spring的时候，我们经常会用到AOP功能。在目标类的方法前后插入其他逻辑。比如通常使用Spring AOP来实现日志，监控和鉴权等功能。
Dubbo的扩展机制，是否也支持类似的功能呢？答案是yes。在Dubbo中，有一种特殊的类，被称为Wrapper类。通过装饰者模式，使用包装类包装原始的扩展点实例。在原始扩展点实现前后插入其他逻辑，实现AOP功能。&lt;/p>
&lt;h3 id="什么是wrapper类">什么是Wrapper类&lt;/h3>
&lt;p>那什么样类的才是Dubbo扩展机制中的Wrapper类呢？Wrapper类是一个有复制构造函数的类，也是典型的装饰者模式。下面就是一个Wrapper类:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">A&lt;/span>&lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> A a&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">A&lt;/span>&lt;span style="color:#719e07">(&lt;/span>A a&lt;span style="color:#719e07">){&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>a &lt;span style="color:#719e07">=&lt;/span> a&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>类A有一个构造函数&lt;code>public A(A a)&lt;/code>，构造函数的参数是A本身。这样的类就可以成为Dubbo扩展机制中的一个Wrapper类。Dubbo中这样的Wrapper类有ProtocolFilterWrapper, ProtocolListenerWrapper等, 大家可以查看源码加深理解。&lt;/p>
&lt;h3 id="怎么配置wrapper类">怎么配置Wrapper类&lt;/h3>
&lt;p>在Dubbo中Wrapper类也是一个扩展点，和其他的扩展点一样，也是在&lt;code>META-INF&lt;/code>文件夹中配置的。比如前面举例的ProtocolFilterWrapper和ProtocolListenerWrapper就是在路径&lt;code>dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol&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-text" data-lang="text">&lt;span style="display:flex;">&lt;span>filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mock=org.apache.dubbo.rpc.support.MockProtocol
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&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:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clazz&lt;span style="color:#719e07">.&lt;/span>getConstructor&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> wrappers &lt;span style="color:#719e07">=&lt;/span> cachedWrapperClasses&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>wrappers &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cachedWrapperClasses &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashSet&lt;span style="color:#719e07">&amp;lt;&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> wrappers &lt;span style="color:#719e07">=&lt;/span> cachedWrapperClasses&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> wrappers&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>clazz&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>NoSuchMethodException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这段代码的意思是，如果扩展类有复制构造函数，就把该类存起来，供以后使用。有复制构造函数的类就是Wrapper类。通过&lt;code>clazz.getConstructor(type)&lt;/code>来获取参数是扩展点接口的构造函数。注意构造函数的参数类型是扩展点接口，而不是扩展类。
以Protocol为例。配置文件&lt;code>dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol&lt;/code>中定义了&lt;code>filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper&lt;/code>。
ProtocolFilterWrapper代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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">ProtocolFilterWrapper&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Protocol &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&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> Protocol protocol&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 有一个参数是Protocol的复制构造函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ProtocolFilterWrapper&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Protocol protocol&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>protocol &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;protocol == null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>protocol &lt;span style="color:#719e07">=&lt;/span> protocol&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ProtocolFilterWrapper有一个构造函数&lt;code>public ProtocolFilterWrapper(Protocol protocol)&lt;/code>，参数是扩展点Protocol，所以它是一个Dubbo扩展机制中的Wrapper类。ExtensionLoader会把它缓存起来，供以后创建Extension实例的时候，使用这些包装类依次包装原始扩展点。&lt;/p>
&lt;h2 id="扩展点自适应">扩展点自适应&lt;/h2>
&lt;p>前面讲到过，Dubbo需要在运行时根据方法参数来决定该使用哪个扩展，所以有了扩展点自适应实例。其实是一个扩展点的代理，将扩展的选择从Dubbo启动时，延迟到RPC调用时。Dubbo中每一个扩展点都有一个自适应类，如果没有显式提供，Dubbo会自动为我们创建一个，默认使用Javaassist。
先来看下创建自适应扩展类的代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">getAdaptiveExtension&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object instance &lt;span style="color:#719e07">=&lt;/span> cachedAdaptiveInstance&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedAdaptiveInstance&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> cachedAdaptiveInstance&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>instance &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> instance &lt;span style="color:#719e07">=&lt;/span> createAdaptiveExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cachedAdaptiveInstance&lt;span style="color:#719e07">.&lt;/span>set&lt;span style="color:#719e07">(&lt;/span>instance&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> instance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续看createAdaptiveExtension方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> T &lt;span style="color:#268bd2">createAdaptiveExtension&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> injectExtension&lt;span style="color:#719e07">((&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> getAdaptiveExtensionClass&lt;span style="color:#719e07">().&lt;/span>newInstance&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续看getAdaptiveExtensionClass方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> getAdaptiveExtensionClass&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> getExtensionClasses&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>cachedAdaptiveClass &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> cachedAdaptiveClass&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> cachedAdaptiveClass &lt;span style="color:#719e07">=&lt;/span> createAdaptiveExtensionClass&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>继续看createAdaptiveExtensionClass方法，绕了一大圈，终于来到了具体的实现了。看这个createAdaptiveExtensionClass方法，它首先会生成自适应类的Java源码，然后再将源码编译成Java的字节码，加载到JVM中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> createAdaptiveExtensionClass&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String code &lt;span style="color:#719e07">=&lt;/span> createAdaptiveExtensionClassCode&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassLoader classLoader &lt;span style="color:#719e07">=&lt;/span> findClassLoader&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>compiler&lt;span style="color:#719e07">.&lt;/span>Compiler compiler &lt;span style="color:#719e07">=&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>compiler&lt;span style="color:#719e07">.&lt;/span>Compiler&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getAdaptiveExtension&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> compiler&lt;span style="color:#719e07">.&lt;/span>compile&lt;span style="color:#719e07">(&lt;/span>code&lt;span style="color:#719e07">,&lt;/span> classLoader&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Compiler的代码，默认实现是javassist。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SPI&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;javassist&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">Compiler&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> compile&lt;span style="color:#719e07">(&lt;/span>String code&lt;span style="color:#719e07">,&lt;/span> ClassLoader classLoader&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>createAdaptiveExtensionClassCode()方法中使用一个StringBuilder来构建自适应类的Java源码。方法实现比较长，这里就不贴代码了。这种生成字节码的方式也挺有意思的，先生成Java源代码，然后编译，加载到jvm中。通过这种方式，可以更好的控制生成的Java类。而且这样也不用care各个字节码生成框架的api等。因为xxx.java文件是Java通用的，也是我们最熟悉的。只是代码的可读性不强，需要一点一点构建xx.java的内容。
下面是使用createAdaptiveExtensionClassCode方法为Protocol创建的自适应类的Java代码范例:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org.apache.dubbo.rpc&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.common.extension.ExtensionLoader&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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">Protocol$Adaptive&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Protocol &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">destroy&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> UnsupportedOperationException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> &lt;span style="color:#268bd2">getDefaultPort&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> UnsupportedOperationException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Exporter &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Invoker arg0&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>arg0 &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.rpc.Invoker argument == null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>arg0&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.rpc.Invoker argument getUrl() == null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>URL url &lt;span style="color:#719e07">=&lt;/span> arg0&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String extName &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>extName &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) use keys([protocol])&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Protocol extension &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Protocol&lt;span style="color:#719e07">)&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Protocol&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>extName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extension&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">(&lt;/span>arg0&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Invoker &lt;span style="color:#268bd2">refer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>java&lt;span style="color:#719e07">.&lt;/span>lang&lt;span style="color:#719e07">.&lt;/span>Class arg0&lt;span style="color:#719e07">,&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>URL arg1&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>arg1 &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;url == null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>common&lt;span style="color:#719e07">.&lt;/span>URL url &lt;span style="color:#719e07">=&lt;/span> arg1&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String extName &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>extName &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) use keys([protocol])&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Protocol extension &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Protocol&lt;span style="color:#719e07">)&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Protocol&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>extName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extension&lt;span style="color:#719e07">.&lt;/span>refer&lt;span style="color:#719e07">(&lt;/span>arg0&lt;span style="color:#719e07">,&lt;/span> arg1&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>大致的逻辑和开始说的一样，通过url解析出参数，解析的逻辑由@Adaptive的value参数控制，然后再根据得到的扩展点名获取扩展点实现，然后进行调用。如果大家想知道具体的构建.java代码的逻辑，可以看&lt;code>createAdaptiveExtensionClassCode&lt;/code>的完整实现。
在生成的 Protocol$Adaptive 中，发现getDefaultPort和destroy方法都是直接抛出异常的，这是为什么呢？来看看Protocol的源码&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SPI&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">Protocol&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> &lt;span style="color:#268bd2">getDefaultPort&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Adaptive&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Exporter&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Adaptive&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">refer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">destroy&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到Protocol接口中有4个方法，但只有export和refer两个方法使用了@Adaptive注解。Dubbo自动生成的自适应实例，只有@Adaptive修饰的方法才有具体的实现。所以，Protocol$Adaptive 类中，也只有export和refer这两个方法有具体的实现，其余方法都是抛出异常。&lt;/p></description></item><item><title>Blog: Dubbo 一致性Hash负载均衡实现剖析</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/consistenthash/consistent-hash-init-model.jpg" alt="Init">&lt;/p>
&lt;h4 id="b映射请求定位服务">&lt;strong>b、映射请求、定位服务&lt;/strong>&lt;/h4>
&lt;p>在发起请求时，我们往往会带上参数，而这些参数，就可以被我们用来确定具体调用哪一个服务。假设有请求R1、R2、R3，对它们的参数也经过计算特定识别码、取余的一系列运算之后，有如下映射关系：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/consistenthash/consistent-hash-request-model.jpg" alt="Request">&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 src="https://cn.dubbo.apache.org/imgs/blog/consistenthash/consistent-hash-new-node-model.jpg" alt="New Node">&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 src="https://cn.dubbo.apache.org/imgs/blog/consistenthash/consistent-hash-delete-node-model.jpg" alt="Delete Node">&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 src="https://cn.dubbo.apache.org/imgs/blog/consistenthash/consistent-hash-balance-model.jpg" alt="Balance">&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 src="https://cn.dubbo.apache.org/imgs/blog/consistenthash/consistent-hash-virtual-node-model.jpg" alt="Virtual Node">&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/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>&lt;span style="color:#719e07">(&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&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>invokers&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>LoadBalance&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>invokers&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">).&lt;/span>getUrl&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getMethodParameter&lt;span style="color:#719e07">(&lt;/span>RpcUtils&lt;span style="color:#719e07">.&lt;/span>getMethodName&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">),&lt;/span> LOADBALANCE_KEY&lt;span style="color:#719e07">,&lt;/span> DEFAULT_LOADBALANCE&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>LoadBalance&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>DEFAULT_LOADBALANCE&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这部分代码逻辑分为两部分：&lt;/p>
&lt;p>1、获取调用方法所配置的LOADBALANCE_KEY属性的值，LOADBALANCE_KEY这个常量的实际值为：loadbalance，即为我们的所配置的属性；&lt;/p>
&lt;p>2、利用SPI机制来初始化并加载该值所代表的负载均衡策略。&lt;/p>
&lt;p>所有的负载均衡策略都会继承LoadBalance接口。在各种集群策略中，最终都会调用AbstractClusterInvoker的select方法，而AbstractClusterInvoker会在doSelect中，&lt;strong>调用LoadBalance的select方法，这里即开始了负载均衡策略的执行。&lt;/strong>&lt;/p>
&lt;h3 id="三dubbo一致性hash负载均衡的实现">三、Dubbo一致性Hash负载均衡的实现&lt;/h3>
&lt;p>需要说明的一点是，我所说的&lt;strong>负载均衡策略的执行&lt;/strong>，即是在所有的Provider中选出一个，作为当前Consumer的远程调用对象。在代码中，Provider被封装成了Invoker实体，所以直接说来，负载均衡策略的执行就是在Invoker列表中选出一个Invoker。&lt;/p>
&lt;p>所以，对比普通一致性Hash的实现，Dubbo的一致性Hash算法也可以分为两步：&lt;/p>
&lt;p>&lt;strong>1、映射Provider至Hash值区间中（实际中映射的是Invoker）；&lt;/strong>&lt;/p>
&lt;p>&lt;strong>2、映射请求，然后找到大于请求Hash值的第一个Invoker。&lt;/strong>&lt;/p>
&lt;h4 id="a映射invoker">&lt;strong>a、映射Invoker&lt;/strong>&lt;/h4>
&lt;p>Dubbo中所有的负载均衡实现类都继承了AbstractLoadBalance，调用LoadBalance的select方法时，实际上调用的是AbstractLoadBalance的实现：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">select&lt;/span>&lt;span style="color:#719e07">(&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&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>invokers&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>invokers&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invokers&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// doSelect这里进入具体负载均衡算法的执行逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> doSelect&lt;span style="color:#719e07">(&lt;/span>invokers&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到这里调用了doSelect，Dubbo一致性Hash的具体实现类名字是&lt;strong>ConsistentHashLoadBalance&lt;/strong>，让我们来看看它的doSelect方法干了啥：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#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">doSelect&lt;/span>&lt;span style="color:#719e07">(&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&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String methodName &lt;span style="color:#719e07">=&lt;/span> RpcUtils&lt;span style="color:#719e07">.&lt;/span>getMethodName&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// key格式：接口名.方法名
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> invokers&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">).&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getServiceKey&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;.&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> methodName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// identityHashCode 用来识别invokers是否发生过变更
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> identityHashCode &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>identityHashCode&lt;span style="color:#719e07">(&lt;/span>invokers&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConsistentHashSelector&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> selector &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConsistentHashSelector&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> selectors&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>selector &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> selector&lt;span style="color:#719e07">.&lt;/span>identityHashCode &lt;span style="color:#719e07">!=&lt;/span> identityHashCode&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 若不存在&amp;#34;接口.方法名&amp;#34;对应的选择器，或是Invoker列表已经发生了变更，则初始化一个选择器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> selectors&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConsistentHashSelector&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>invokers&lt;span style="color:#719e07">,&lt;/span> methodName&lt;span style="color:#719e07">,&lt;/span> identityHashCode&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConsistentHashSelector&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> selectors&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> selector&lt;span style="color:#719e07">.&lt;/span>select&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里有个很重要的概念：&lt;strong>选择器——selector&lt;/strong>。这是Dubbo一致性Hash实现中，承载着整个映射关系的数据结构。它里面主要有这么几个参数：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-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"> * 存储Hash值与节点映射关系的TreeMap
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> TreeMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>Long&lt;span style="color:#719e07">,&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> virtualInvokers&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> replicaNumber&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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"> * 用来识别Invoker列表是否发生变更的Hash码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> identityHashCode&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/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"> * 请求中用来作Hash映射的参数的索引
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">int&lt;/span>&lt;span style="color:#719e07">[]&lt;/span> argumentIndex&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在新建ConsistentHashSelector对象的时候，就会遍历所有Invoker对象，然后计算出其地址（ip+port）对应的md5码，并按照配置的节点数目replicaNumber的值来初始化服务节点和所有虚拟节点：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ConsistentHashSelector&lt;span style="color:#719e07">(&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&lt;span style="color:#719e07">,&lt;/span> String methodName&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> identityHashCode&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>virtualInvokers &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> TreeMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>Long&lt;span style="color:#719e07">,&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>identityHashCode &lt;span style="color:#719e07">=&lt;/span> identityHashCode&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL url &lt;span style="color:#719e07">=&lt;/span> invokers&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">).&lt;/span>getUrl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取配置的节点数目
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>replicaNumber &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getMethodParameter&lt;span style="color:#719e07">(&lt;/span>methodName&lt;span style="color:#719e07">,&lt;/span> HASH_NODES&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">160&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取配置的用作Hash映射的参数的索引
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> index &lt;span style="color:#719e07">=&lt;/span> COMMA_SPLIT_PATTERN&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getMethodParameter&lt;span style="color:#719e07">(&lt;/span>methodName&lt;span style="color:#719e07">,&lt;/span> HASH_ARGUMENTS&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;0&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> argumentIndex &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> &lt;span style="color:#dc322f">int&lt;/span>&lt;span style="color:#719e07">[&lt;/span>index&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> index&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> argumentIndex&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> Integer&lt;span style="color:#719e07">.&lt;/span>parseInt&lt;span style="color:#719e07">(&lt;/span>index&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 遍历所有Invoker对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker &lt;span style="color:#719e07">:&lt;/span> invokers&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取Provider的ip+port
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String address &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getAddress&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> replicaNumber &lt;span style="color:#719e07">/&lt;/span> &lt;span style="color:#2aa198">4&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">byte&lt;/span>&lt;span style="color:#719e07">[]&lt;/span> digest &lt;span style="color:#719e07">=&lt;/span> md5&lt;span style="color:#719e07">(&lt;/span>address &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">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> h &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> h &lt;span style="color:#719e07">&amp;lt;&lt;/span> &lt;span style="color:#2aa198">4&lt;/span>&lt;span style="color:#719e07">;&lt;/span> h&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">long&lt;/span> m &lt;span style="color:#719e07">=&lt;/span> hash&lt;span style="color:#719e07">(&lt;/span>digest&lt;span style="color:#719e07">,&lt;/span> h&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> virtualInvokers&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>m&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里值得注意的是：以replicaNumber取默认值160为例，假设当前遍历到的Invoker地址为127.0.0.1:20880，它会依次获得“127.0.0.1:208800”、“127.0.0.1:208801”、&amp;hellip;&amp;hellip;、“127.0.0.1:2088040”的md5摘要，在每次获得摘要之后，还会对该摘要进行四次数位级别的散列。大致可以猜到其目的应该是为了加强散列效果。（希望有人能告诉我相关的理论依据。）&lt;/p>
&lt;p>代码中 &lt;strong>virtualInvokers.put(m, invoker)&lt;/strong> 即是存储当前计算出的Hash值与Invoker的映射关系。&lt;/p>
&lt;p>这段代码简单说来，就是为每个Invoker都创建replicaNumber个节点，Hash值与Invoker的映射关系即象征着一个节点，这个关系存储在TreeMap中。&lt;/p>
&lt;h4 id="b映射请求">&lt;strong>b、映射请求&lt;/strong>&lt;/h4>
&lt;p>让我们重新回到ConsistentHashLoadBalance的&lt;strong>doSelect&lt;/strong>方法，若没有找到selector则会新建selector，找到selector后便会调用selector的select方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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> 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">select&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 根据invocation的【参数值】来确定key，默认使用第一个参数来做hash计算
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> toKey&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">.&lt;/span>getArguments&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取【参数值】的md5编码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">byte&lt;/span>&lt;span style="color:#719e07">[]&lt;/span> digest &lt;span style="color:#719e07">=&lt;/span> md5&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> selectForKey&lt;span style="color:#719e07">(&lt;/span>hash&lt;span style="color:#719e07">(&lt;/span>digest&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 根据参数索引获取参数，并将所有参数拼接成字符串
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">private&lt;/span> String &lt;span style="color:#268bd2">toKey&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Object&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> StringBuilder buf &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> StringBuilder&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">:&lt;/span> argumentIndex&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>i &lt;span style="color:#719e07">&amp;gt;=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> args&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> buf&lt;span style="color:#719e07">.&lt;/span>append&lt;span style="color:#719e07">(&lt;/span>args&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> buf&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 根据参数字符串的md5编码找出Invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">private&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">selectForKey&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">long&lt;/span> hash&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>Long&lt;span style="color:#719e07">,&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">=&lt;/span> virtualInvokers&lt;span style="color:#719e07">.&lt;/span>ceilingEntry&lt;span style="color:#719e07">(&lt;/span>hash&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>entry &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> entry &lt;span style="color:#719e07">=&lt;/span> virtualInvokers&lt;span style="color:#719e07">.&lt;/span>firstEntry&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>argumentIndex是在初始化Selector的时候一起赋值的，代表着需要用哪几个请求参数作Hash映射获取Invoker。比如：有方法methodA(Integer a, Integer b, Integer c)，如果argumentIndex的值为{0,2}，那么即用a和c拼接的字符串来计算Hash值。&lt;/p>
&lt;p>我们已经知道virtualInvokers是一个TreeMap，TreeMap的底层实现是红黑树。对于TreeMap的方法ceilingEntry(hash)，它的作用是用来&lt;strong>获取大于等于传入值的首个元素&lt;/strong>。可以看到，这一点与一般的一致性Hash算法的处理逻辑完全是相同的。&lt;/p>
&lt;p>但这里的回环逻辑有点不同。对于取模运算来讲，大于最大值后，会自动回环从0开始，而这里的逻辑是：当没有比传入ceilingEntry()方法中的值大的元素的时候，virtualInvokers.ceilingEntry(hash)必然会得到null，于是，就用virtualInvokers.firstEntry()来获取整个TreeMap的第一个元素。&lt;/p>
&lt;p>从selectForKey中获取到Invoker后，负载均衡策略也就算是执行完毕了。后续获取远程调用客户端等调用流程不再赘述。&lt;/p></description></item><item><title>Blog: Dubbo可扩展机制实战</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">{&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>&lt;span style="color:#719e07">(&lt;/span>String data&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>提供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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">save&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String data&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;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 style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MongoRepository&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> IRepository &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">save&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String data&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;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 style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="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&lt;span style="color:#719e07">.&lt;/span>load&lt;span style="color:#719e07">(&lt;/span>IRepository&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>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&lt;span style="color:#719e07">.&lt;/span>iterator&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">(&lt;/span>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&lt;span style="color:#719e07">.&lt;/span>hasNext&lt;span style="color:#719e07">()){&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> IRepository demoService &lt;span style="color:#719e07">=&lt;/span> it&lt;span style="color:#719e07">.&lt;/span>next&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;class:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> demoService&lt;span style="color:#719e07">.&lt;/span>getClass&lt;span style="color:#719e07">().&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> demoService&lt;span style="color:#719e07">.&lt;/span>save&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;tom&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/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>
&lt;h2 id="4-dubbo的spi机制">4. Dubbo的SPI机制&lt;/h2>
&lt;p>Java SPI的使用很简单。也做到了基本的加载扩展点的功能。但Java SPI有以下的不足:&lt;/p>
&lt;ul>
&lt;li>需要遍历所有的实现，并实例化，然后我们在循环中才能找到我们需要的实现。&lt;/li>
&lt;li>配置文件中只是简单的列出了所有的扩展实现，而没有给他们命名。导致在程序中很难去准确的引用它们。&lt;/li>
&lt;li>扩展如果依赖其他的扩展，做不到自动注入和装配&lt;/li>
&lt;li>不提供类似于Spring的IOC和AOP功能&lt;/li>
&lt;li>扩展很难和其他的框架集成，比如扩展里面依赖了一个Spring bean，原生的Java SPI不支持&lt;/li>
&lt;/ul>
&lt;p>所以Java SPI应付一些简单的场景是可以的，但对于Dubbo，它的功能还是比较弱的。Dubbo对原生SPI机制进行了一些扩展。接下来，我们就更深入地了解下Dubbo的SPI机制。&lt;/p>
&lt;h2 id="5-dubbo扩展点机制基本概念">5. Dubbo扩展点机制基本概念&lt;/h2>
&lt;p>在深入学习Dubbo的扩展机制之前，我们先明确Dubbo SPI中的一些基本概念。在接下来的内容中，我们会多次用到这些术语。&lt;/p>
&lt;h3 id="51-扩展点extension-point">5.1 扩展点(Extension Point)&lt;/h3>
&lt;p>是一个Java的接口。&lt;/p>
&lt;h3 id="52-扩展extension">5.2 扩展(Extension)&lt;/h3>
&lt;p>扩展点的实现类。&lt;/p>
&lt;h3 id="53-扩展实例extension-instance">5.3 扩展实例(Extension Instance)&lt;/h3>
&lt;p>扩展点实现类的实例。&lt;/p>
&lt;h3 id="54-扩展自适应实例extension-adaptive-instance">5.4 扩展自适应实例(Extension Adaptive Instance)&lt;/h3>
&lt;p>第一次接触这个概念时，可能不太好理解(我第一次也是这样的&amp;hellip;)。如果称它为扩展代理类，可能更好理解些。扩展的自适应实例其实就是一个Extension的代理，它实现了扩展点接口。在调用扩展点的接口方法时，会根据实际的参数来决定要使用哪个扩展。比如一个IRepository的扩展点，有一个save方法。有两个实现MysqlRepository和MongoRepository。IRepository的自适应实例在调用接口方法的时候，会根据save方法中的参数，来决定要调用哪个IRepository的实现。如果方法参数中有repository=mysql，那么就调用MysqlRepository的save方法。如果repository=mongo，就调用MongoRepository的save方法。和面向对象的延迟绑定很类似。为什么Dubbo会引入扩展自适应实例的概念呢？&lt;/p>
&lt;ul>
&lt;li>Dubbo中的配置有两种，一种是固定的系统级别的配置，在Dubbo启动之后就不会再改了。还有一种是运行时的配置，可能对于每一次的RPC，这些配置都不同。比如在xml文件中配置了超时时间是10秒钟，这个配置在Dubbo启动之后，就不会改变了。但针对某一次的RPC调用，可以设置它的超时时间是30秒钟，以覆盖系统级别的配置。对于Dubbo而言，每一次的RPC调用的参数都是未知的。只有在运行时，根据这些参数才能做出正确的决定。&lt;/li>
&lt;li>很多时候，我们的类都是一个单例的，比如Spring的bean，在Spring bean都实例化时，如果它依赖某个扩展点，但是在bean实例化时，是不知道究竟该使用哪个具体的扩展实现的。这时候就需要一个代理模式了，它实现了扩展点接口，方法内部可以根据运行时参数，动态的选择合适的扩展实现。而这个代理就是自适应实例。
自适应扩展实例在Dubbo中的使用非常广泛，Dubbo中，每一个扩展都会有一个自适应类，如果我们没有提供，Dubbo会使用字节码工具为我们自动生成一个。所以我们基本感觉不到自适应类的存在。后面会有例子说明自适应类是怎么工作的。&lt;/li>
&lt;/ul>
&lt;h3 id="55-spi">5.5 @SPI&lt;/h3>
&lt;p>@SPI注解作用于扩展点的接口上，表明该接口是一个扩展点。可以被Dubbo的ExtensionLoader加载。如果没有此ExtensionLoader调用会异常。&lt;/p>
&lt;h3 id="56-adaptive">5.6 @Adaptive&lt;/h3>
&lt;p>@Adaptive注解用在扩展接口的方法上。表示该方法是一个自适应方法。Dubbo在为扩展点生成自适应实例时，如果方法有@Adaptive注解，会为该方法生成对应的代码。方法内部会根据方法的参数，来决定使用哪个扩展。
@Adaptive注解用在类上代表实现一个装饰类，类似于设计模式中的装饰模式，它主要作用是返回指定类，目前在整个系统中AdaptiveCompiler、AdaptiveExtensionFactory这两个类拥有该注解。&lt;/p>
&lt;h3 id="57-extensionloader">5.7 ExtensionLoader&lt;/h3>
&lt;p>类似于Java SPI的ServiceLoader，负责扩展的加载和生命周期维护。&lt;/p>
&lt;h3 id="58-扩展别名">5.8 扩展别名&lt;/h3>
&lt;p>和Java SPI不同，Dubbo中的扩展都有一个别名，用于在应用中引用它们。比如&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">random&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">roundrobin&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中的random，roundrobin就是对应扩展的别名。这样我们在配置文件中使用random或roundrobin就可以了。&lt;/p>
&lt;h3 id="59-一些路径">5.9 一些路径&lt;/h3>
&lt;p>和Java SPI从&lt;code>/META-INF/services&lt;/code>目录加载扩展配置类似，Dubbo也会从以下路径去加载扩展配置文件:&lt;/p>
&lt;ul>
&lt;li>&lt;code>META-INF/dubbo/internal&lt;/code>&lt;/li>
&lt;li>&lt;code>META-INF/dubbo&lt;/code>&lt;/li>
&lt;li>&lt;code>META-INF/services&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="6-dubbo的loadbalance扩展点解读">6. Dubbo的LoadBalance扩展点解读&lt;/h2>
&lt;p>在了解了Dubbo的一些基本概念后，让我们一起来看一个Dubbo中实际的扩展点，对这些概念有一个更直观的认识。&lt;/p>
&lt;p>我们选择的是Dubbo中的LoadBalance扩展点。Dubbo中的一个服务，通常有多个Provider，consumer调用服务时，需要在多个Provider中选择一个。这就是一个LoadBalance。我们一起来看看在Dubbo中，LoadBalance是如何成为一个扩展点的。&lt;/p>
&lt;h3 id="61-loadbalance接口">6.1 LoadBalance接口&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">@SPI&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RandomLoadBalance&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">LoadBalance&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Adaptive&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;loadbalance&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&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">select&lt;/span>&lt;span style="color:#719e07">(&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&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>LoadBalance接口只有一个select方法。select方法从多个invoker中选择其中一个。上面代码中和Dubbo SPI相关的元素有:&lt;/p>
&lt;ul>
&lt;li>@SPI(RandomLoadBalance.NAME)
@SPI作用于LoadBalance接口，表示接口LoadBalance是一个扩展点。如果没有@SPI注解，试图去加载扩展时，会抛出异常。@SPI注解有一个参数，该参数表示该扩展点的默认实现的别名。如果没有显示的指定扩展，就使用默认实现。&lt;code>RandomLoadBalance.NAME&lt;/code>是一个常量，值是&amp;quot;random&amp;quot;，是一个随机负载均衡的实现。
random的定义在配置文件&lt;code>META-INF/dubbo/internal/com.alibaba.dubbo.rpc.cluster.LoadBalance&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">random&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">roundrobin&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">leastactive&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">consistenthash&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到文件中定义了4个LoadBalance的扩展实现。由于负载均衡的实现不是本次的内容，这里就不过多说明。只用知道Dubbo提供了4种负载均衡的实现，我们可以通过xml文件，properties文件，JVM参数显式的指定一个实现。如果没有，默认使用随机。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo_loadbalance.png" alt="">&lt;/p>
&lt;ul>
&lt;li>@Adaptive(&amp;ldquo;loadbalance&amp;rdquo;)
@Adaptive注解修饰select方法，表明方法select方法是一个可自适应的方法。Dubbo会自动生成该方法对应的代码。当调用select方法时，会根据具体的方法参数来决定调用哪个扩展实现的select方法。@Adaptive注解的参数&lt;code>loadbalance&lt;/code>表示方法参数中的loadbalance的值作为实际要调用的扩展实例。
但奇怪的是，我们发现select的方法中并没有loadbalance参数，那怎么获取loadbalance的值呢？select方法中还有一个URL类型的参数，Dubbo就是从URL中获取loadbalance的值的。这里涉及到Dubbo的URL总线模式，简单说，URL中包含了RPC调用中的所有参数。URL类中有一个&lt;code>Map&amp;lt;String, String&amp;gt; parameters&lt;/code>字段，parameters中就包含了loadbalance。&lt;/li>
&lt;/ul>
&lt;h3 id="62-获取loadbalance扩展">6.2 获取LoadBalance扩展&lt;/h3>
&lt;p>Dubbo中获取LoadBalance的代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>LoadBalance lb &lt;span style="color:#719e07">=&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>LoadBalance&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>loadbalanceName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>使用ExtensionLoader.getExtensionLoader(LoadBalance.class)方法获取一个ExtensionLoader的实例，然后调用getExtension，传入一个扩展的别名来获取对应的扩展实例。&lt;/p>
&lt;h2 id="7-自定义一个loadbalance扩展">7. 自定义一个LoadBalance扩展&lt;/h2>
&lt;p>本节中，我们通过一个简单的例子，来自己实现一个LoadBalance，并把它集成到Dubbo中。我会列出一些关键的步骤和代码，也可以从这个地址(&lt;a href="https://github.com/vangoleo/dubbo-spi-demo">https://github.com/vangoleo/dubbo-spi-demo&lt;/a>)下载完整的demo。&lt;/p>
&lt;h3 id="71-实现loadbalance接口">7.1 实现LoadBalance接口&lt;/h3>
&lt;p>首先，编写一个自己实现的LoadBalance，因为是为了演示Dubbo的扩展机制，而不是LoadBalance的实现，所以这里LoadBalance的实现非常简单，选择第一个invoker，并在控制台输出一条日志。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.dubbo.spi.demo.consumer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoLoadBalance&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> LoadBalance &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#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">select&lt;/span>&lt;span style="color:#719e07">(&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&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;DemoLoadBalance: Select the first invoker...&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invokers&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="72-添加扩展配置文件">7.2 添加扩展配置文件&lt;/h3>
&lt;p>添加文件:&lt;code>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance&lt;/code>。文件内容如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">demo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.dubbo.spi.demo.consumer.DemoLoadBalance
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="73-配置使用自定义loadbalance">7.3 配置使用自定义LoadBalance&lt;/h3>
&lt;p>通过上面的两步，已经添加了一个名字为demo的LoadBalance实现，并在配置文件中进行了相应的配置。接下来，需要显式的告诉Dubbo使用demo的负载均衡实现。如果是通过spring的方式使用Dubbo，可以在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:reference&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;helloService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.dubbo.spi.demo.api.IHelloService&amp;#34;&lt;/span> loadbalance=&lt;span style="color:#2aa198">&amp;#34;demo&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在consumer端的&lt;a href="dubbo:reference">dubbo:reference&lt;/a>中配置&amp;lt;loadbalance=&amp;ldquo;demo&amp;rdquo;&amp;gt;&lt;/p>
&lt;h3 id="74-启动dubbo">7.4 启动Dubbo&lt;/h3>
&lt;p>启动Dubbo，调用一次IHelloService，可以看到控制台会输出一条&lt;code>DemoLoadBalance: Select the first invoker...&lt;/code>日志。说明Dubbo的确是使用了我们自定义的LoadBalance。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>到此，我们从Java SPI开始，了解了Dubbo SPI 的基本概念，并结合了Dubbo中的LoadBalance加深了理解。最后，我们还实践了一下，创建了一个自定义LoadBalance，并集成到Dubbo中。相信通过这里理论和实践的结合，大家对Dubbo的可扩展有更深入的理解。
总结一下，Dubbo SPI有以下的特点:&lt;/p>
&lt;ul>
&lt;li>对Dubbo进行扩展，不需要改动Dubbo的源码&lt;/li>
&lt;li>自定义的Dubbo的扩展点实现，是一个普通的Java类，Dubbo没有引入任何Dubbo特有的元素，对代码侵入性几乎为零。&lt;/li>
&lt;li>将扩展注册到Dubbo中，只需要在ClassPath中添加配置文件。使用简单。而且不会对现有代码造成影响。符合开闭原则。&lt;/li>
&lt;li>dubbo的扩展机制设计默认值：@SPI(&amp;ldquo;dubbo&amp;rdquo;) 代表默认的spi对象&lt;/li>
&lt;li>Dubbo的扩展机制支持IoC,AoP等高级功能&lt;/li>
&lt;li>Dubbo的扩展机制能很好的支持第三方IoC容器，默认支持Spring Bean，可自己扩展来支持其他容器，比如Google的Guice。&lt;/li>
&lt;li>切换扩展点的实现，只需要在配置文件中修改具体的实现，不需要改代码。使用方便。&lt;/li>
&lt;/ul>
&lt;p>下一篇，我们将会一起深入Dubbo的源码，更深入的了解Dubbo的可扩展机制。&lt;/p></description></item><item><title>Blog: 提前if判断帮助CPU分支预测</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">enum&lt;/span> ChannelState &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CONNECTED&lt;span style="color:#719e07">,&lt;/span> DISCONNECTED&lt;span style="color:#719e07">,&lt;/span> SENT&lt;span style="color:#719e07">,&lt;/span> RECEIVED&lt;span style="color:#719e07">,&lt;/span> CAUGHT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@State&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Scope&lt;span style="color:#719e07">.&lt;/span>Benchmark&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ExecutionPlan&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Param&lt;/span>&lt;span style="color:#719e07">({&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1000000&amp;#34;&lt;/span> &lt;span style="color:#719e07">})&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> size&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#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 style="color:#719e07">;&lt;/span>
&lt;/span>&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&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&lt;span style="color:#719e07">.&lt;/span>values&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 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">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> Date&lt;span style="color:#719e07">().&lt;/span>getTime&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> size&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> nextInt &lt;span style="color:#719e07">=&lt;/span> random&lt;span style="color:#719e07">.&lt;/span>nextInt&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1000000&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>nextInt &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">100&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> states&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>RECEIVED&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 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&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Fork&lt;/span>&lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">5&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Benchmark&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@BenchmarkMode&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Mode&lt;span style="color:#719e07">.&lt;/span>Throughput&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">benchSiwtch&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ExecutionPlan plan&lt;span style="color:#719e07">,&lt;/span> Blackhole bh&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> plan&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#719e07">++&lt;/span>i&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">switch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>plan&lt;span style="color:#719e07">.&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>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> CONNECTED&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>CONNECTED&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> DISCONNECTED&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>DISCONNECTED&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> SENT&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>SENT&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> RECEIVED&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>RECEIVED&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> CAUGHT&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>CAUGHT&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bh&lt;span style="color:#719e07">.&lt;/span>consume&lt;span style="color:#719e07">(&lt;/span>result&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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>&lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">5&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Benchmark&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@BenchmarkMode&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Mode&lt;span style="color:#719e07">.&lt;/span>Throughput&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">benchIfAndSwitch&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ExecutionPlan plan&lt;span style="color:#719e07">,&lt;/span> Blackhole bh&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> plan&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#719e07">++&lt;/span>i&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ChannelState state &lt;span style="color:#719e07">=&lt;/span> plan&lt;span style="color:#719e07">.&lt;/span>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> &lt;span style="color:#719e07">(&lt;/span>state &lt;span style="color:#719e07">==&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>RECEIVED&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>RECEIVED&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">switch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>state&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> CONNECTED&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>CONNECTED&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> SENT&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>SENT&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> DISCONNECTED&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>DISCONNECTED&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> CAUGHT&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">+=&lt;/span> ChannelState&lt;span style="color:#719e07">.&lt;/span>CAUGHT&lt;span style="color:#719e07">.&lt;/span>ordinal&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bh&lt;span style="color:#719e07">.&lt;/span>consume&lt;span style="color:#719e07">(&lt;/span>result&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>benchSiwtch里是纯switch判断&lt;/li>
&lt;li>benchIfAndSwitch 里用一个if提前判断state是否&lt;code>ChannelState.RECEIVED&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>benchmark结果是：&lt;/p>
&lt;div class="highlight">&lt;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>Result &amp;#34;io.github.hengyunabc.jmh.TestBenchMarks.benchSiwtch&amp;#34;:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 576.745 ±(99.9%) 6.806 ops/s [Average]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (min, avg, max) = (490.348, 576.745, 618.360), stdev = 20.066
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CI (99.9%): [569.939, 583.550] (assumes normal distribution)
&lt;/span>&lt;/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># Run complete. Total time: 00:06:48
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Benchmark (size) Mode Cnt Score Error Units
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TestBenchMarks.benchIfAndSwitch 1000000 thrpt 100 1535.867 ± 61.212 ops/s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TestBenchMarks.benchSiwtch 1000000 thrpt 100 576.745 ± 6.806 ops/s
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到提前if判断的确提高了代码效率，这种技巧可以放在性能要求严格的地方。&lt;/p>
&lt;p>Benchmark代码：https://github.com/hengyunabc/jmh-demo&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;ul>
&lt;li>switch对于CPU来说难以做分支预测&lt;/li>
&lt;li>某些switch条件如果概率比较高，可以考虑单独提前if判断，充分利用CPU的分支预测机制&lt;/li>
&lt;/ul></description></item><item><title>Blog: 浅谈 RPC</title><link>https://cn.dubbo.apache.org/zh/blog/2019/01/07/%E6%B5%85%E8%B0%88-rpc/</link><pubDate>Mon, 07 Jan 2019 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/rpc/rpc-structure-1.png" alt="RPC结构">&lt;/p>
&lt;p>这里 user 就是 client 端，当 user 想发起一个远程调用时，它实际是通过本地调用 user-stub。user-stub 负责将调用的接口、方法和参数通过约定的协议规范进行编码并通过本地的 RPCRuntime 实例传输到远端的实例。远端 RPCRuntime 实例收到请求后交给 server-stub 进行解码后发起本地端调用，调用结果再返回给 user 端。&lt;/p>
&lt;p>以上是粗粒度的 RPC 实现概念结构，接下来我们进一步细化它应该由哪些组件构成，如下图所示。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/rpc/rpc-structure-2.png" alt="RPC 结构拆解">&lt;/p>
&lt;p>RPC 服务方通过 RpcServer 去导出（export）远程接口方法，而客户方通过 RpcClient 去引入（import）远程接口方法。客户方像调用本地方法一样去调用远程接口方法，RPC 框架提供接口的代理实现，实际的调用将委托给代理RpcProxy 。代理封装调用信息并将调用转交给RpcInvoker 去实际执行。在客户端的RpcInvoker 通过连接器RpcConnector 去维持与服务端的通道RpcChannel，并使用RpcProtocol 执行协议编码（encode）并将编码后的请求消息通过通道发送给服务方。&lt;/p>
&lt;p>RPC 服务端接收器 RpcAcceptor 接收客户端的调用请求，同样使用RpcProtocol 执行协议解码（decode）。解码后的调用信息传递给RpcProcessor 去控制处理调用过程，最后再委托调用给RpcInvoker 去实际执行并返回调用结果。如下是各个部分的详细职责：&lt;/p>
&lt;div class="highlight">&lt;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>1. RpcServer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 负责导出（export）远程接口
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2. RpcClient
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 负责导入（import）远程接口的代理实现
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>3. RpcProxy
&lt;/span>&lt;/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>4. RpcInvoker
&lt;/span>&lt;/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>5. RpcProtocol
&lt;/span>&lt;/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>6. RpcConnector
&lt;/span>&lt;/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>7. RpcAcceptor
&lt;/span>&lt;/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>8. RpcProcessor
&lt;/span>&lt;/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>9. RpcChannel
&lt;/span>&lt;/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="rpc-工作原理">RPC 工作原理&lt;/h2>
&lt;p>RPC的设计由Client，Client stub，Network ，Server stub，Server构成。 其中Client就是用来调用服务的，Cient stub是用来把调用的方法和参数序列化的（因为要在网络中传输，必须要把对象转变成字节），Network用来传输这些信息到Server stub， Server stub用来把这些信息反序列化的，Server就是服务的提供者，最终调用的就是Server提供的方法。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/rpc/rpc-work-principle.png" alt="RPC工作原理">&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Client像调用本地服务似的调用远程服务；&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Client stub接收到调用后，将方法、参数序列化&lt;/p>
&lt;/li>
&lt;li>
&lt;p>客户端通过sockets将消息发送到服务端&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Server stub 收到消息后进行解码（将消息对象反序列化）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Server stub 根据解码结果调用本地的服务&lt;/p>
&lt;/li>
&lt;li>
&lt;p>本地服务执行(对于服务端来说是本地执行)并将结果返回给Server stub&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Server stub将返回结果打包成消息（将结果消息对象序列化）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>服务端通过sockets将消息发送到客户端&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Client stub接收到结果消息，并进行解码（将结果消息反序列化）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>客户端得到最终结果。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>RPC 调用分以下两种：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>同步调用：客户方等待调用执行完成并返回结果。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>异步调用：客户方调用后不用等待执行结果返回，但依然可以通过回调通知等方式获取返回结果。若客户方不关心调用返回结果，则变成单向异步调用，单向调用不用返回结果。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>异步和同步的区分在于是否等待服务端执行完成并返回结果。&lt;/p>
&lt;h2 id="rpc-能干什么">RPC 能干什么？&lt;/h2>
&lt;p>RPC 的主要功能目标是让构建分布式计算（应用）更容易，在提供强大的远程调用能力时不损失本地调用的语义简洁性。为实现该目标，RPC 框架需提供一种透明调用机制，让使用者不必显式的区分本地调用和远程调用，在之前给出的一种实现结构，基于 stub 的结构来实现。下面我们将具体细化 stub 结构的实现。&lt;/p>
&lt;ul>
&lt;li>可以做到分布式，现代化的微服务&lt;/li>
&lt;li>部署灵活&lt;/li>
&lt;li>解耦服务&lt;/li>
&lt;li>扩展性强&lt;/li>
&lt;/ul>
&lt;p>RPC的目的是让你在本地调用远程的方法，而对你来说这个调用是透明的，你并不知道这个调用的方法是部署哪里。通过RPC能解耦服务，这才是使用RPC的真正目的。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>这篇文章介绍了 RPC 的一些基本原理，相信到这里您已经对 RPC 有了一定理解。其实发现实现一个 RPC 不算难，难的是实现一个高性能高可靠的RPC框架。比如，既然是分布式了，那么一个服务可能有多个实例，你在调用时，要如何获取这些实例的地址呢？这时候就需要一个服务注册中心，比如在Dubbo中，就可以使用Zookeeper作为注册中心，在调用时，从Zookeeper获取服务的实例列表，再从中选择一个进行调用。那么选哪个调用好呢？这时候就需要负载均衡了，于是你又得考虑如何实现复杂均衡，比如Dubbo就提供了好几种负载均衡策略。所以请继续关注我的另外两篇文章&lt;strong>RPC与服务化的关系&lt;/strong>和&lt;strong>注册中心，配置中心， 服务发现浅谈&lt;/strong>，相信会帮助对RPC设计和实现有更多的理解。&lt;/p></description></item><item><title>Blog: Dubbo服务分组和版本聚合</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>&lt;span style="color:#719e07">(&lt;/span>URL consumerUrl&lt;span style="color:#719e07">,&lt;/span> URL providerUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerInterface &lt;span style="color:#719e07">=&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getServiceInterface&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerInterface &lt;span style="color:#719e07">=&lt;/span> providerUrl&lt;span style="color:#719e07">.&lt;/span>getServiceInterface&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>ANY_VALUE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>consumerInterface&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> StringUtils&lt;span style="color:#719e07">.&lt;/span>isEquals&lt;span style="color:#719e07">(&lt;/span>consumerInterface&lt;span style="color:#719e07">,&lt;/span> providerInterface&lt;span style="color:#719e07">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&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&lt;span style="color:#719e07">(&lt;/span>providerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>CATEGORY_KEY&lt;span style="color:#719e07">,&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_CATEGORY&lt;span style="color:#719e07">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>CATEGORY_KEY&lt;span style="color:#719e07">,&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_CATEGORY&lt;span style="color:#719e07">)))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>providerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>ENABLED_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>ANY_VALUE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>consumerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>ENABLED_KEY&lt;span style="color:#719e07">)))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerGroup &lt;span style="color:#719e07">=&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>GROUP_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerVersion &lt;span style="color:#719e07">=&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>VERSION_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String consumerClassifier &lt;span style="color:#719e07">=&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>CLASSIFIER_KEY&lt;span style="color:#719e07">,&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>ANY_VALUE&lt;span style="color:#719e07">);&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 providerGroup &lt;span style="color:#719e07">=&lt;/span> providerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>GROUP_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerVersion &lt;span style="color:#719e07">=&lt;/span> providerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>VERSION_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String providerClassifier &lt;span style="color:#719e07">=&lt;/span> providerUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>CLASSIFIER_KEY&lt;span style="color:#719e07">,&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>ANY_VALUE&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>ANY_VALUE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>consumerGroup&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> StringUtils&lt;span style="color:#719e07">.&lt;/span>isEquals&lt;span style="color:#719e07">(&lt;/span>consumerGroup&lt;span style="color:#719e07">,&lt;/span> providerGroup&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> StringUtils&lt;span style="color:#719e07">.&lt;/span>isContains&lt;span style="color:#719e07">(&lt;/span>consumerGroup&lt;span style="color:#719e07">,&lt;/span> providerGroup&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>ANY_VALUE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>consumerVersion&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> StringUtils&lt;span style="color:#719e07">.&lt;/span>isEquals&lt;span style="color:#719e07">(&lt;/span>consumerVersion&lt;span style="color:#719e07">,&lt;/span> providerVersion&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">(&lt;/span>consumerClassifier &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>ANY_VALUE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>consumerClassifier&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> StringUtils&lt;span style="color:#719e07">.&lt;/span>isEquals&lt;span style="color:#719e07">(&lt;/span>consumerClassifier&lt;span style="color:#719e07">,&lt;/span> providerClassifier&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果没有配置组别和版本号，默认值为空。服务端和消费端都没有配，只有服务接口，其他两个信息都为空，也是可以“找到”对方的，那服务名和版本号可以如何使用呢？下面我们来看一下具体的场景：&lt;/p>
&lt;h3 id="服务分组">服务分组&lt;/h3>
&lt;p>当一个接口有多种实现时，可以用 group 区分。&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> group=&lt;span style="color:#2aa198">&amp;#34;feedback&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.IndexService&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> group=&lt;span style="color:#2aa198">&amp;#34;member&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.IndexService&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;feedbackIndexService&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;feedback&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.IndexService&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;memberIndexService&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;member&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.IndexService&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;barService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.foo.BarService&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;*&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>当一个接口实现，出现不兼容升级时，可以用版本号过渡，版本号不同的服务相互间不引用。&lt;/p>
&lt;p>可以按照以下的步骤进行版本迁移：&lt;/p>
&lt;ol>
&lt;li>在低压力时间段，先升级一半提供者为新版本&lt;/li>
&lt;li>再将所有消费者升级为新版本&lt;/li>
&lt;li>然后将剩下的一半提供者升级为新版本&lt;/li>
&lt;/ol>
&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;com.foo.BarService&amp;#34;&lt;/span> version=&lt;span style="color:#2aa198">&amp;#34;1.0.0&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;com.foo.BarService&amp;#34;&lt;/span> version=&lt;span style="color:#2aa198">&amp;#34;2.0.0&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;barService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.foo.BarService&amp;#34;&lt;/span> version=&lt;span style="color:#2aa198">&amp;#34;1.0.0&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;barService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.foo.BarService&amp;#34;&lt;/span> version=&lt;span style="color:#2aa198">&amp;#34;2.0.0&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;barService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.foo.BarService&amp;#34;&lt;/span> version=&lt;span style="color:#2aa198">&amp;#34;*&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>按组合并返回结果，比如菜单服务，接口一样，但有多种实现，用group区分，现在消费方需从每种group中调用一次返回结果，合并结果返回，这样就可以实现聚合菜单项。&lt;/p>
&lt;h4 id="配置">配置&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-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.MenuService&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;*&amp;#34;&lt;/span> merger=&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>合并指定分组&lt;/p>
&lt;div class="highlight">&lt;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> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.MenuService&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;aaa,bbb&amp;#34;&lt;/span> merger=&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>指定方法合并结果，其它未指定的方法，将只调用一个 Group&lt;/p>
&lt;div class="highlight">&lt;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> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.MenuService&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;*&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;getMenuItems&amp;#34;&lt;/span> merger=&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;/p>
&lt;div class="highlight">&lt;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> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.MenuService&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;*&amp;#34;&lt;/span> merger=&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:method&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;getMenuItems&amp;#34;&lt;/span> merger=&lt;span style="color:#2aa198">&amp;#34;false&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> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.MenuService&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;*&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;getMenuItems&amp;#34;&lt;/span> merger=&lt;span style="color:#2aa198">&amp;#34;mymerge&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> interface=&lt;span style="color:#2aa198">&amp;#34;com.xxx.MenuService&amp;#34;&lt;/span> group=&lt;span style="color:#2aa198">&amp;#34;*&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;getMenuItems&amp;#34;&lt;/span> merger=&lt;span style="color:#2aa198">&amp;#34;.addAll&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;h4 id="实现原理">实现原理&lt;/h4>
&lt;p>如果配置了merge，Dubbo会分别调用多个组别的服务提供者，然后把结果聚合，返回给消费端，具体的实现在&lt;code>MergeableClusterInvoker.java&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> Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 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 &lt;span style="color:#719e07">=&lt;/span> directory&lt;span style="color:#719e07">.&lt;/span>list&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String merger &lt;span style="color:#719e07">=&lt;/span> getUrl&lt;span style="color:#719e07">().&lt;/span>getMethodParameter&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">.&lt;/span>getMethodName&lt;span style="color:#719e07">(),&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>MERGER_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>merger&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#586e75">// If a method doesn&amp;#39;t have a merger, only invoke one Group
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#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 &lt;span style="color:#719e07">:&lt;/span> invokers&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>isAvailable&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invokers&lt;span style="color:#719e07">.&lt;/span>iterator&lt;span style="color:#719e07">().&lt;/span>next&lt;span style="color:#719e07">().&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&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> returnType&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> returnType &lt;span style="color:#719e07">=&lt;/span> getInterface&lt;span style="color:#719e07">().&lt;/span>getMethod&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invocation&lt;span style="color:#719e07">.&lt;/span>getMethodName&lt;span style="color:#719e07">(),&lt;/span> invocation&lt;span style="color:#719e07">.&lt;/span>getParameterTypes&lt;span style="color:#719e07">()).&lt;/span>getReturnType&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>NoSuchMethodException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> returnType &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Future&lt;span style="color:#719e07">&amp;lt;&lt;/span>Result&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> results &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Future&lt;span style="color:#719e07">&amp;lt;&lt;/span>Result&lt;span style="color:#719e07">&amp;gt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&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 &lt;span style="color:#719e07">:&lt;/span> invokers&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Future&lt;span style="color:#719e07">&amp;lt;&lt;/span>Result&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> executor&lt;span style="color:#719e07">.&lt;/span>submit&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> Callable&lt;span style="color:#719e07">&amp;lt;&lt;/span>Result&lt;span style="color:#719e07">&amp;gt;()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Result &lt;span style="color:#268bd2">call&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RpcInvocation&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> results&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getServiceKey&lt;span style="color:#719e07">(),&lt;/span> future&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object result &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Result&lt;span style="color:#719e07">&amp;gt;&lt;/span> resultList &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&lt;/span>Result&lt;span style="color:#719e07">&amp;gt;(&lt;/span>results&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> timeout &lt;span style="color:#719e07">=&lt;/span> getUrl&lt;span style="color:#719e07">().&lt;/span>getMethodParameter&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">.&lt;/span>getMethodName&lt;span style="color:#719e07">(),&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>TIMEOUT_KEY&lt;span style="color:#719e07">,&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_TIMEOUT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Future&lt;span style="color:#719e07">&amp;lt;&lt;/span>Result&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> results&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Future&lt;span style="color:#719e07">&amp;lt;&lt;/span>Result&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Result r &lt;span style="color:#719e07">=&lt;/span> future&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>timeout&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>r&lt;span style="color:#719e07">.&lt;/span>hasException&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Invoke &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> getGroupDescFromServiceKey&lt;span style="color:#719e07">(&lt;/span>entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34; failed: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> r&lt;span style="color:#719e07">.&lt;/span>getException&lt;span style="color:#719e07">().&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> r&lt;span style="color:#719e07">.&lt;/span>getException&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resultList&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>r&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to invoke service &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>resultList&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcResult&lt;span style="color:#719e07">((&lt;/span>Object&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>resultList&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> resultList&lt;span style="color:#719e07">.&lt;/span>iterator&lt;span style="color:#719e07">().&lt;/span>next&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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>returnType &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#dc322f">void&lt;/span>&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcResult&lt;span style="color:#719e07">((&lt;/span>Object&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果配置了merger，会依次调用，结果都放在results里面，其中value都是future类型，等调用完成之后，再遍历results，通过future.get拿到真正的结果，到此为止，所有调用的结果都放在resultList里面了，接下来要做的是把结果进行聚合：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> Merger resultMerger&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ConfigUtils&lt;span style="color:#719e07">.&lt;/span>isDefault&lt;span style="color:#719e07">(&lt;/span>merger&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resultMerger &lt;span style="color:#719e07">=&lt;/span> MergerFactory&lt;span style="color:#719e07">.&lt;/span>getMerger&lt;span style="color:#719e07">(&lt;/span>returnType&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resultMerger &lt;span style="color:#719e07">=&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Merger&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getExtension&lt;span style="color:#719e07">(&lt;/span>merger&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>resultMerger &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> rets &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&lt;/span>Object&lt;span style="color:#719e07">&amp;gt;(&lt;/span>resultList&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Result r &lt;span style="color:#719e07">:&lt;/span> resultList&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rets&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>r&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">=&lt;/span> resultMerger&lt;span style="color:#719e07">.&lt;/span>merge&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rets&lt;span style="color:#719e07">.&lt;/span>toArray&lt;span style="color:#719e07">((&lt;/span>Object&lt;span style="color:#719e07">[])&lt;/span> Array&lt;span style="color:#719e07">.&lt;/span>newInstance&lt;span style="color:#719e07">(&lt;/span>returnType&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;There is no merger to merge result.&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcResult&lt;span style="color:#719e07">(&lt;/span>result&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里会根据返回值的类型，获取到对应的resultMerger，除了Dubbo默认实现的类型外，也可以自己指定merger类型并且添加相应的扩展，通过实现&lt;code>merge&lt;/code>方法类进行结果聚合。&lt;/p></description></item><item><title>Blog: Dubbo 协议详解</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;/blockquote>
&lt;p>可以看到通信协议需要定义语法、语义以及通信上的同步操作，这里描述的内容实际就是对前面自定义字符串传输协议的形式化描述。&lt;/p>
&lt;h3 id="codec的定义">&lt;code>Codec&lt;/code>的定义&lt;/h3>
&lt;p>&lt;code>org.apache.dubbo.remoting.Codec2&lt;/code>定义为I/O的 &lt;strong>Codec&lt;/strong> 过程，因此主要的方法就是&lt;code>encode&lt;/code>和&lt;code>decode&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">@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">Codec2&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Adaptive&lt;/span>&lt;span style="color:#719e07">({&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>CODEC_KEY&lt;span style="color:#719e07">})&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">encode&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Channel channel&lt;span style="color:#719e07">,&lt;/span> ChannelBuffer buffer&lt;span style="color:#719e07">,&lt;/span> Object message&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Adaptive&lt;/span>&lt;span style="color:#719e07">({&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>CODEC_KEY&lt;span style="color:#719e07">})&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object &lt;span style="color:#268bd2">decode&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Channel channel&lt;span style="color:#719e07">,&lt;/span> ChannelBuffer buffer&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&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">enum&lt;/span> DecodeResult &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NEED_MORE_INPUT&lt;span style="color:#719e07">,&lt;/span> SKIP_SOME_INPUT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>​ &lt;code>Codec&lt;/code>工作在一种协议上，&lt;code>encode&lt;/code>是将通信对象编码到&lt;code>ByteBufferWrapper&lt;/code>中，&lt;code>decode&lt;/code>是将从网络上读取的&lt;code>ChannelBuffer&lt;/code>解码为&lt;code>Object&lt;/code>，也就是通信对象。&lt;/p>
&lt;h2 id="常见的协议模式">常见的协议模式&lt;/h2>
&lt;p>应用层协议一般的形式有三种：定长协议、特殊结束符和协议头+payload模式，下面介绍一下这些形式的具体内容。&lt;/p>
&lt;p>从网络上以流的形式进行数据的读取，需要确定的是一次有意义的传输内容在读到何时结束，因为一个一个byte传输过来，需要有一个结束。而且数据在网络上的传输，存在粘包和半包的情况，能够应对这个问题的办法就是协议能够准确的识别，当粘包发生时不会多读，当半包发生时会继续读取。&lt;/p>
&lt;h3 id="定长协议">定长协议&lt;/h3>
&lt;p>定长的协议是指协议内容的长度是固定的，比如协议byte长度是50，当从网络上读取50个byte后，就进行decode解码操作。定长协议在读取或者写入时，效率比较高，因为数据缓存的大小基本都确定了，就好比数组一样，缺陷就是适应性不足，以RPC场景为例，很难估计出定长的长度是多少。&lt;/p>
&lt;blockquote>
&lt;p>可以参考Netty的&lt;code>FixedLengthFrameDecoder&lt;/code>&lt;/p>
&lt;/blockquote>
&lt;h3 id="特殊结束符">特殊结束符&lt;/h3>
&lt;p>相比定长协议，如果能够定义一个特殊字符作为每个协议单元结束的标示，就能够以变长的方式进行通信，从而在数据传输和高效之间取得平衡，比如用特殊字符&lt;code>\n&lt;/code>。&lt;/p>
&lt;p>特殊结束符方式的问题是过于简单的思考了协议传输的过程，对于一个协议单元必须要全部读入才能够进行处理，除此之外必须要防止用户传输的数据不能同结束符相同，否则就会出现紊乱。&lt;/p>
&lt;blockquote>
&lt;p>可以参考Netty的&lt;code>DelimiterBasedFrameDecoder&lt;/code>&lt;/p>
&lt;/blockquote>
&lt;h3 id="变长协议协议头payload">变长协议（协议头+payload）&lt;/h3>
&lt;p>一般是自定义协议，会以定长加不定长的部分组成，其中定长的部分需要描述不定长的内容长度。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&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;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>可以参考Netty的&lt;code>LengthFieldBasedFrameDecoder&lt;/code>&lt;/p>
&lt;/blockquote>
&lt;p>Dubbo 协议实际上就是一种变长协议，后面的章节会详细介绍。&lt;/p>
&lt;h2 id="dubbo-协议">Dubbo 协议&lt;/h2>
&lt;h3 id="协议概览">协议概览&lt;/h3>
&lt;p>Dubbo 框架定义了私有的RPC协议，其中请求和响应协议的具体内容我们使用表格来展示。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/dev/dubbo_protocol_header.png" alt="/dev-guide/images/dubbo_protocol_header.jpg">&lt;/p>
&lt;h3 id="协议详情">协议详情&lt;/h3>
&lt;ul>
&lt;li>
&lt;p>Magic - Magic High &amp;amp; Magic Low (16 bits)&lt;/p>
&lt;p>标识协议版本号，Dubbo 协议：0xdabb&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Req/Res (1 bit)&lt;/p>
&lt;p>标识是请求或响应。请求： 1; 响应： 0。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>2 Way (1 bit)&lt;/p>
&lt;p>仅在 Req/Res 为1（请求）时才有用，标记是否期望从服务器返回值。如果需要来自服务器的返回值，则设置为1。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Event (1 bit)&lt;/p>
&lt;p>标识是否是事件消息，例如，心跳事件。如果这是一个事件，则设置为1。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Serialization ID (5 bit)&lt;/p>
&lt;p>标识序列化类型：比如 fastjson 的值为6。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Status (8 bits)&lt;/p>
&lt;p>仅在 Req/Res 为0（响应）时有用，用于标识响应的状态。&lt;/p>
&lt;ul>
&lt;li>20 - OK&lt;/li>
&lt;li>30 - CLIENT_TIMEOUT&lt;/li>
&lt;li>31 - SERVER_TIMEOUT&lt;/li>
&lt;li>40 - BAD_REQUEST&lt;/li>
&lt;li>50 - BAD_RESPONSE&lt;/li>
&lt;li>60 - SERVICE_NOT_FOUND&lt;/li>
&lt;li>70 - SERVICE_ERROR&lt;/li>
&lt;li>80 - SERVER_ERROR&lt;/li>
&lt;li>90 - CLIENT_ERROR&lt;/li>
&lt;li>100 - SERVER_THREADPOOL_EXHAUSTED_ERROR&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Request ID (64 bits)&lt;/p>
&lt;p>标识唯一请求。类型为long。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Data Length (32 bits)&lt;/p>
&lt;p>序列化后的内容长度（可变部分），按字节计数。int类型。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Variable Part&lt;/p>
&lt;p>被特定的序列化类型（由序列化 ID 标识）序列化后，每个部分都是一个 byte [] 或者 byte&lt;/p>
&lt;ul>
&lt;li>如果是请求包 ( Req/Res = 1)，则每个部分依次为：
&lt;ul>
&lt;li>Dubbo version&lt;/li>
&lt;li>Service name&lt;/li>
&lt;li>Service version&lt;/li>
&lt;li>Method name&lt;/li>
&lt;li>Method parameter types&lt;/li>
&lt;li>Method arguments&lt;/li>
&lt;li>Attachments&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>如果是响应包（Req/Res = 0），则每个部分依次为：
&lt;ul>
&lt;li>返回值类型(byte)，标识从服务器端返回的值类型：
&lt;ul>
&lt;li>返回空值：RESPONSE_NULL_VALUE 2&lt;/li>
&lt;li>正常响应值： RESPONSE_VALUE 1&lt;/li>
&lt;li>异常：RESPONSE_WITH_EXCEPTION 0&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>返回值：从服务端返回的响应bytes&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>注意：&lt;/strong> 对于(Variable Part)变长部分，当前版本的Dubbo 框架使用json序列化时，在每部分内容间额外增加了换行符作为分隔，请在Variable Part的每个part后额外增加换行符， 如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>Dubbo version bytes (换行符)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service name bytes (换行符)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="dubbo-协议的优缺点">Dubbo 协议的优缺点&lt;/h2>
&lt;h3 id="优点">优点&lt;/h3>
&lt;ul>
&lt;li>协议设计上很紧凑，能用 1 个 bit 表示的，不会用一个 byte 来表示，比如 boolean 类型的标识。&lt;/li>
&lt;li>请求、响应的 header 一致，通过序列化器对 content 组装特定的内容，代码实现起来简单。&lt;/li>
&lt;/ul>
&lt;h3 id="可以改进的点">可以改进的点&lt;/h3>
&lt;ul>
&lt;li>
&lt;p>类似于 http 请求，通过 header 就可以确定要访问的资源，而 Dubbo 需要涉及到用特定序列化协议才可以将服务名、方法、方法签名解析出来，并且这些资源定位符是 string 类型或者 string 数组，很容易转成 bytes，因此可以组装到 header 中。类似于 http2 的 header 压缩，对于 rpc 调用的资源也可以协商出来一个int来标识，从而提升性能，如果在&lt;code>header&lt;/code>上组装资源定位符的话，该功能则更易实现。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>通过 req/res 是否是请求后，可以精细定制协议，去掉一些不需要的标识和添加一些特定的标识。比如&lt;code>status&lt;/code>,&lt;code>twoWay&lt;/code>标识可以严格定制，去掉冗余标识。还有超时时间是作为 Dubbo 的 &lt;code>attachment&lt;/code> 进行传输的，理论上应该放到请求协议的header中，因为超时是网络请求中必不可少的。提到 &lt;code>attachment&lt;/code> ，通过实现可以看到 &lt;code>attachment&lt;/code> 中有一些是跟协议 &lt;code>content&lt;/code>中已有的字段是重复的，比如 &lt;code>path&lt;/code>和&lt;code>version&lt;/code>等字段，这些会增大协议尺寸。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Dubbo 会将服务名&lt;code>com.alibaba.middleware.hsf.guide.api.param.ModifyOrderPriceParam&lt;/code>，转换为&lt;code>Lcom/alibaba/middleware/hsf/guide/api/param/ModifyOrderPriceParam;&lt;/code>，理论上是不必要的，最后追加一个&lt;code>;&lt;/code>即可。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Dubbo 协议没有预留扩展字段，没法新增标识，扩展性不太好，比如新增&lt;code>响应上下文&lt;/code>的功能，只有改协议版本号的方式，但是这样要求客户端和服务端的版本都进行升级，对于分布式场景很不友好。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>本文主要介绍了协议的概念和常用的协议模式，后面对 Dubbo 协议进行了详细分析，也提到了一些不足的地方，但是相对于其简洁性和易于实现性，以上提出的缺点不足以有动力设计出一个新版本的协议，所以欢迎大家提出对协议优化方面的建议和特性。&lt;/p></description></item><item><title>Blog: Dubbo与Kubernetes集成</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/k8s/1.png" alt="img">&lt;/p>
&lt;p>以微服务架构视角，Kubernetes在一定意义上是微服务框架（这时较叫微服务平台或toolkit集更合适），支持微服务的服务发现/注册的基本能力。借用如下图做一个简单描述。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/k8s/2.jpeg" alt="img">&lt;/p>
&lt;p>话题再展开一下，微服务领域涉及众多问题，大概可以用下图说明。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/k8s/3.jpeg" alt="img">&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>
&lt;/blockquote>
&lt;p>概括来说Kubernetes service具有如下特点&lt;/p>
&lt;ul>
&lt;li>每个Service都有一个唯一的名字，及对应IP。IP是kubernetes自动分配的，名字是开发者自己定义的。&lt;/li>
&lt;li>Service的IP有几种表现形式，分别是ClusterIP，NodePort,LoadBalance,Ingress。 ClusterIP主要用于集群内通信；NodePort，Ingress，LoadBalance用于暴露服务给集群外的访问入口。&lt;/li>
&lt;/ul>
&lt;p>乍一看，Kubernetes的service都是唯一的IP，在原有的Dubbo/HSF固定思维下：Dubbo/HSF的service是由整个服务集群的IP聚合而成，貌似是有本质区别的，细想下来差别不大，因为Kubernetes下的唯一IP只是一个VIP，背后挂在了多个endpoint，那才是事实上的处理节点。此处只讨论集群内的Dubbo服务在同一个kubernetes集群内访问；至于kubernetes外的consumer访问kubernetes内的provider，涉及到网络地址空间的问题，一般需要GateWay/loadbalance来做映射转换，不展开讨论。针对Kubernetes内有两种方案可选： ：&lt;/p>
&lt;ol>
&lt;li>DNS： 默认Kubernetes的service是靠DNS插件(最新版推荐是coreDNS)， Dubbo上有个proposal是关于这个的。我的理解是static resolution的机制是最简单最需要支持的一种service discovery机制，具体也可以参考Envoy在此的观点，由于HSF/Dubbo一直突出其软负载的地址发现能力，反而忽略Static的策略。同时蚂蚁的SOFA一直是支持此种策略，那一个SOFA工程的工程片段做一个解释。这样做有两个好处，1）当软负载中心crash不可用造成无法获取地址列表时，有一定的机制Failover到此策略来处理一定的请求。 2）在LDC/单元化下，蚂蚁的负载中心集群是机房/区域内收敛部署的，首先保证软负载中心的LDC化了进而稳定可控，当单元需要请求中心时，此VIP的地址发现就排上用场了。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2018/09/30/integrate-dubbo-with-kubernetes/TB1Kj1ktpkoBKNjSZFEXXbrEVXa-985-213.png" alt="img">&lt;/p>
&lt;ol start="2">
&lt;li>API：DNS是依靠DNS插件进行的，相当于额外的运维开销，所以考虑直接通过kubernetes的client来获取endpoint。事实上，通过访问Kubernetes的API server接口是可以直接获取某个servie背后的endpoint列表，同时可以监听其地址列表的变化。从而实现Dubbo/HSF所推荐的软负载发现策略。具体可以参考代码：&lt;/li>
&lt;/ol>
&lt;p>以上两种思路都需要考虑以下两点:&lt;/p>
&lt;ol>
&lt;li>Kubernetes和Dubbo对于service的名字是映射一致的。Dubbo的服务是由serviename，group，version三个来确定其唯一性，而且servicename一般其服务接口的包名称，比较长。需要映射Kubernetes的servie名与dubbo的服务名。要么是像SOFA那样增加一个属性来进行定义，这是个大的改动，但最合理；要么是通过固定规则来引用部署的环境变量，可用于快速验证。&lt;/li>
&lt;li>端口问题：默认Pod与Pod的网络互通算是解决了，需要验证。&lt;/li>
&lt;/ol>
&lt;h2 id="demo验证">Demo验证&lt;/h2>
&lt;p>下面通过阿里云的容器镜像服务和EDAS中的Kubernetes服务来做一次Demo部署。访问阿里云 -&amp;gt; 容器镜像服务。&lt;/p>
&lt;ol>
&lt;li>创建镜像仓库并绑定github代码库。如下图&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2018/09/30/integrate-dubbo-with-kubernetes/TB1m.tEtrorBKNjSZFjXXc_SpXa-1892-870.png" alt="img">&lt;/p>
&lt;ol start="2">
&lt;li>点击管理 &lt;strong>进行创建好的仓库&lt;/strong>，通过镜像服务下的构建功能，把demo构建成image，并发布到指定仓库。如下图。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2018/09/30/integrate-dubbo-with-kubernetes/TB1oYqvtcIrBKNjSZK9XXagoVXa-1872-888.png" alt="img">&lt;/p>
&lt;ol start="3">
&lt;li>切换到企业级分布式应用服务（EDAS）产品，在资源管理 -&amp;gt; 集群 下创建Kubernetes集群并绑定ECS，如下图.&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2018/09/30/integrate-dubbo-with-kubernetes/TB1b1p2trZnBKNjSZFKXXcGOVXa-1858-833.png" alt="img">&lt;/p>
&lt;ol start="4">
&lt;li>应用管理 -》创建应用，&lt;strong>类型为kubernetes应用&lt;/strong> 并且指定在容器镜像服务中的镜像。如下图。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2018/09/30/integrate-dubbo-with-kubernetes/TB1b1p2trZnBKNjSZFKXXcGOVXa-1858-833.png" alt="img">&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2018/09/30/integrate-dubbo-with-kubernetes/TB18uzTtdcnBKNjSZR0XXcFqFXa-1820-861.png" alt="">&lt;/p>
&lt;ol start="5">
&lt;li>创建完成后，进行应用部署。如下图&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2018/09/30/integrate-dubbo-with-kubernetes/TB1fEpEtrorBKNjSZFjXXc_SpXa-1846-783.png" alt="">&lt;/p>
&lt;ul>
&lt;li>
&lt;p>补充应用名不能有大写字母，全部小写，否则有部署失败的问题。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>在创建应用时，选中镜像后，下一步的按钮无法点击，需要点击选择来继续。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>EDAS有两套独立的Kubernetes服务，一套是基于阿里云的容器服务，一套是Lark自己搞的。本人体验的是后者。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Docker与IDE集成的开发联调，需要考虑集成IDEA的相关插件。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>部署时总是出错，感觉Kubernetes服务上哪里有问题。需要进一步排查。&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-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;kind&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;Pod&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;namespace&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;lzumwsrddf831iwarhehd14zh2-default&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo-k8s-demo-610694273-jq238&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;uid&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;12892e67-8bc8-11e8-b96a-00163e02c37b&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;apiVersion&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;v1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#34;resourceVersion&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;850282769&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}, &lt;span style="color:#2aa198">&amp;#34;reason&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;FailedSync&amp;#34;&lt;/span>, &lt;span style="color:#2aa198">&amp;#34;message&amp;#34;&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;Error syncing pod&amp;#34;&lt;/span>, &amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: Dubbo Mesh ｜ Service Mesh的实践与探索</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/meetup-chengdu/all-hands.webp" alt="img">&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 src="https://cn.dubbo.apache.org/imgs/blog/meetup-chengdu/challenges.jpg" alt="img">&lt;/p>
&lt;h4 id="挑战二微服务框架sdk多语言并行开发与维护成本高">挑战二：微服务框架SDK多语言并行开发与维护成本高。&lt;/h4>
&lt;p>以前我们都是通过对技术栈的统一来提升成本优势和团队效率，大家可以用一种语言去开发和维护，避免多语言时团队的不聚焦。但在软件和开源生态演进的过程中，多语言已经成为一种流行，因为不同语言都有其自身的优势，今天大家能看到的一个现象是云原生的生态中有多种开发语言，使用频率最高的语言已经不是Java了，而是Go，是因为Go的footprint很小。再以 Dubbo为例，除了Java，我们还提供C++，Node.js的SDK，以便让更多的开发者可以加入Dubbo生态，但所有的这些，如果没有社区力量的参与，是很难维持的。
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/meetup-chengdu/speaker.webp" alt="img">&lt;/p>
&lt;h4 id="挑战三异构服务框架难以共存完成渐进式演进">挑战三：异构服务框架难以共存完成渐进式演进。&lt;/h4>
&lt;p>我们结合场景来看看这个挑战。阿里巴巴收购了一些企业，被收购企业的技术栈可能和阿里巴巴不同，比如有些用的是Go语言，有些用的是PHP，这时候为了统一技术栈，我们需要对这类技术平台推倒重来，但这个过程中，我们会面临一系列问题，首当其冲的就是推倒重来会带来巨大的技术风险，其次是可能会面临技术人员大批量流失的风险，这在社会责任的层面也是很难接受。所以我们在寻求一种可能的方案，去解决这类问题。&lt;/p>
&lt;h4 id="挑战四是单一的语言限制了人才的多样性">挑战四：是单一的语言限制了人才的多样性。&lt;/h4>
&lt;p>这里，我们不去争论某个编程语言的好与坏，每个语言都有其适用场景，你不能说我手里有个榔头，你面对的都是钉子。以前我们觉得统一技术栈可以集中开发力量，并且带来较高的运维便利性。但伴随着互联网带来的快节奏，以往的团队能力设置已经很难满足这类变化，对工程师个体提出了更高的要求，我们不仅仅需要是某一方面的专家，而且还需要具备多域的工作技能，DevOps和全栈工程师就是这类快节奏变化下最好的注脚。
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/meetup-chengdu/challenges-continued.jpg" alt="img">&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 src="https://cn.dubbo.apache.org/imgs/blog/meetup-chengdu/trends.jpg" alt="img">&lt;/p>
&lt;p>数据安全将成为公有云分布式应用的生命线。云原生时代，业务即便没上云，企业对自身数据的安全都是有诉求的，尤其是在金融行业，如果通过抓包就能获取一些敏感信息，这将会给企业带来巨大的风险。&lt;/p>
&lt;p>Cloud native成为distributionless（无分布式）的主要探索路径。分布式发展的终极形式是无分布式，在未来我们做开发，所有的代码在web上写好后，通过点击一个按钮，所有部署都会自动实现，所有的code review的工作可以在一个统一的工作台上全部实现。
&lt;img src="https://cn.dubbo.apache.org/imgs/blog/meetup-chengdu/audience-shapshot.webp" alt="img">&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 src="https://cn.dubbo.apache.org/imgs/blog/meetup-chengdu/what.jpg" alt="img">&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 src="https://cn.dubbo.apache.org/imgs/blog/meetup-chengdu/forms.jpg" alt="img">&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>
&lt;h2 id="service-mesh的价值">Service Mesh的价值&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>为单体应用向微服务架构演进提供了渐进的途径&lt;/p>
&lt;/li>
&lt;li>
&lt;p>为异构（微）服务框架/平台提供了融合发展的可能&lt;/p>
&lt;ul>
&lt;li>被收购子公司与母公司的业务可以融合发展&lt;/li>
&lt;/ul>
&lt;/li>
&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;li>
&lt;p>培育了多语言业务开发的土壤&lt;/p>
&lt;ul>
&lt;li>助力人才发展中编程语言的多样性&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>对（异构）微服务架构应用实现更为有效的全局一体化监管控&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="dubbo-mesh的发展思路">Dubbo Mesh的发展思路&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>迎合Kubernetes已成orchestrator王者的大势&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="qa">Q&amp;amp;A&lt;/h2>
&lt;h4 id="阿里巴巴是怎么从微服务过渡到sidecar模式再过渡到service-mesh">阿里巴巴是怎么从微服务过渡到sidecar模式，再过渡到Service Mesh？&lt;/h4>
&lt;p>整个过渡是渐进式的，我们会将控制平面的一些组件先下沉到与sidecar部署在一起，这一下沉能很好复用开源软件已有的能力而减少开发工作量。当这一步骤完成后，被下沉的控制面组件会重新拉回到上面的控制面，那时就会面临一定的服务端改造，一旦改造完成就有了一个全新、完整的Service Mesh。&lt;/p>
&lt;h4 id="service-mesh中的服务注册发现负载均衡网关熔断降级超时限流消息总线分布式配置这些都是怎么实现的">Service Mesh中的服务注册发现，负载均衡，网关，熔断降级，超时，限流，消息总线，分布式配置，这些都是怎么实现的？&lt;/h4>
&lt;p>Dubbo Mesh在控制面会基于Istio去做，而Istio已经具备了Kubernetes下的服务注册与发现能力，我们要做的是扩充Istio的能力，让服务注册与发现能与ZooKeeper、Nacos进行对接去完成。基于开源的Envoy所实现的sidecar已实现了超时处理的功能，相应的内容可以读代码去了解。其他内容我们仍在规划中。&lt;/p>
&lt;h4 id="dubbo-mesh目前性能怎么样-增加一层sidecar导致dubbo的rt有多少">Dubbo Mesh目前性能怎么样? 增加一层sidecar导致Dubbo的RT有多少？&lt;/h4>
&lt;p>在使用iptables的情形下，一跳增加1.5毫秒，如果不采用iptables直接proxy方式的情形下应当性能更好（这一点与Lyft也邮件确认过了），我们接下来会做更多的性能测试，目前的焦点更多在于功能层面。&lt;/p>
&lt;h4 id="dubbo-mesh是把双刃剑经过的链路更复杂运维和开发者问题排查有没有更有效的工具">Dubbo Mesh是把双刃剑，经过的链路更复杂，运维和开发者问题排查有没有更有效的工具？&lt;/h4>
&lt;p>理论上，增加一跳并没有改变服务调用的拓扑结构，但确实会增加复杂度，这个应当通过设计实现去解决。好在因为是一体化的方案，所以解决这类问题时需要更具全局视野。&lt;/p>
&lt;h4 id="service-mesh中控制面板也用c吗我看主流很多实现都是go-我相信大佬做过技术调研有哪些优势">Service Mesh中控制面板也用C++吗？我看主流很多实现都是Go， 我相信大佬做过技术调研，有哪些优势？&lt;/h4>
&lt;p>控制面是复用Istio的，是Go语言的。我们力争不重复造轮子，而是以开放的心态去共建。&lt;/p>
&lt;h4 id="client做解码和反序列化是吧有计划支持http2协议吗">Client做解码和反序列化是吧，有计划支持HTTP2协议吗？&lt;/h4>
&lt;p>Envoy默认就支持了，不需我们开发。这也是借力开源的收益。&lt;/p>
&lt;h4 id="dubbo-mesh已经支持unix-domain-socket了吗">Dubbo Mesh已经支持UNIX Domain Socket了吗？&lt;/h4>
&lt;p>目前不支持，这个还处于意向阶段。&lt;/p></description></item><item><title>Blog: 如何基于Dubbo实现全异步调用链</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/docsv2.7/user/examples/async-call/">异步调用&lt;/a>、&lt;a href="https://cn.dubbo.apache.org/zh/docsv2.7/user/examples/callback-parameter/">参数回调&lt;/a>、&lt;a href="https://cn.dubbo.apache.org/zh/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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">findFoo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;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>&lt;span style="color:#586e75">&lt;/span>fooService&lt;span style="color:#719e07">.&lt;/span>findFoo&lt;span style="color:#719e07">(&lt;/span>fooId&lt;span style="color:#719e07">);&lt;/span>
&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>&lt;span style="color:#586e75">&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&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">().&lt;/span>getFuture&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>fooFuture&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>或&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 此调用会立即返回null
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>fooService&lt;span style="color:#719e07">.&lt;/span>findFoo&lt;span style="color:#719e07">(&lt;/span>fooId&lt;span style="color:#719e07">);&lt;/span>
&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>&lt;span style="color:#586e75">&lt;/span>ResponseFuture future &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">((&lt;/span>FutureAdapter&lt;span style="color:#719e07">)&lt;/span>RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">().&lt;/span>getFuture&lt;span style="color:#719e07">()).&lt;/span>getFuture&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>future&lt;span style="color:#719e07">.&lt;/span>setCallback&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ResponseCallback&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">done&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Object response&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>print&lt;span style="color:#719e07">(&lt;/span>response&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">caught&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Throwable exception&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exception&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>从这个简单的示例我们可以体会到一些使用中的不便之处：&lt;/p>
&lt;ol>
&lt;li>findFoo的同步接口，不能直接返回代表异步结果的Future，通过RpcContext进一步获取。&lt;/li>
&lt;li>Future只支持阻塞式的get()接口获取结果。&lt;/li>
&lt;li>通过获取内置的ResponseFuture接口，可以设置回调。但获取ResponseFuture的API使用不便，且仅支持设置回调其他异步场景均不支持，如多个Future协同工作的场景等。&lt;/li>
&lt;/ol>
&lt;h2 id="270基于completablefuture的增强">2.7.0基于CompletableFuture的增强&lt;/h2>
&lt;p>了解Java中Future演进历史的同学应该知道，Dubbo 2.6.x及之前版本中使用的Future是在java 5中引入的，所以存在以上一些功能设计上的问题，而在java 8中引入的CompletableFuture进一步丰富了Future接口，很好的解决了这些问题。&lt;/p>
&lt;p>Dubbo在2.7.0版本已经升级了对Java 8的支持，同时基于CompletableFuture对当前的异步功能进行了增强。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>支持直接定义返回CompletableFuture的服务接口。通过这种类型的接口，我们可以更自然的实现Consumer、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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>如果你不想将接口的返回值定义为Future类型，或者存在定义好的同步类型接口，则可以选择重载原始方法并为新方法定义CompletableFuture类型返回值。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 为了保证方法级服务治理规则依然有效，建议保持方法名不变: sayHi
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// 使用default实现，避免给服务端提供者带来额外实现成本
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// boolean placeHoler只是为了实现重载而增加，只要Java语法规则允许，你可以使用任何方法重载手段
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">default&lt;/span> 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">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> placeHolder&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>completedFuture&lt;span style="color:#719e07">(&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样，Provider依然可以只实现sayHi方法；而Consumer通过直接调用新增的sayHi重载方法可以拿到一个Future实例。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>如果你的原始接口定义是同步的，这时要实现Provider端异步，则可以使用AsyncContext（类似Servlet 3.0里的AsyncContext的编程接口）。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>注意：在已有CompletabeFuture返回类型的接口上，不建议再使用AsyncContext，请直接利用CompletableFuture带来的异步能力。&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>public interface AsyncService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String sayHello(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public class AsyncServiceImpl implements AsyncService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public String sayHello(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> final AsyncContext asyncContext = RpcContext.startAsync();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> new Thread(() -&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext.write(&amp;#34;Hello &amp;#34; + name + &amp;#34;, response from provider.&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }).start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return null;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在方法体的开始&lt;code>RpcContext.startAsync()&lt;/code>启动异步，并开启新线程异步的执行业务逻辑，在耗时操作完成后通过&lt;code>asyncContext.write&lt;/code>将结果写回。&lt;/p>
&lt;ol start="4">
&lt;li>
&lt;p>RpcContext直接返回CompletableFuture&lt;/p>
&lt;div class="highlight">&lt;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>CompletableFuture&amp;lt;String&amp;gt; f = RpcContext.getContext().getCompletableFuture();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>以上所有的增强，是在兼容已有异步编程的基础上进行的，因此基于2.6.x版本编写的异步程序不用做任何改造即可顺利编译通过。&lt;/p>
&lt;p>接下来，我们通过几个示例看一下如何实现一个全异步的Dubbo服务调用链。&lt;/p>
&lt;h2 id="示例1completablefuture类型接口">示例1：CompletableFuture类型接口&lt;/h2>
&lt;p>CompletableFuture类型的接口既可以用作同步调用，也可以实现Consumer或Provider的异步调用。本示例实现了Consumer和Provider端异步调用，代码参见&lt;a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-async/dubbo-samples-async-original-future">dubbo-samples-async-original-future&lt;/a>。&lt;/p>
&lt;ol>
&lt;li>
&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">interface&lt;/span> &lt;span style="color:#268bd2">AsyncService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意接口的返回类型是&lt;code>CompletableFuture&amp;lt;String&amp;gt;&lt;/code>。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Provider端&lt;/p>
&lt;ul>
&lt;li>
&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">AsyncServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> AsyncService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> 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">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>supplyAsync&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">5000&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>InterruptedException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;async response from provider.&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到这里通过supplyAsync将业务代码切换到了新的线程执行，因此实现了Provider端异步。&lt;/p>
&lt;/li>
&lt;li>
&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;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;asyncService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.async.impl.AsyncServiceImpl&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;com.alibaba.dubbo.samples.async.api.AsyncService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;asyncService&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;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Consumer端&lt;/p>
&lt;ul>
&lt;li>配置&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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> timeout=&lt;span style="color:#2aa198">&amp;#34;10000&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;/code>&lt;/pre>&lt;/div>&lt;p>​ 配置方式和普通接口是一样的。&lt;/p>
&lt;ul>
&lt;li>调用远程服务&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ClassPathXmlApplicationContext&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;META-INF/spring/async-consumer.xml&amp;#34;&lt;/span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> AsyncService asyncService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>AsyncService&lt;span style="color:#719e07">)&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;asyncService&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> asyncService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;async call request&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> future&lt;span style="color:#719e07">.&lt;/span>whenComplete&lt;span style="color:#719e07">((&lt;/span>v&lt;span style="color:#719e07">,&lt;/span> t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>t &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Response: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> v&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Executed before response return.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>CompletableFuture&amp;lt;String&amp;gt; future = asyncService.sayHello(&amp;quot;async call request&amp;quot;);&lt;/code>很自然的返回了Future示例，这样就实现了Consumer端的异步服务调用。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="示例2重载同步接口">示例2：重载同步接口&lt;/h2>
&lt;p>这个示例演示了如何在同步接口的基础上，通过增加重载方法实现消费端的异步调用，具体代码参见地址&lt;a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-async/dubbo-samples-async-generated-future">dubbo-samples-async-generated-future&lt;/a>&lt;/p>
&lt;ol>
&lt;li>
&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">@DubboAsync&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">GreetingsService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;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">interface&lt;/span> &lt;span style="color:#268bd2">GreetingsService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#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">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> isAsync&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>completedFuture&lt;span style="color:#719e07">(&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Provider端&lt;/p>
&lt;ul>
&lt;li>配置&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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;greetingsService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.async.impl.GreetingsServiceImpl&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;com.alibaba.dubbo.samples.api.GreetingsService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;greetingsService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>服务实现&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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">GreetingsServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> GreetingsService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hi, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Consumer端&lt;/p>
&lt;ul>
&lt;li>配置&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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;greetingsService&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.api.GreetingsService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>调用服务&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ClassPathXmlApplicationContext&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;META-INF/spring/async-consumer.xml&amp;#34;&lt;/span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> GreetingsService greetingsService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>GreetingsService&lt;span style="color:#719e07">)&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;greetingsService&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> greetingsService&lt;span style="color:#719e07">.&lt;/span>sayHi&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;async call reqeust&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;async call ret :&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> future&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样，我们就可以直接使用&lt;code>CompletableFuture&amp;lt;String&amp;gt; future = greetingsService.sayHi(&amp;quot;async call reqeust&amp;quot;, true);&lt;/code>，直接返回CompletableFuture。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="示例3使用asynccontext">示例3：使用AsyncContext&lt;/h2>
&lt;p>本示例演示了如何在同步接口的基础上，通过AsyncContext实现Provider端异步执行，示例代码参见&lt;a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-async/dubbo-samples-async-provider">dubbo-samples-async-provider&lt;/a>。&lt;/p>
&lt;blockquote>
&lt;p>之前已经提到过，已经是CompletableFuture签名的接口，要实现Provider端异步没必要再用AsyncContext。&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&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">interface&lt;/span> &lt;span style="color:#268bd2">AsyncService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Provider端，和普通provider端配置完全一致&lt;/p>
&lt;ul>
&lt;li>配置&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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;asyncService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.async.impl.AsyncServiceImpl&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> async=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.async.api.AsyncService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;asyncService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>异步执行实现&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> AsyncContext asyncContext &lt;span style="color:#719e07">=&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>startAsync&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Thread&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext&lt;span style="color:#719e07">.&lt;/span>signalContextSwitch&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">500&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>InterruptedException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext&lt;span style="color:#719e07">.&lt;/span>write&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}).&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Consumer端&lt;/p>
&lt;ul>
&lt;li>配置&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>服务调用&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ClassPathXmlApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ClassPathXmlApplicationContext&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;META-INF/spring/async-consumer.xml&amp;#34;&lt;/span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AsyncService asyncService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>AsyncService&lt;span style="color:#719e07">)&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;asyncService&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>asyncService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;async call request&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="异步引入的新问题">异步引入的新问题&lt;/h2>
&lt;h3 id="filter链">Filter链&lt;/h3>
&lt;p>以下是一次普通Dubbo调用的完整Filter链(Filter链路图待补充)。&lt;/p>
&lt;p>而采用异步调用后，由于异步结果在异步线程中单独执行，所以流经后半段Filter链的Result是空值，当真正的结果返回时已无法被Filter链处理。&lt;/p>
&lt;p>为了解决这个问题，2.7.0中为Filter增加了回调接口onResponse。&lt;/p>
&lt;p>以下是一个扩展Filter并支持异步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:#268bd2">@Activate&lt;/span>&lt;span style="color:#719e07">(&lt;/span>group &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">{&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>PROVIDER&lt;span style="color:#719e07">,&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>CONSUMER&lt;span style="color:#719e07">})&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AsyncPostprocessFilter&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Filter &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&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> Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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> Result &lt;span style="color:#268bd2">onResponse&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Result result&lt;span style="color:#719e07">,&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Filter get the return value: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> result&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="上下文传递">上下文传递&lt;/h3>
&lt;p>这里的上下文问题主要是指在提供端异步的场景。&lt;/p>
&lt;p>当前我们考虑的上下文主要是指保存在RpcContext中的数据，大多数场景是需要用户在切换业务线程前自己完成Context的传递。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 保存当前线程的上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RpcContext context &lt;span style="color:#719e07">=&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> 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">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>supplyAsync&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 设置到新线程中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>setContext&lt;span style="color:#719e07">(&lt;/span>context&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">5000&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>InterruptedException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;async response from provider.&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>不过AsyncContext也提供了signalContextSwitch()的方法来实现方便的Context切换。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> AsyncContext asyncContext &lt;span style="color:#719e07">=&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>startAsync&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Thread&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext&lt;span style="color:#719e07">.&lt;/span>signalContextSwitch&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">500&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>InterruptedException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncContext&lt;span style="color:#719e07">.&lt;/span>write&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}).&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: Dubbo 集群容错</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;div class="highlight">&lt;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;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>
&lt;h4 id="failfast快速失败">Failfast(快速失败)&lt;/h4>
&lt;p>某些业务场景中，某些操作可能是非幂等的，如果重复发起调用，可能会导致出现脏数据等。例如调用某个服务，其中包含一个数据库的写操作，如果写操作完成，但是在发送结果给调用方的过程中出错了，那么在调用发看来这次调用失败了，但其实数据写入已经完成。这种情况下，重试可能并不是一个好策略，这时候就需要使用到&lt;code>Failfast&lt;/code>策略，调用失败立即报错。让调用方来决定下一步的操作并保证业务的幂等性。&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;failfast&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;failfast&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;h4 id="failback失败自动恢复">Failback(失败自动恢复)&lt;/h4>
&lt;p>&lt;code>Failback&lt;/code>通常和&lt;code>Failover&lt;/code>两个概念联系在一起。在高可用系统中，当主机发生故障，通过&lt;code>Failover&lt;/code>进行主备切换后，待故障恢复后，系统应该具备自动恢复原始配置的能力。&lt;/p>
&lt;p>Dubbo中的&lt;code>Failback&lt;/code>策略中，如果调用失败，则此次失败相当于&lt;code>Failsafe&lt;/code>，将返回一个空结果。而与&lt;code>Failsafe&lt;/code>不同的是，Failback策略会将这次调用加入内存中的失败列表中，对于这个列表中的失败调用，会在另一个线程中进行异步重试，重试如果再发生失败，则会忽略，即使重试调用成功，原来的调用方也感知不到了。因此它通常适合于，对于实时性要求不高，且不需要返回值的一些异步操作。&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>
&lt;div class="highlight">&lt;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;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>
&lt;p>按照目前的实现，Failback策略还有一些局限，例如内存中的失败调用列表没有上限，可能导致堆积，异步重试的执行间隔无法调整，默认是5秒。&lt;/p>
&lt;h4 id="forking并行调用">Forking(并行调用)&lt;/h4>
&lt;p>上述几种策略中，主要都是针对调用失败发生后如何进行弥补的角度去考虑的，而&lt;code>Forking&lt;/code>策略则跟上述几种策略不同，是一种典型的用成本换时间的思路。即第一次调用的时候就同时发起多个调用，只要其中一个调用成功，就认为成功。在资源充足，且对于失败的容忍度较低的场景下，可以采用此策略。&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;forking&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;forking&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;h4 id="broadcast广播调用">Broadcast(广播调用)&lt;/h4>
&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;broadcast&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;broadcast&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;h4 id="各种策略对比">各种策略对比&lt;/h4>
&lt;p>下表对各种策略做一个简单对比，&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>策略名称&lt;/th>
&lt;th>优点&lt;/th>
&lt;th>缺点&lt;/th>
&lt;th>主要应用场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Failover&lt;/td>
&lt;td>对调用者屏蔽调用失败的信息&lt;/td>
&lt;td>增加RT，额外资源开销，资源浪费&lt;/td>
&lt;td>对调用rt不敏感的场景&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Failfast&lt;/td>
&lt;td>业务快速感知失败状态进行自主决策&lt;/td>
&lt;td>产生较多报错的信息&lt;/td>
&lt;td>非幂等性操作，需要快速感知失败的场景&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Failsafe&lt;/td>
&lt;td>即使失败了也不会影响核心流程&lt;/td>
&lt;td>对于失败的信息不敏感，需要额外的监控&lt;/td>
&lt;td>旁路系统，失败不影响核心流程正确性的场景&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Failback&lt;/td>
&lt;td>失败自动异步重试&lt;/td>
&lt;td>重试任务可能堆积&lt;/td>
&lt;td>对于实时性要求不高，且不需要返回值的一些异步操作&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Forking&lt;/td>
&lt;td>并行发起多个调用，降低失败概率&lt;/td>
&lt;td>消耗额外的机器资源，需要确保操作幂等性&lt;/td>
&lt;td>资源充足，且对于失败的容忍度较低，实时性要求高的场景&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Broadcast&lt;/td>
&lt;td>支持对所有的服务提供者进行操作&lt;/td>
&lt;td>资源消耗很大&lt;/td>
&lt;td>通知所有提供者更新缓存或日志等本地资源信息&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="自定义容错策略">自定义容错策略&lt;/h3>
&lt;p>如果上述内置的容错策略无法满足你的需求，还可以通过扩展的方式来实现自定义容错策略。&lt;/p>
&lt;h4 id="扩展接口">扩展接口&lt;/h4>
&lt;p>&lt;code>com.alibaba.dubbo.rpc.cluster.Cluster&lt;/code>&lt;/p>
&lt;h4 id="扩展配置">扩展配置&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> cluster=&lt;span style="color:#2aa198">&amp;#34;xxx&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="扩展示例">扩展示例&lt;/h4>
&lt;p>下面通过一个例子来展示如何使用自定义的容错策略。
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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>src
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-com
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-xxx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-XxxCluster.java (实现Cluster接口)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-resources
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-META-INF
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-org.apache.dubbo.rpc.cluster.Cluster (纯文本文件，内容为：xxx=com.xxx.XxxCluster)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>XxxCluster.java：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.xxx&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.cluster.Cluster&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.cluster.Directory&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.cluster.LoadBalance&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Invocation&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.Result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.RpcException&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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.List&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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">XxxCluster&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Cluster &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&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:#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">join&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Directory&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> directory&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AbstractClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> Result &lt;span style="color:#268bd2">doInvoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invocation invocation&lt;span style="color:#719e07">,&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&lt;span style="color:#719e07">,&lt;/span> LoadBalance loadbalance&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// your custimzed fault tolarence strategy goes here
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster&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>xxx=com.xxx.XxxCluster
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: Dubbo 现有心跳方案总结以及改进建议</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>&lt;span style="color:#719e07">(&lt;/span>Channel channel&lt;span style="color:#719e07">,&lt;/span> Request request&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> timeout&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> DefaultFuture future &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DefaultFuture&lt;span style="color:#719e07">(&lt;/span>channel&lt;span style="color:#719e07">,&lt;/span> request&lt;span style="color:#719e07">,&lt;/span> timeout&lt;span style="color:#719e07">);&lt;/span>
&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>&lt;span style="color:#586e75">&lt;/span> timeoutCheck&lt;span style="color:#719e07">(&lt;/span>future&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> future&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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>&lt;span style="color:#719e07">(&lt;/span>DefaultFuture future&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&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&lt;span style="color:#719e07">(&lt;/span>future&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TIME_OUT_TIMER&lt;span style="color:#719e07">.&lt;/span>newTimeout&lt;span style="color:#719e07">(&lt;/span>task&lt;span style="color:#719e07">,&lt;/span> future&lt;span style="color:#719e07">.&lt;/span>getTimeout&lt;span style="color:#719e07">(),&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DefaultFuture future&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TimeoutCheckTask&lt;span style="color:#719e07">(&lt;/span>DefaultFuture future&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>future &lt;span style="color:#719e07">=&lt;/span> future&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">run&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Timeout timeout&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>future &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> future&lt;span style="color:#719e07">.&lt;/span>isDone&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// create exception response.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Response timeoutResponse &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Response&lt;span style="color:#719e07">(&lt;/span>future&lt;span style="color:#719e07">.&lt;/span>getId&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// set timeout status.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> timeoutResponse&lt;span style="color:#719e07">.&lt;/span>setStatus&lt;span style="color:#719e07">(&lt;/span>future&lt;span style="color:#719e07">.&lt;/span>isSent&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">?&lt;/span> Response&lt;span style="color:#719e07">.&lt;/span>SERVER_TIMEOUT &lt;span style="color:#719e07">:&lt;/span> Response&lt;span style="color:#719e07">.&lt;/span>CLIENT_TIMEOUT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timeoutResponse&lt;span style="color:#719e07">.&lt;/span>setErrorMessage&lt;span style="color:#719e07">(&lt;/span>future&lt;span style="color:#719e07">.&lt;/span>getTimeoutMessage&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// handle response.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> DefaultFuture&lt;span style="color:#719e07">.&lt;/span>received&lt;span style="color:#719e07">(&lt;/span>future&lt;span style="color:#719e07">.&lt;/span>getChannel&lt;span style="color:#719e07">(),&lt;/span> timeoutResponse&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>主要逻辑涉及的类：&lt;code>DubboInvoker&lt;/code>，&lt;code>HeaderExchangeChannel&lt;/code>，&lt;code>DefaultFuture&lt;/code> ，通过上述代码，我们可以得知一个细节，无论是何种调用，都会经过这个定时器的检测，&lt;strong>超时即调用失败，一次 RPC 调用的失败，必须以客户端收到失败响应为准&lt;/strong>。&lt;/p>
&lt;h4 id="22-心跳检测需要容错">2.2 心跳检测需要容错&lt;/h4>
&lt;p>网络通信永远要考虑到最坏的情况，一次心跳失败，不能认定为连接不通，多次心跳失败，才能采取相应的措施。&lt;/p>
&lt;h4 id="23-心跳检测不需要忙检测">2.3 心跳检测不需要忙检测&lt;/h4>
&lt;p>忙检测的对立面是空闲检测，我们做心跳的初衷，是为了保证连接的可用性，以保证及时采取断连，重连等措施。如果一条通道上有频繁的 RPC 调用正在进行，我们不应该为通道增加负担去发送心跳包。&lt;strong>心跳扮演的角色应当是晴天收伞，雨天送伞。&lt;/strong>&lt;/p>
&lt;h3 id="3-dubbo-现有方案">3 Dubbo 现有方案&lt;/h3>
&lt;blockquote>
&lt;p>本文的源码对应 Dubbo 2.7.x 版本，在 apache 孵化的该版本中，心跳机制得到了增强。&lt;/p>
&lt;/blockquote>
&lt;p>介绍完了一些基础的概念，我们再来看看 Dubbo 是如何设计应用层心跳的。Dubbo 的心跳是双向心跳，客户端会给服务端发送心跳，反之，服务端也会向客户端发送心跳。&lt;/p>
&lt;h4 id="31-连接建立时创建定时器">3.1 连接建立时创建定时器&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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">HeaderExchangeClient&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> ExchangeClient &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> heartbeat&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> heartbeatTimeout&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> HashedWheelTimer heartbeatTimer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">HeaderExchangeClient&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Client client&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> needHeartbeat&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>client &lt;span style="color:#719e07">=&lt;/span> client&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>channel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HeaderExchangeChannel&lt;span style="color:#719e07">(&lt;/span>client&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>heartbeat &lt;span style="color:#719e07">=&lt;/span> client&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>HEARTBEAT_KEY&lt;span style="color:#719e07">,&lt;/span> dubbo &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> dubbo&lt;span style="color:#719e07">.&lt;/span>startsWith&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;1.0.&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">?&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_HEARTBEAT &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>heartbeatTimeout &lt;span style="color:#719e07">=&lt;/span> client&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>HEARTBEAT_TIMEOUT_KEY&lt;span style="color:#719e07">,&lt;/span> heartbeat &lt;span style="color:#719e07">*&lt;/span> &lt;span style="color:#2aa198">3&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>needHeartbeat&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">long&lt;/span> tickDuration &lt;span style="color:#719e07">=&lt;/span> calculateLeastDuration&lt;span style="color:#719e07">(&lt;/span>heartbeat&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> heartbeatTimer &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashedWheelTimer&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> NamedThreadFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-client-heartbeat&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">),&lt;/span> tickDuration&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">,&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>TICKS_PER_WHEEL&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>&lt;span style="color:#2aa198">2&lt;/span>&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startHeartbeatTimer&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&amp;lt;1&amp;gt; &lt;strong>默认开启心跳检测的定时器&lt;/strong>&lt;/p>
&lt;p>&amp;lt;2&amp;gt; &lt;strong>创建了一个 &lt;code>HashedWheelTimer &lt;/code> 开启心跳检测&lt;/strong>，这是 Netty 所提供的一个经典的时间轮定时器实现，至于它和 jdk 的实现有何不同，不了解的同学也可以关注下，我就不拓展了。&lt;/p>
&lt;p>不仅 &lt;code>HeaderExchangeClient&lt;/code> 客户端开起了定时器，&lt;code>HeaderExchangeServer&lt;/code> 服务端同样开起了定时器，由于服务端的逻辑和客户端几乎一致，所以后续我并不会重复粘贴服务端的代码。&lt;/p>
&lt;blockquote>
&lt;p>Dubbo 在早期版本版本中使用的是 schedule 方案，在 2.7.x 中替换成了 HashedWheelTimer。&lt;/p>
&lt;/blockquote>
&lt;h4 id="32-开启两个定时任务">3.2 开启两个定时任务&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startHeartbeatTimer&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">long&lt;/span> heartbeatTick &lt;span style="color:#719e07">=&lt;/span> calculateLeastDuration&lt;span style="color:#719e07">(&lt;/span>heartbeat&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">long&lt;/span> heartbeatTimeoutTick &lt;span style="color:#719e07">=&lt;/span> calculateLeastDuration&lt;span style="color:#719e07">(&lt;/span>heartbeatTimeout&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HeartbeatTimerTask heartBeatTimerTask &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HeartbeatTimerTask&lt;span style="color:#719e07">(&lt;/span>cp&lt;span style="color:#719e07">,&lt;/span> heartbeatTick&lt;span style="color:#719e07">,&lt;/span> heartbeat&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReconnectTimerTask reconnectTimerTask &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReconnectTimerTask&lt;span style="color:#719e07">(&lt;/span>cp&lt;span style="color:#719e07">,&lt;/span> heartbeatTimeoutTick&lt;span style="color:#719e07">,&lt;/span> heartbeatTimeout&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>&lt;span style="color:#2aa198">2&lt;/span>&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> heartbeatTimer&lt;span style="color:#719e07">.&lt;/span>newTimeout&lt;span style="color:#719e07">(&lt;/span>heartBeatTimerTask&lt;span style="color:#719e07">,&lt;/span> heartbeatTick&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> heartbeatTimer&lt;span style="color:#719e07">.&lt;/span>newTimeout&lt;span style="color:#719e07">(&lt;/span>reconnectTimerTask&lt;span style="color:#719e07">,&lt;/span> heartbeatTimeoutTick&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo 在 &lt;code>startHeartbeatTimer&lt;/code> 方法中主要开启了两个定时器： &lt;code>HeartbeatTimerTask&lt;/code>，&lt;code>ReconnectTimerTask&lt;/code>&lt;/p>
&lt;p>&amp;lt;1&amp;gt; &lt;code>HeartbeatTimerTask&lt;/code> 主要用于定时发送心跳请求&lt;/p>
&lt;p>&amp;lt;2&amp;gt; &lt;code>ReconnectTimerTask&lt;/code> 主要用于心跳失败之后处理重连，断连的逻辑&lt;/p>
&lt;p>至于方法中的其他代码，其实也是本文的重要分析内容，先容我卖个关子，后面再来看追溯。&lt;/p>
&lt;h4 id="33-定时任务一发送心跳请求">3.3 定时任务一：发送心跳请求&lt;/h4>
&lt;p>详细解析下心跳检测定时任务的逻辑 &lt;code>HeartbeatTimerTask#doTask&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">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doTask&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Channel channel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Long lastRead &lt;span style="color:#719e07">=&lt;/span> lastRead&lt;span style="color:#719e07">(&lt;/span>channel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Long lastWrite &lt;span style="color:#719e07">=&lt;/span> lastWrite&lt;span style="color:#719e07">(&lt;/span>channel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">((&lt;/span>lastRead &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> now&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&lt;/span> lastRead &lt;span style="color:#719e07">&amp;gt;&lt;/span> heartbeat&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">||&lt;/span> &lt;span style="color:#719e07">(&lt;/span>lastWrite &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> now&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&lt;/span> lastWrite &lt;span style="color:#719e07">&amp;gt;&lt;/span> heartbeat&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Request req &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Request&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req&lt;span style="color:#719e07">.&lt;/span>setVersion&lt;span style="color:#719e07">(&lt;/span>Version&lt;span style="color:#719e07">.&lt;/span>getProtocolVersion&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req&lt;span style="color:#719e07">.&lt;/span>setTwoWay&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req&lt;span style="color:#719e07">.&lt;/span>setEvent&lt;span style="color:#719e07">(&lt;/span>Request&lt;span style="color:#719e07">.&lt;/span>HEARTBEAT_EVENT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> channel&lt;span style="color:#719e07">.&lt;/span>send&lt;span style="color:#719e07">(&lt;/span>req&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前面已经介绍过，&lt;strong>Dubbo 采取的是双向心跳设计&lt;/strong>，即服务端会向客户端发送心跳，客户端也会向服务端发送心跳，接收的一方更新 lastRead 字段，发送的一方更新 lastWrite 字段，超过心跳间隙的时间，便发送心跳请求给对端。这里的 lastRead/lastWrite 同样会被同一个通道上的普通调用更新，通过更新这两个字段，实现了只在连接空闲时才会真正发送空闲报文的机制，符合我们一开始科普的做法。&lt;/p>
&lt;blockquote>
&lt;p>注意：不仅仅心跳请求会更新 lastRead 和 lastWrite，普通请求也会。这对应了我们预备知识中的空闲检测机制。&lt;/p>
&lt;/blockquote>
&lt;h4 id="34-定时任务二处理重连和断连">3.4 定时任务二：处理重连和断连&lt;/h4>
&lt;p>继续研究下重连和断连定时器都实现了什么 &lt;code>ReconnectTimerTask#doTask&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">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doTask&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Channel channel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Long lastRead &lt;span style="color:#719e07">=&lt;/span> lastRead&lt;span style="color:#719e07">(&lt;/span>channel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Long now &lt;span style="color:#719e07">=&lt;/span> now&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>lastRead &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> now &lt;span style="color:#719e07">-&lt;/span> lastRead &lt;span style="color:#719e07">&amp;gt;&lt;/span> heartbeatTimeout&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>channel &lt;span style="color:#719e07">instanceof&lt;/span> Client&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">((&lt;/span>Client&lt;span style="color:#719e07">)&lt;/span> channel&lt;span style="color:#719e07">).&lt;/span>reconnect&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> channel&lt;span style="color:#719e07">.&lt;/span>close&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>第二个定时器则负责根据客户端、服务端类型来对连接做不同的处理，当超过设置的心跳总时间之后，客户端选择的是重新连接，服务端则是选择直接断开连接。这样的考虑是合理的，客户端调用是强依赖可用连接的，而服务端可以等待客户端重新建立连接。&lt;/p>
&lt;blockquote>
&lt;p>细心的朋友会发现，这个类被命名为 ReconnectTimerTask 是不太准确的，因为它处理的是重连和断连两个逻辑。&lt;/p>
&lt;/blockquote>
&lt;h4 id="35-定时不精确的问题">3.5 定时不精确的问题&lt;/h4>
&lt;p>在 Dubbo 的 issue 中曾经有人反馈过定时不精确的问题，我们来看看是怎么一回事。&lt;/p>
&lt;p>Dubbo 中默认的心跳周期是 60s，设想如下的时序：&lt;/p>
&lt;ul>
&lt;li>第 0 秒，心跳检测发现连接活跃&lt;/li>
&lt;li>第 1 秒，连接实际断开&lt;/li>
&lt;li>第 60 秒，心跳检测发现连接不活跃&lt;/li>
&lt;/ul>
&lt;p>由于&lt;strong>时间窗口的问题，死链不能够被及时检测出来，最坏情况为一个心跳周期&lt;/strong>。&lt;/p>
&lt;p>为了解决上述问题，我们再倒回去看一下上面的 &lt;code>startHeartbeatTimer()&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:#dc322f">long&lt;/span> heartbeatTick &lt;span style="color:#719e07">=&lt;/span> calculateLeastDuration&lt;span style="color:#719e07">(&lt;/span>heartbeat&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#dc322f">long&lt;/span> heartbeatTimeoutTick &lt;span style="color:#719e07">=&lt;/span> calculateLeastDuration&lt;span style="color:#719e07">(&lt;/span>heartbeatTimeout&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中 &lt;code>calculateLeastDuration&lt;/code> 根据心跳时间和超时时间分别计算出了一个 tick 时间，实际上就是将两个变量除以了 3，使得他们的值缩小，并传入了 &lt;code>HashedWheelTimer&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>heartbeatTimer&lt;span style="color:#719e07">.&lt;/span>newTimeout&lt;span style="color:#719e07">(&lt;/span>heartBeatTimerTask&lt;span style="color:#719e07">,&lt;/span> heartbeatTick&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>heartbeatTimer&lt;span style="color:#719e07">.&lt;/span>newTimeout&lt;span style="color:#719e07">(&lt;/span>reconnectTimerTask&lt;span style="color:#719e07">,&lt;/span> heartbeatTimeoutTick&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>tick 的含义便是定时任务执行的频率。这样，通过减少检测间隔时间，增大了及时发现死链的概率，原先的最坏情况是 60s，如今变成了 20s。这个频率依旧可以加快，但需要考虑资源消耗的问题。&lt;/p>
&lt;blockquote>
&lt;p>定时不准确的问题出现在 Dubbo 的两个定时任务之中，所以都做了 tick 操作。事实上，所有的定时检测的逻辑都存在类似的问题。&lt;/p>
&lt;/blockquote>
&lt;h4 id="36-dubbo-心跳总结">3.6 Dubbo 心跳总结&lt;/h4>
&lt;p>Dubbo 对于建立的每一个连接，同时在客户端和服务端开启了 2 个定时器，一个用于定时发送心跳，一个用于定时重连、断连，执行的频率均为各自检测周期的 1/3。定时发送心跳的任务负责在连接空闲时，向对端发送心跳包。定时重连、断连的任务负责检测 lastRead 是否在超时周期内仍未被更新，如果判定为超时，客户端处理的逻辑是重连，服务端则采取断连的措施。&lt;/p>
&lt;p>先不急着判断这个方案好不好，再来看看改进方案是怎么设计的。&lt;/p>
&lt;h3 id="4-dubbo-改进方案">4 Dubbo 改进方案&lt;/h3>
&lt;p>实际上我们可以更优雅地实现心跳机制，本小节开始，我将介绍一个新的心跳机制。&lt;/p>
&lt;h4 id="41-idlestatehandler-介绍">4.1 IdleStateHandler 介绍&lt;/h4>
&lt;p>Netty 对空闲连接的检测提供了天然的支持，使用 &lt;code>IdleStateHandler&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">IdleStateHandler&lt;/span>&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">long&lt;/span> readerIdleTime&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">long&lt;/span> writerIdleTime&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">long&lt;/span> allIdleTime&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TimeUnit unit&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>readerIdleTime：读超时时间&lt;/li>
&lt;li>writerIdleTime：写超时时间&lt;/li>
&lt;li>allIdleTime：所有类型的超时时间&lt;/li>
&lt;/ul>
&lt;p>&lt;code>IdleStateHandler&lt;/code> 这个类会根据设置的超时参数，循环检测 channelRead 和 write 方法多久没有被调用。当在 pipeline 中加入 &lt;code>IdleSateHandler&lt;/code> 之后，可以在此 pipeline 的任意 Handler 的 &lt;code>userEventTriggered&lt;/code> 方法之中检测 &lt;code>IdleStateEvent&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">@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">userEventTriggered&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ChannelHandlerContext ctx&lt;span style="color:#719e07">,&lt;/span> Object evt&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>evt &lt;span style="color:#719e07">instanceof&lt;/span> IdleStateEvent&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//do something
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ctx&lt;span style="color:#719e07">.&lt;/span>fireUserEventTriggered&lt;span style="color:#719e07">(&lt;/span>evt&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>为什么需要介绍 &lt;code>IdleStateHandler&lt;/code> 呢？其实提到它的空闲检测 + 定时的时候，大家应该能够想到了，这不天然是给心跳机制服务的吗？很多服务治理框架都选择了借助 &lt;code>IdleStateHandler&lt;/code> 来实现心跳。&lt;/p>
&lt;blockquote>
&lt;p>IdleStateHandler 内部使用了 eventLoop.schedule(task) 的方式来实现定时任务，使用 eventLoop 线程的好处是还同时保证了&lt;strong>线程安全&lt;/strong>，这里是一个小细节。&lt;/p>
&lt;/blockquote>
&lt;h4 id="42-客户端和服务端配置">4.2 客户端和服务端配置&lt;/h4>
&lt;p>首先是将 &lt;code>IdleStateHandler&lt;/code> 加入 pipeline 中。&lt;/p>
&lt;p>&lt;strong>客户端：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>bootstrap&lt;span style="color:#719e07">.&lt;/span>handler&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ChannelInitializer&lt;span style="color:#719e07">&amp;lt;&lt;/span>NioSocketChannel&lt;span style="color:#719e07">&amp;gt;()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initChannel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>NioSocketChannel ch&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ch&lt;span style="color:#719e07">.&lt;/span>pipeline&lt;span style="color:#719e07">().&lt;/span>addLast&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;clientIdleHandler&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IdleStateHandler&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">60&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>服务端：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>serverBootstrap&lt;span style="color:#719e07">.&lt;/span>childHandler&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ChannelInitializer&lt;span style="color:#719e07">&amp;lt;&lt;/span>NioSocketChannel&lt;span style="color:#719e07">&amp;gt;()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initChannel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>NioSocketChannel ch&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ch&lt;span style="color:#719e07">.&lt;/span>pipeline&lt;span style="color:#719e07">().&lt;/span>addLast&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;serverIdleHandler&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>&lt;span style="color:#719e07">new&lt;/span> IdleStateHandler&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">200&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>客户端配置了 read 超时为 60s，服务端配置了 write/read 超时为 200s，先在此埋下两个伏笔：&lt;/p>
&lt;ol>
&lt;li>为什么客户端和服务端配置的超时时间不一致？&lt;/li>
&lt;li>为什么客户端检测的是读超时，而服务端检测的是读写超时？&lt;/li>
&lt;/ol>
&lt;h4 id="43-空闲超时逻辑--客户端">4.3 空闲超时逻辑 — 客户端&lt;/h4>
&lt;p>对于空闲超时的处理逻辑，客户端和服务端是不同的。首先来看客户端&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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">userEventTriggered&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ChannelHandlerContext ctx&lt;span style="color:#719e07">,&lt;/span> Object evt&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>evt &lt;span style="color:#719e07">instanceof&lt;/span> IdleStateEvent&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// send heartbeat
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> sendHeartBeat&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>userEventTriggered&lt;span style="color:#719e07">(&lt;/span>ctx&lt;span style="color:#719e07">,&lt;/span> evt&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>检测到空闲超时之后，采取的行为是向服务端发送心跳包，具体是如何发送，以及处理响应的呢？伪代码如下&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">sendHeartBeat&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Invocation invocation &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Invocation&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invocation&lt;span style="color:#719e07">.&lt;/span>setInvocationType&lt;span style="color:#719e07">(&lt;/span>InvocationType&lt;span style="color:#719e07">.&lt;/span>HEART_BEAT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> channel&lt;span style="color:#719e07">.&lt;/span>writeAndFlush&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">).&lt;/span>addListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> CallbackFuture&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">callback&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Future future&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RPCResult result &lt;span style="color:#719e07">=&lt;/span> future&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//超时 或者 写失败
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>result&lt;span style="color:#719e07">.&lt;/span>isError&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> channel&lt;span style="color:#719e07">.&lt;/span>addFailedHeartBeatTimes&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>channel&lt;span style="color:#719e07">.&lt;/span>getFailedHeartBeatTimes&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;=&lt;/span> channel&lt;span style="color:#719e07">.&lt;/span>getMaxHeartBeatFailedTimes&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> channel&lt;span style="color:#719e07">.&lt;/span>reconnect&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> channel&lt;span style="color:#719e07">.&lt;/span>clearHeartBeatFailedTimes&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>行为并不复杂，构造一个心跳包发送到服务端，接受响应结果&lt;/p>
&lt;ul>
&lt;li>响应成功，清空请求失败标记&lt;/li>
&lt;li>响应失败，心跳失败标记+1，如果超过配置的失败次数，则重新连接&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>不仅仅是心跳，普通请求返回成功响应时也会清空标记&lt;/p>
&lt;/blockquote>
&lt;h4 id="44-空闲超时逻辑--服务端">4.4 空闲超时逻辑 — 服务端&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@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">userEventTriggered&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ChannelHandlerContext ctx&lt;span style="color:#719e07">,&lt;/span> Object evt&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>evt &lt;span style="color:#719e07">instanceof&lt;/span> IdleStateEvent&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> channel&lt;span style="color:#719e07">.&lt;/span>close&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>userEventTriggered&lt;span style="color:#719e07">(&lt;/span>ctx&lt;span style="color:#719e07">,&lt;/span> evt&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>服务端处理空闲连接的方式非常简单粗暴，直接关闭连接。&lt;/p>
&lt;h4 id="45-改进方案心跳总结">4.5 改进方案心跳总结&lt;/h4>
&lt;ol>
&lt;li>
&lt;p>为什么客户端和服务端配置的超时时间不一致？&lt;/p>
&lt;p>因为客户端有重试逻辑，不断发送心跳失败 n 次之后，才认为是连接断开；而服务端是直接断开，留给服务端时间得长一点。60 * 3 &amp;lt; 200 还说明了一个问题，双方都拥有断开连接的能力，但连接的创建是由客户端主动发起的，那么客户端也更有权利去主动断开连接。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>为什么客户端检测的是读超时，而服务端检测的是读写超时？&lt;/p>
&lt;p>这其实是一个心跳的共识了，仔细思考一下，定时逻辑是由客户端发起的，所以整个链路中不通的情况只有可能是：服务端接收，服务端发送，客户端接收。也就是说，只有客户端的 pong，服务端的 ping，pong 的检测是有意义的。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>主动追求别人的是你，主动说分手的也是你。&lt;/p>
&lt;/blockquote>
&lt;p>利用 &lt;code>IdleStateHandler&lt;/code> 实现心跳机制可以说是十分优雅的，借助 Netty 提供的空闲检测机制，利用客户端维护单向心跳，在收到 3 次心跳失败响应之后，客户端断开连接，交由异步线程重连，本质还是表现为客户端重连。服务端在连接空闲较长时间后，主动断开连接，以避免无谓的资源浪费。&lt;/p>
&lt;h3 id="5-心跳设计方案对比">5 心跳设计方案对比&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:center">&lt;/th>
&lt;th style="text-align:center">Dubbo 现有方案&lt;/th>
&lt;th style="text-align:center">Dubbo 改进方案&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:center">&lt;strong>主体设计&lt;/strong>&lt;/td>
&lt;td style="text-align:center">开启两个定时器&lt;/td>
&lt;td style="text-align:center">借助 IdleStateHandler，底层使用 schedule&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">&lt;strong>心跳方向&lt;/strong>&lt;/td>
&lt;td style="text-align:center">双向&lt;/td>
&lt;td style="text-align:center">单向（客户端 -&amp;gt; 服务端）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">&lt;strong>心跳失败判定方式&lt;/strong>&lt;/td>
&lt;td style="text-align:center">心跳成功更新标记，借助定时器定时扫描标记，如果超过心跳超时周期未更新标记，认为心跳失败。&lt;/td>
&lt;td style="text-align:center">通过判断心跳响应是否失败，超过失败次数，认为心跳失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">&lt;strong>扩展性&lt;/strong>&lt;/td>
&lt;td style="text-align:center">Dubbo 存在 mina，grizzy 等其他通信层实现，自定义定时器很容易适配多种扩展&lt;/td>
&lt;td style="text-align:center">多通信层各自实现心跳，不做心跳的抽象&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">&lt;strong>设计性&lt;/strong>&lt;/td>
&lt;td style="text-align:center">编码复杂度高，代码量大，方案复杂，不易维护&lt;/td>
&lt;td style="text-align:center">编码量小，可维护性强&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>私下请教过&lt;strong>美团点评的长连接负责人：俞超（闪电侠）&lt;/strong>，美点使用的心跳方案和 Dubbo 改进方案几乎一致，可以说该方案是标准实现了。&lt;/p>
&lt;h3 id="6-dubbo-实际改动点建议">6 Dubbo 实际改动点建议&lt;/h3>
&lt;p>鉴于 Dubbo 存在一些其他通信层的实现，所以可以保留现有的定时发送心跳的逻辑。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>建议改动点一：&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>双向心跳的设计是不必要的，兼容现有的逻辑，可以让客户端在连接空闲时发送单向心跳，服务端定时检测连接可用性。定时时间尽量保证：客户端超时时间 * 3 ≈ 服务端超时时间&lt;/p>
&lt;ul>
&lt;li>&lt;strong>建议改动点二：&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>去除处理重连和断连的定时任务，Dubbo 可以判断心跳请求是否响应失败，可以借鉴改进方案的设计，在连接级别维护一个心跳失败次数的标记，任意响应成功，清除标记；连续心跳失败 n 次，客户端发起重连。这样可以减少一个不必要的定时器，任何轮询的方式，都是不优雅的。&lt;/p>
&lt;p>最后再聊聊可扩展性这个话题。其实我是建议把定时器交给更加底层的 Netty 去做，也就是完全使用 &lt;code>IdleStateHandler&lt;/code> ，其他通信层组件各自实现自己的空闲检测逻辑，但是 Dubbo 中 mina，grizzy 的兼容问题囿住了我的拳脚，但试问一下，如今的 2019 年，又有多少人在使用 mina 和 grizzy？因为一些不太可能用的特性，而限制了主流用法的优化，这肯定不是什么好事。抽象，功能，可扩展性并不是越多越好，开源产品的人力资源是有限的，框架使用者的理解能力也是有限的，能解决大多数人问题的设计，才是好的设计。哎，谁让我不会 mina，grizzy，还懒得去学呢[摊手]。&lt;/p></description></item><item><title>Blog: Dubbo2.7 三大新特性详解</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/270/branches.png" alt="分支">&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>
&lt;/blockquote>
&lt;h2 id="3-dubbo-27-新特性">3 Dubbo 2.7 新特性&lt;/h2>
&lt;p>Dubbo 2.7.x 作为 Apache 的孵化版本，除了代码优化之外，还新增了许多重磅的新特性，本文将会介绍其中最典型的三个新特性：&lt;/p>
&lt;ul>
&lt;li>异步化改造&lt;/li>
&lt;li>三大中心改造&lt;/li>
&lt;li>服务治理增强&lt;/li>
&lt;/ul>
&lt;h2 id="4-异步化改造">4 异步化改造&lt;/h2>
&lt;h3 id="41-几种调用方式">4.1 几种调用方式&lt;/h3>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/270/invokes.png" alt="调用方式">&lt;/p>
&lt;p>在远程方法调用中，大致可以分为这 4 种调用方式。oneway 指的是客户端发送消息后，不需要接受响应。对于那些不关心服务端响应的请求，比较适合使用 oneway 通信。&lt;/p>
&lt;blockquote>
&lt;p>注意，void hello() 方法在远程方法调用中，不属于 oneway 调用，虽然 void 方法表达了不关心返回值的语义，但在 RPC 层面，仍然需要做通信层的响应。&lt;/p>
&lt;/blockquote>
&lt;p>sync 是最常用的通信方式，也是默认的通信方法。&lt;/p>
&lt;p>future 和 callback 都属于异步调用的范畴，他们的区别是：在接收响应时，future.get() 会导致线程的阻塞;callback 通常会设置一个回调线程，当接收到响应时，自动执行，不会对当前线程造成阻塞。&lt;/p>
&lt;h3 id="42-dubbo-26-异步化">4.2 Dubbo 2.6 异步化&lt;/h3>
&lt;p>异步化的优势在于客户端不需要启动多线程即可完成并行调用多个远程服务，相对多线程开销较小。介绍 2.7 中的异步化改造之前，先回顾一下如何在 2.6 中使用 Dubbo 异步化的能力。&lt;/p>
&lt;ol>
&lt;li>将同步接口声明成 &lt;code>async=true&lt;/code>
&lt;div class="highlight">&lt;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;org.apache.dubbo.demo.api.AsyncService&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;/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">interface&lt;/span> &lt;span style="color:#268bd2">AsyncService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>通过上下文类获取 future
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>AsyncService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Han Meimei&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Future&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> fooFuture &lt;span style="color:#719e07">=&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">().&lt;/span>getFuture&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>fooFuture&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>可以看出，这样的使用方式，不太符合异步编程的习惯，竟然需要从一个上下文类中获取到 Future。如果同时进行多个异步调用，使用不当很容易造成上下文污染。而且，Future 并不支持 callback 的调用方式。这些弊端在 Dubbo 2.7 中得到了改进。&lt;/p>
&lt;h3 id="43-dubbo-27-异步化">4.3 Dubbo 2.7 异步化&lt;/h3>
&lt;ol>
&lt;li>无需配置中特殊声明，显式声明异步接口即可
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">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">sayHiAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>completedFuture&lt;span style="color:#719e07">(&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>使用 callback 方式处理返回值
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> asyncService&lt;span style="color:#719e07">.&lt;/span>sayHiAsync&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Han MeiMei&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>future&lt;span style="color:#719e07">.&lt;/span>whenComplete&lt;span style="color:#719e07">((&lt;/span>retValue&lt;span style="color:#719e07">,&lt;/span> exception&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>exception &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>retValue&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exception&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Dubbo 2.7 中使用了 JDK1.8 提供的 &lt;code>CompletableFuture&lt;/code> 原生接口对自身的异步化做了改进。&lt;code>CompletableFuture&lt;/code> 可以支持 future 和 callback 两种调用方式，用户可以根据自己的喜好和场景选择使用，非常灵活。&lt;/p>
&lt;h3 id="44-异步化设计-faq">4.4 异步化设计 FAQ&lt;/h3>
&lt;p>Q：如果 RPC 接口只定义了同步接口，有办法使用异步调用吗？&lt;/p>
&lt;p>A：2.6 中的异步调用唯一的优势在于，不需要在接口层面做改造，又可以进行异步调用，这种方式仍然在 2.7 中保留；使用 Dubbo 官方提供的 compiler hacker，编译期自动重写同步方法，请&lt;a href="https://github.com/dubbo/dubbo-async-processor#compiler-hacker-processer">在此&lt;/a>讨论和跟进具体进展。&lt;/p>
&lt;hr>
&lt;p>Q：关于异步接口的设计问题，为何不提供编译插件，根据原接口，自动编译出一个 XxxAsync 接口？&lt;/p>
&lt;p>A：Dubbo 2.7 采用过这种设计，但接口的膨胀会导致服务类的增量发布，而且接口名的变化会影响服务治理的一些相关逻辑，改为方法添加 Async 后缀相对影响范围较小。&lt;/p>
&lt;hr>
&lt;p>Q：Dubbo 分为了客户端异步和服务端异步，刚刚你介绍的是客户端异步，为什么不提服务端异步呢？&lt;/p>
&lt;p>A：Dubbo 2.7 新增了服务端异步的支持，但实际上，Dubbo 的业务线程池模型，本身就可以理解为异步调用，个人认为服务端异步的特性较为鸡肋。&lt;/p>
&lt;h2 id="5-三大中心改造">5 三大中心改造&lt;/h2>
&lt;p>三大中心指的：注册中心，元数据中心，配置中心。&lt;/p>
&lt;p>在 2.7 之前的版本，Dubbo 只配备了注册中心，主流使用的注册中心为 zookeeper。新增加了元数据中心和配置中心，自然是为了解决对应的痛点，下面我们来详细阐释三大中心改造的原因。&lt;/p>
&lt;h3 id="51-元数据改造">5.1 元数据改造&lt;/h3>
&lt;p>元数据是什么？元数据定义为描述数据的数据，在服务治理中，例如服务接口名，重试次数，版本号等等都可以理解为元数据。在 2.7 之前，元数据一股脑丢在了注册中心之中，这造成了一系列的问题：&lt;/p>
&lt;p>&lt;strong>推送量大 -&amp;gt; 存储数据量大 -&amp;gt; 网络传输量大 -&amp;gt; 延迟严重&lt;/strong>&lt;/p>
&lt;p>生产者端注册 30+ 参数，有接近一半是不需要作为注册中心进行传递；消费者端注册 25+ 参数，只有个别需要传递给注册中心。有了以上的理论分析，Dubbo 2.7 进行了大刀阔斧的改动，只将真正属于服务治理的数据发布到注册中心之中，大大降低了注册中心的负荷。&lt;/p>
&lt;p>同时，将全量的元数据发布到另外的组件中：元数据中心。元数据中心目前支持 redis（推荐），zookeeper。这也为 Dubbo 2.7 全新的 Dubbo Admin 做了准备，关于新版的 Dubbo Admin，我将会后续准备一篇独立的文章进行介绍。&lt;/p>
&lt;p>示例：使用 zookeeper 作为元数据中心&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:metadata-report&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;/code>&lt;/pre>&lt;/div>&lt;h3 id="52-dubbo-26-元数据">5.2 Dubbo 2.6 元数据&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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>dubbo://30.5.120.185:20880/com.alibaba.dubbo.demo.DemoService?
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">anyhost&lt;/span>&lt;span style="color:#719e07">=&lt;/span>true&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">application&lt;/span>&lt;span style="color:#719e07">=&lt;/span>demo-provider&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.demo.DemoService&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">methods&lt;/span>&lt;span style="color:#719e07">=&lt;/span>sayHello&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>bean.name&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.demo.DemoService&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.0.2&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">executes&lt;/span>&lt;span style="color:#719e07">=&lt;/span>4500&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">generic&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">owner&lt;/span>&lt;span style="color:#719e07">=&lt;/span>kirito&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">pid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>84228&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">retries&lt;/span>&lt;span style="color:#719e07">=&lt;/span>7&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">side&lt;/span>&lt;span style="color:#719e07">=&lt;/span>provider&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1552965771067&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>从本地的 zookeeper 中取出一条服务数据，通过解码之后，可以看出，的确有很多参数是不必要。&lt;/p>
&lt;h3 id="53-dubbo-27-元数据">5.3 Dubbo 2.7 元数据&lt;/h3>
&lt;p>在 2.7 中，如果不进行额外的配置，zookeeper 中的数据格式仍然会和 Dubbo 2.6 保持一致，这主要是为了保证兼容性，让 Dubbo 2.6 的客户端可以调用 Dubbo 2.7 的服务端。如果整体迁移到 2.7，则可以为注册中心开启简化配置的参数：&lt;/p>
&lt;div class="highlight">&lt;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">“zookeeper://127.0.0.1:2181”&lt;/span> simplified=&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>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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>dubbo://30.5.120.185:20880/org.apache.dubbo.demo.api.DemoService?
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">application&lt;/span>&lt;span style="color:#719e07">=&lt;/span>demo-provider&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.0.2&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">release&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.7.0&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">1552975501873&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对于那些非必要的服务信息，仍然全量存储在元数据中心之中：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/270/metadata.png" alt="元数据">&lt;/p>
&lt;blockquote>
&lt;p>元数据中心的数据可以被用于服务测试，服务 MOCK 等功能。目前注册中心配置中 simplified 的默认值为 false，因为考虑到了迁移的兼容问题，在后续迭代中，默认值将会改为 true。&lt;/p>
&lt;/blockquote>
&lt;h3 id="54-配置中心支持">5.4 配置中心支持&lt;/h3>
&lt;p>衡量配置中心的必要性往往从三个角度出发：&lt;/p>
&lt;ol>
&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;/ol>
&lt;p>Spring Cloud Config, Apollo, Nacos 等分布式配置中心组件都对上述功能有不同程度的支持。在 2.7 之前的版本中，在 zookeeper 中设置了部分节点：configurators，routers，用于管理部分配置和路由信息，它们可以理解为 Dubbo 配置中心的雏形。在 2.7 中，Dubbo 正式支持了配置中心，目前支持的几种注册中心 Zookeeper，Apollo，Nacos（2.7.1-release 支持）。&lt;/p>
&lt;p>在 Dubbo 中，配置中心主要承担了两个作用&lt;/p>
&lt;ul>
&lt;li>
&lt;p>外部化配置。启动配置的集中式存储&lt;/p>
&lt;/li>
&lt;li>
&lt;p>服务治理。服务治理规则的存储与通知&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>示例：使用 Zookeeper 作为配置中心&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:config-center&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;/code>&lt;/pre>&lt;/div>&lt;p>引入配置中心后，需要注意配置项的覆盖问题，优先级如图所示&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/configuration.jpg" alt="配置覆盖优先级">&lt;/p>
&lt;h2 id="6-服务治理增强">6 服务治理增强&lt;/h2>
&lt;p>我更倾向于将 Dubbo 当做一个服务治理框架，而不仅仅是一个 RPC 框架。在 2.7 中，Dubbo 对其服务治理能力进行了增强，增加了标签路由的能力，并抽象出了应用路由和服务路由的概念。在最后一个特性介绍中，着重对标签路由 TagRouter 进行探讨。&lt;/p>
&lt;blockquote>
&lt;p>在服务治理中，路由层和负载均衡层的对比。区别 1，Router：m 选 n，LoadBalance：n 选 1；区别 2，路由往往是叠加使用的，负载均衡只能配置一种。&lt;/p>
&lt;/blockquote>
&lt;p>在很长的一段时间内，Dubbo 社区经常有人提的一个问题是：Dubbo 如何实现流量隔离和灰度发布，直到 2.7 提供了标签路由，用户可以使用这个功能，来实现上述的需求。&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/270/tag-route.png" alt="标签路由">&lt;/p>
&lt;p>标签路由提供了这样一个能力，当调用链路为 A -&amp;gt; B -&amp;gt; C -&amp;gt; D 时，用户给请求打标，最典型的打标方式可以借助 attachment（他可以在分布式调用中传递下去），调用会优先请求那些匹配的服务端，如 A -&amp;gt; B，C -&amp;gt; D，由于集群中未部署 C 节点，则会降级到普通节点。&lt;/p>
&lt;p>打标方式会受到集成系统差异的影响，从而导致很大的差异，所以 Dubbo 只提供了 &lt;code>RpcContext.getContext().setAttachment()&lt;/code> 这样的基础接口，用户可以使用 SPI 扩展，或者 server filter 的扩展，对测试流量进行打标，引导进入隔离环境/灰度环境。&lt;/p>
&lt;p>新版的 Dubbo Admin 提供了标签路由的配置项：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/270/tag-route-config.png" alt="标签路由配置">&lt;/p>
&lt;p>Dubbo 用户可以在自己系统的基础上对标签路由进行二次扩展，或者借鉴标签路由的设计，实现自己系统的流量隔离，灰度发布。&lt;/p>
&lt;h2 id="7-总结">7 总结&lt;/h2>
&lt;p>本文介绍了 Dubbo 2.7 比较重要的三大新特性：异步化改造，三大中心改造，服务治理增强。Dubbo 2.7 还包含了很多功能优化、特性升级，可以在项目源码的 &lt;a href="https://github.com/apache/dubbo/blob/master/CHANGES.md">CHANGES.md&lt;/a> 中浏览全部的改动点。最后提供一份 Dubbo 2.7 的升级文档：&lt;a href="https://cn.dubbo.apache.org/zh/docsv2.7/user/versions/version-270/">2.7迁移文档&lt;/a>，欢迎体验。&lt;/p></description></item><item><title>Blog: Dubbo 关于同步/异步调用的几种方式</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">goodbye&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;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>
&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-java" data-lang="java">&lt;span style="display:flex;">&lt;span>AsyncService service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">...;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>String result &lt;span style="color:#719e07">=&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>goodbye&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;samples&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>&lt;span style="color:#586e75">// 这里的返回值为空，请不要使用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>Future&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">().&lt;/span>getFuture&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span> &lt;span style="color:#586e75">// 业务线程可以开始做其他事情
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>result &lt;span style="color:#719e07">=&lt;/span> future&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// 阻塞需要获取异步结果时，也可以使用 get(timeout, unit) 设置超时时间
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dubbo Consumer 端发起调用后，同时通过&lt;code>RpcContext.getContext().getFuture()&lt;/code>获取跟返回结果关联的&lt;code>Future&lt;/code>对象，然后就可以开始处理其他任务；当需要这次异步调用的结果时，可以在任意时刻通过&lt;code>future.get(timeout)&lt;/code>来获取。&lt;/p>
&lt;p>一些特殊场景下，为了尽快调用返回，可以设置是否等待消息发出：&lt;/p>
&lt;ul>
&lt;li>&lt;code>sent=&amp;quot;true&amp;quot;&lt;/code> 等待消息发出，消息发送失败将抛出异常；&lt;/li>
&lt;li>&lt;code>sent=&amp;quot;false&amp;quot;&lt;/code> 不等待消息发出，将消息放入 IO 队列，即刻返回。&lt;/li>
&lt;/ul>
&lt;p>默认为&lt;code>false&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: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> sent=&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>如果你只是想异步，完全忽略返回值，可以配置 &lt;code>return=&amp;quot;false&amp;quot;&lt;/code>，以减少 Future 对象的创建和管理成本：&lt;/p>
&lt;div class="highlight">&lt;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: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> return=&lt;span style="color:#2aa198">&amp;#34;false&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;code>RpcContext.getContext().getFuture()&lt;/code>将返回&lt;code>null&lt;/code>。&lt;/p>
&lt;p>整个异步调用的时序图如下：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-async.svg" alt="异步调用">&lt;/p>
&lt;p>此示例代码位于：https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-async&lt;/p>
&lt;h2 id="参数回调">参数回调&lt;/h2>
&lt;p>参数回调有点类似于本地 Callback 机制，但 Callback 并不是 Dubbo 内部的类或接口，而是由 Provider 端自定义的；Dubbo 将基于长连接生成反向代理，从而实现从 Provider 端调用 Consumer 端的逻辑。&lt;/p>
&lt;h3 id="provider-端定义-service-和-callback">Provider 端定义 Service 和 Callback&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">CallbackService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addListener&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String key&lt;span style="color:#719e07">,&lt;/span> CallbackListener listener&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">CallbackListener&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">changed&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String msg&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="provider-端-service-实现">Provider 端 Service 实现&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">CallbackServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> CallbackService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&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> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> CallbackListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> listeners &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> CallbackListener&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:#268bd2">CallbackServiceImpl&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread t &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Thread&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> Runnable&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">run&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> CallbackListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> listeners&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">().&lt;/span>changed&lt;span style="color:#719e07">(&lt;/span>getChanged&lt;span style="color:#719e07">(&lt;/span>entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listeners&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">5000&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// timely trigger change event
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t&lt;span style="color:#719e07">.&lt;/span>setDaemon&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&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">addListener&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String key&lt;span style="color:#719e07">,&lt;/span> CallbackListener listener&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listeners&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> listener&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listener&lt;span style="color:#719e07">.&lt;/span>changed&lt;span style="color:#719e07">(&lt;/span>getChanged&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">));&lt;/span> &lt;span style="color:#586e75">// send notification for change
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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 &lt;span style="color:#268bd2">getChanged&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String key&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Changed: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#719e07">new&lt;/span> SimpleDateFormat&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;yyyy-MM-dd HH:mm:ss&amp;#34;&lt;/span>&lt;span style="color:#719e07">).&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> Date&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="provider-端暴露服务">Provider 端暴露服务&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-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;callbackService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.callback.impl.CallbackServiceImpl&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;com.alibaba.dubbo.samples.callback.api.CallbackService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;callbackService&amp;#34;&lt;/span> connections=&lt;span style="color:#2aa198">&amp;#34;1&amp;#34;&lt;/span> callbacks=&lt;span style="color:#2aa198">&amp;#34;1000&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;addListener&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:argument&lt;/span> index=&lt;span style="color:#2aa198">&amp;#34;1&amp;#34;&lt;/span> callback=&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:#586e75">&amp;lt;!--&amp;lt;dubbo:argument type=&amp;#34;com.demo.CallbackListener&amp;#34; callback=&amp;#34;true&amp;#34; /&amp;gt;--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/dubbo:method&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dubbo:service&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里，Provider 需要在方法中声明哪个参数是 Callback 参数。&lt;/p>
&lt;h4 id="consumer-端实现-callback-接口">Consumer 端实现 Callback 接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>CallbackService callbackService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">...;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>callbackService&lt;span style="color:#719e07">.&lt;/span>addListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;foo.bar&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CallbackListener&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">changed&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String msg&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;callback1:&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> msg&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Callback 接口的实现类在 Consumer 端，当方法发生调用时，Consumer 端会自动 export 一个 Callback 服务。而 Provider 端在处理调用时，判断如果参数是 Callback，则生成了一个 proxy，因此服务实现类里在调用 Callback 方法的时候，会被传递到 Consumer 端执行 Callback 实现类的代码。&lt;/p>
&lt;p>上述示例代码位于：https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-callback&lt;/p>
&lt;p>这种调用方式有点像消息的发布和订阅，但又有区别。比如当 Consumer 端 完成了Callback 服务的 export 后，如果后续重启了，这时 Provider 端就会调不通；同时 Provider 端如何清理掉这个 proxy 也是一个问题。&lt;/p>
&lt;h2 id="事件通知">事件通知&lt;/h2>
&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 style="color:#719e07">{&lt;/span>
&lt;/span>&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&lt;span style="color:#719e07">,&lt;/span> 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&lt;span style="color:#719e07">,&lt;/span> 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>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> id&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>id&lt;span style="color:#719e07">,&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;onreturn: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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>&lt;span style="color:#719e07">(&lt;/span>Throwable ex&lt;span style="color:#719e07">,&lt;/span> String name&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> id&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;onthrow: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里要强调一点，自定义 Notify 接口中的三个方法的参数规则如下：&lt;/p>
&lt;ul>
&lt;li>&lt;code>oninvoke&lt;/code> 方法参数与调用方法的参数相同；&lt;/li>
&lt;li>&lt;code>onreturn&lt;/code>方法第一个参数为调用方法的返回值，其余为调用方法的参数；&lt;/li>
&lt;li>&lt;code>onthrow&lt;/code>方法第一个参数为调用异常，其余为调用方法的参数。&lt;/li>
&lt;/ul>
&lt;p>上述配置中，&lt;code>sayHello&lt;/code>方法为同步调用，因此事件通知方法的执行也是同步执行。可以配置 &lt;code>async=true&lt;/code>让方法调用为异步，这时事件通知的方法也是异步执行的。特别强调一下，&lt;code>oninvoke&lt;/code>方法不管是否异步调用，都是同步执行的。&lt;/p>
&lt;p>事件通知的示例代码请参考：https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-notify&lt;/p></description></item><item><title>Blog: Dubbo 基本用法 - Dubbo Consumer 配置</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>属性名&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>id&lt;/td>
&lt;td>服务引用id，作为java bean id，需要唯一&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>interface&lt;/td>
&lt;td>接口名，用于查找服务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>version&lt;/td>
&lt;td>版本号，与服务提供者的版本一致&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>服务方法调用超时时间(毫秒)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>retries&lt;/td>
&lt;td>远程服务调用重试次数，不包括第一次调用，不需要重试请设为0&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>connections&lt;/td>
&lt;td>对每个提供者的最大连接数，rmi、http、hessian等短连接协议表示限制连接数，dubbo等长连接协表示建立的长连接个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>loadbalance&lt;/td>
&lt;td>负载均衡策略，可选值：random,roundrobin,leastactive，分别表示：随机，轮询，最少活跃调用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>async&lt;/td>
&lt;td>是否异步执行，不可靠异步，只是忽略返回值，不阻塞执行线程&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>generic&lt;/td>
&lt;td>泛化调用，可以绕过&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>check&lt;/td>
&lt;td>启动时检查提供者是否存在，true报错，false忽略&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>actives&lt;/td>
&lt;td>每服务消费者每服务每方法最大并发调用数&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>其他配置属性请参考xsd：http://dubbo.apache.org/schema/dubbo/dubbo.xsd&lt;/p>
&lt;ul>
&lt;li>&amp;lt;dubbo:method/&amp;gt;作为&amp;lt;dubbo:reference/&amp;gt;的子元素，它可以针对方法进行配置。比较常用的属性有：&lt;/li>
&lt;/ul>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性名&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>executes&lt;/td>
&lt;td>服务执行的请求上限&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>retries&lt;/td>
&lt;td>超时重试次数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>调用超时时间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>loadbalance&lt;/td>
&lt;td>负载均衡策略，可选值：random,roundrobin,leastactive，分别表示：随机，轮询，最少活跃调用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>async&lt;/td>
&lt;td>是否异步执行，不可靠异步，只是忽略返回值，不阻塞执行线程&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>actives&lt;/td>
&lt;td>每服务消费者最大并发调用限制&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>其他属性，可以参考上面的xsd。&lt;/p>
&lt;h6 id="配置的覆盖关系">配置的覆盖关系&lt;/h6>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/2018/08/14/dubbo-usage/1536496436861-1b63bc4e-3e59-4aa3-800e-a32cfe64950d.png" alt="undefined">&lt;/p>
&lt;center>配置的覆盖关系图&lt;/center>
&lt;p>其中包含了consumer端和provider的配置，注意区分。&lt;/p>
&lt;h4 id="annotation">annotation&lt;/h4>
&lt;h6 id="reference注解远程服务">Reference注解远程服务&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>public class AnnotationConsumeService {
&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.config.annotation.Reference
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public AnnotateService annotateService;
&lt;/span>&lt;/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>这种方式的配置和前面用xml配置的方式是一样的效果。&lt;/p>
&lt;blockquote>
&lt;p>指定dubbo扫描路径的方式，可以参考前一章节中provider的实现。&lt;/p>
&lt;/blockquote>
&lt;h4 id="api直接触发">api直接触发&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>import com.alibaba.dubbo.rpc.config.ApplicationConfig;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.alibaba.dubbo.rpc.config.RegistryConfig;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.alibaba.dubbo.rpc.config.ConsumerConfig;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.alibaba.dubbo.rpc.config.ReferenceConfig;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.xxx.XxxService;
&lt;/span>&lt;/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>ApplicationConfig application = new ApplicationConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application.setName(&amp;#34;yyy&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;10.20.130.230:9090&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>registry.setUsername(&amp;#34;aaa&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>registry.setPassword(&amp;#34;bbb&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 注意：ReferenceConfig为重对象，内部封装了与注册中心的连接，以及与服务提供方的连接
&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;XxxService&amp;gt; reference = new ReferenceConfig&amp;lt;XxxService&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.setApplication(application);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference.setInterface(XxxService.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference.setVersion(&amp;#34;1.0.0&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 和本地bean一样使用xxxService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>XxxService xxxService = reference.get();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="method特殊设置">method特殊设置&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>// 方法级配置
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>List&amp;lt;MethodConfig&amp;gt; methods = new ArrayList&amp;lt;MethodConfig&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>MethodConfig method = new MethodConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>method.setName(&amp;#34;createXxx&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>method.setTimeout(10000);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>method.setRetries(0);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>methods.add(method);
&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;XxxService&amp;gt; reference = new ReferenceConfig&amp;lt;XxxService&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.setMethods(methods); // 设置方法级配置
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="consumer-调用远程服务">Consumer 调用远程服务&lt;/h3>
&lt;p>上面章节更多从配置角度出发，接下来通过一个完整的例子，来讲解下dubbo consumer的完整使用。&lt;/p>
&lt;p>这个例子中只有一个服务UserReadService，有一个方法 getUserById。 需要将通过Dubbo调用远程的服务。具体的步骤如下：&lt;/p>
&lt;p>1.创建一个工程
如果本来已经有工程，可以忽略。创建一个spring boot工程，可以通过 &lt;a href="https://start.spring.io/">https://start.spring.io/&lt;/a> 创建。&lt;br>
服务的提供方，已经在provider章节中进行了定义。
2.调用服务&lt;/p>
&lt;div class="highlight">&lt;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>@RestController
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class UserTestController{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Autowired
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private UserReadService userReadService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @RequestMapping(&amp;#34;/user/getById&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public String getUserById(Long id){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // just test
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return userReadService.getUserById(id).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;/code>&lt;/pre>&lt;/div>&lt;p>3.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-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;userReadService&amp;#34; interface=&amp;#34;com.package.UserReadService&amp;#34;check=&amp;#34;false&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配置的其他方式可以参考上一章节的相关配置，或者使用集成dubbo spring boot starter方式。&lt;/p></description></item><item><title>Blog: Dubbo 基础用法 - Provider 配置</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>标签&lt;/th>
&lt;th>用途&lt;/th>
&lt;th style="text-align:left">解释&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&amp;lt;dubbo:service/&amp;gt;&lt;/td>
&lt;td>服务配置&lt;/td>
&lt;td style="text-align:left">用于暴露一个服务，定义服务的元信息，一个服务可以用多个协议暴露，一个服务也可以注册到多个注册中心&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:reference/&amp;gt;&lt;/td>
&lt;td>引用配置&lt;/td>
&lt;td style="text-align:left">用于创建一个远程服务代理，一个引用可以指向多个注册中心&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:protocol/&amp;gt;&lt;/td>
&lt;td>协议配置&lt;/td>
&lt;td style="text-align:left">用于配置提供服务的协议信息，协议由提供方指定，消费方被动接受&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:application/&amp;gt;&lt;/td>
&lt;td>应用配置&lt;/td>
&lt;td style="text-align:left">用于配置当前应用信息，不管该应用是提供者还是消费者&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:module/&amp;gt;&lt;/td>
&lt;td>模块配置&lt;/td>
&lt;td style="text-align:left">用于配置当前模块信息，可选&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:registry/&amp;gt;&lt;/td>
&lt;td>注册中心配置&lt;/td>
&lt;td style="text-align:left">用于配置连接注册中心相关信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:monitor/&amp;gt;&lt;/td>
&lt;td>监控中心配置&lt;/td>
&lt;td style="text-align:left">用于配置连接监控中心相关信息，可选&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:provider/&amp;gt;&lt;/td>
&lt;td>提供方配置&lt;/td>
&lt;td style="text-align:left">当 ProtocolConfig 和 ServiceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:consumer/&amp;gt;&lt;/td>
&lt;td>消费方配置&lt;/td>
&lt;td style="text-align:left">当 ReferenceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:method/&amp;gt;&lt;/td>
&lt;td>方法配置&lt;/td>
&lt;td style="text-align:left">用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;lt;dubbo:argument/&amp;gt;&lt;/td>
&lt;td>参数配置&lt;/td>
&lt;td style="text-align:left">用于指定方法参数配置&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/user/dubbo-config.jpg" alt="配置之间关系图">&lt;/p>
&lt;h6 id="配置项详解">配置项详解&lt;/h6>
&lt;ul>
&lt;li>
&lt;p>&amp;lt;dubbo:application name=&amp;ldquo;hello-world-app&amp;rdquo; /&amp;gt;&lt;br>
用于指定应用名，这里需要保证应用名唯一，这个应用名在后续的console admin中可以在列表中显示，方便管理。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;lt;dubbo:registry address=&amp;ldquo;multicast://224.5.6.7:1234&amp;rdquo; /&amp;gt;&lt;br>
注册中心配置，和服务发现的具体机制有关系。可以是zookeeper地址，也可以eureka地址。上面这个是广播地址，在本地服务调用的测试过程中非常方便。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;lt;dubbo:protocol name=&amp;ldquo;dubbo&amp;rdquo; port=&amp;ldquo;20880&amp;rdquo; /&amp;gt;&lt;br>
这里是传输的协议和默认端口，一般不需要更改。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>接下来重点讲解下&amp;lt;dubbo:service/&amp;gt;的配置。&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>&amp;lt;dubbo:service/&amp;gt;支持的主要属性列表：&lt;/li>
&lt;/ul>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性名&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>version&lt;/td>
&lt;td>版本号&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>scope&lt;/td>
&lt;td>服务可见性, 值为：local 或者 remote，默认为remote&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>actives&lt;/td>
&lt;td>最大的激活的请求数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>async&lt;/td>
&lt;td>方法调用是否异步，默认为false&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cache&lt;/td>
&lt;td>服务缓存，可选值：lru/threadlocal/jcache&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>callbacks&lt;/td>
&lt;td>callback实例的限制&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>generic&lt;/td>
&lt;td>泛化调用，可以绕过&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>class&lt;/td>
&lt;td>Service的实现的类名&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>connections&lt;/td>
&lt;td>这个服务里的连接数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>delay&lt;/td>
&lt;td>发布服务延迟的毫秒数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>executes&lt;/td>
&lt;td>服务执行的请求上限&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>retries&lt;/td>
&lt;td>超时重试次数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>调用超时时间&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>其他配置属性请参考xsd：http://dubbo.apache.org/schema/dubbo/dubbo.xsd&lt;/p>
&lt;ul>
&lt;li>&amp;lt;dubbo:method/&amp;gt;作为&amp;lt;dubbo:service/&amp;gt;的子元素，它可以针对方法进行配置。比较常用的属性有：&lt;/li>
&lt;/ul>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性名&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>executes&lt;/td>
&lt;td>服务执行的请求上限&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>retries&lt;/td>
&lt;td>超时重试次数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>调用超时时间&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>其他属性，可以参考上面的xsd。&lt;/p>
&lt;h6 id="配置的覆盖关系">配置的覆盖关系&lt;/h6>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/user/dubbo-config-override.jpg" alt="配置的覆盖关系图">&lt;/p>
&lt;p>这里的覆盖关系包含了Provider和Consumer两端的配置，如果对consumer有疑问，可以参考后一章节的consumer章节之后再来理解。&lt;/p>
&lt;h4 id="dubboproperties方式配置">dubbo.properties方式配置&lt;/h4>
&lt;blockquote>
&lt;p>如果公共配置很简单，没有多注册中心，多协议等情况，或者想多个 Spring 容器想共享配置，可以使用 dubbo.properties 作为缺省配置。&lt;/p>
&lt;/blockquote>
&lt;p>Dubbo 将自动加载 classpath 根目录下的 dubbo.properties，可以通过JVM启动参数 -Ddubbo.properties.file=xxx.properties 改变缺省配置位置。&lt;/p>
&lt;h6 id="dubboproperties配置样例">dubbo.properties配置样例&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>dubbo.application.name=dubbodemo-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 注册中心地址
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address=zookeeper://localhost:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 广播的注册中心样例
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>#dubbo.registry.address=multicast://224.5.6.7:1234
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 调用协议地址
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.name=dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.port=28080
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="映射规则">映射规则&lt;/h6>
&lt;p>将 XML 配置的标签名，加属性名，用点分隔，多个属性拆成多行&lt;/p>
&lt;ul>
&lt;li>比如：dubbo.application.name=foo等价于&amp;lt;dubbo:application name=&amp;ldquo;foo&amp;rdquo; /&amp;gt;&lt;/li>
&lt;li>比如：dubbo.registry.address=10.20.153.10:9090等价于&amp;lt;dubbo:registry address=&amp;ldquo;10.20.153.10:9090&amp;rdquo; /&amp;gt;&lt;/li>
&lt;/ul>
&lt;p>如果 XML 有多行同名标签配置，可用 id 号区分，如果没有 id 号将对所有同名标签生效&lt;/p>
&lt;ul>
&lt;li>比如：dubbo.protocol.rmi.port=1234等价于&amp;lt;dubbo:protocol id=&amp;ldquo;rmi&amp;rdquo; name=&amp;ldquo;rmi&amp;rdquo; port=&amp;ldquo;1234&amp;rdquo; /&amp;gt;&lt;/li>
&lt;li>比如：dubbo.registry.china.address=10.20.153.10:9090等价于&amp;lt;dubbo:registry id=&amp;ldquo;china&amp;rdquo; address=&amp;ldquo;10.20.153.10:9090&amp;rdquo; /&amp;gt;&lt;/li>
&lt;/ul>
&lt;h6 id="覆盖策略">覆盖策略&lt;/h6>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/user/dubbo-config.jpg" alt="覆盖策略">&lt;/p>
&lt;ul>
&lt;li>JVM 启动 -D 参数优先，这样可以使用户在部署和启动时进行参数重写，比如在启动时需改变协议的端口。&lt;/li>
&lt;li>XML 次之，如果在 XML 中有配置，则 dubbo.properties 中的相应配置项无效。&lt;/li>
&lt;li>Properties 最后，相当于缺省值，只有 XML 没有配置时，dubbo.properties 的相应配置项才会生效，通常用于共享公共配置，比如应用名。&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>注意：&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>如果 classpath 根目录下存在多个 dubbo.properties，比如多个 jar 包中有 dubbo.properties，Dubbo 会任意加载，并打印 Error 日志，后续可能改为抛异常。 ↩&lt;/li>
&lt;li>协议的 id 没配时，缺省使用协议名作为 id&lt;/li>
&lt;/ol>
&lt;h5 id="注解配置">注解配置&lt;/h5>
&lt;h6 id="service注解暴露服务">Service注解暴露服务&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>import com.alibaba.dubbo.config.annotation.Service;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Service(timeout = 5000)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class AnnotateServiceImpl implements AnnotateService {
&lt;/span>&lt;/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;h6 id="javaconfig形式配置公共模块">javaconfig形式配置公共模块&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>@Configuration
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class DubboConfiguration {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Bean
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public ApplicationConfig applicationConfig() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig = new ApplicationConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig.setName(&amp;#34;provider-test&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return 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>&lt;/span>&lt;span style="display:flex;">&lt;span> @Bean
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public RegistryConfig registryConfig() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RegistryConfig registryConfig = new RegistryConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig.setAddress(&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig.setClient(&amp;#34;curator&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return registryConfig;
&lt;/span>&lt;/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>这种方式的配置和前面用xml配置的方式是一样的效果。&lt;/p>
&lt;h6 id="指定dubbo扫描路径">指定dubbo扫描路径&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>@SpringBootApplication
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@DubboComponentScan(basePackages = &amp;#34;com.alibaba.dubbo.test.service.impl&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class ProviderTestApp {
&lt;/span>&lt;/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>或者使用spring bean 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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;dubbo:annotation package=&amp;#34;com.chanshuyi.service.impl&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="代码配置">代码配置&lt;/h5>
&lt;div class="highlight">&lt;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>import com.alibaba.dubbo.rpc.config.ApplicationConfig;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.alibaba.dubbo.rpc.config.RegistryConfig;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.alibaba.dubbo.rpc.config.ProviderConfig;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.alibaba.dubbo.rpc.config.ServiceConfig;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.xxx.XxxService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>import com.xxx.XxxServiceImpl;
&lt;/span>&lt;/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>XxxService xxxService = new XxxServiceImpl();
&lt;/span>&lt;/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>ApplicationConfig application = new ApplicationConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application.setName(&amp;#34;xxx&amp;#34;);
&lt;/span>&lt;/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>RegistryConfig registry = new RegistryConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>registry.setAddress(&amp;#34;10.20.130.230:9090&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>registry.setUsername(&amp;#34;aaa&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>registry.setPassword(&amp;#34;bbb&amp;#34;);
&lt;/span>&lt;/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>ProtocolConfig protocol = new ProtocolConfig();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>protocol.setName(&amp;#34;dubbo&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>protocol.setPort(12345);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>protocol.setThreads(200);
&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>&lt;/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>ServiceConfig&amp;lt;XxxService&amp;gt; service = new ServiceConfig&amp;lt;XxxService&amp;gt;(); // 此实例很重，封装了与注册中心的连接，请自行缓存，否则可能造成内存和连接泄漏
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service.setApplication(application);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service.setRegistry(registry); // 多个注册中心可以用setRegistries()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service.setProtocol(protocol); // 多个协议可以用setProtocols()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service.setInterface(XxxService.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service.setRef(xxxService);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service.setVersion(&amp;#34;1.0.0&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>// 暴露及注册服务
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service.export();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>一般在spring应用中，不推荐使用这种方式。 具体的含义这里不做解释，可以通过github查看源码。&lt;/p>
&lt;h3 id="provider-接口和实现">Provider 接口和实现&lt;/h3>
&lt;p>上面章节更多从配置角度出发，接下来通过一个完整的例子，来讲解下dubbo provider的完整使用。&lt;/p>
&lt;p>这个例子中只有一个服务UserReadService，有一个方法 getUserById。 需要将这个服务通过Dubbo暴露给远程的服务。具体的步骤如下：&lt;/p>
&lt;p>1.创建工程
如果本来已经有工程，可以忽略。创建一个spring boot工程，可以通过 &lt;a href="https://start.spring.io/">https://start.spring.io/&lt;/a> 创建。
2.定义接口
定义接口：UserReadService&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public interface UserReadService{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public User getUserById(Long userId);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个接口一般来说会放到独立的jar包里，作为client包。 其他应用要消费这个服务的时候，一般来说需要应用引用这个client包。(除了泛化调用)
3.实现接口
实现UserReadService, 当前实现部署在Provider的应用中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public UserReadServiceImpl implements UserReadService{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public User getUserById(Long userId){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return xxx;
&lt;/span>&lt;/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>4.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-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;bean id=&amp;#34;userReadService&amp;#34; class=&amp;#34;com.package.UserReadServiceImpl&amp;#34;/&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:service interface=&amp;#34;com.package.UserReadService&amp;#34; ref=&amp;#34;userReadService&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配置的其他方式可以参考上一章节的相关配置，或者使用集成dubbo spring boot starter方式。&lt;/p></description></item><item><title>Blog: Dubbo 优雅停机</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>registered&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> registered&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Runtime&lt;span style="color:#719e07">.&lt;/span>getRuntime&lt;span style="color:#719e07">().&lt;/span>addShutdownHook&lt;span style="color:#719e07">(&lt;/span>getDubboShutdownHook&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>每个ShutdownHook都是一个单独的线程，由JVM在退出时触发执行&lt;code>org.apache.dubbo.config.DubboShutdownHook&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Destroy all the resources, including registries and protocols.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&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">doDestroy&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>destroyed&lt;span style="color:#719e07">.&lt;/span>compareAndSet&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// destroy all the registries
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AbstractRegistryFactory&lt;span style="color:#719e07">.&lt;/span>destroyAll&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// destroy all the protocols
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> destroyProtocols&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>首先关闭所有注册中心，这一步包括：&lt;/p>
&lt;ul>
&lt;li>从注册中心注销所有已经发布的服务；&lt;/li>
&lt;li>取消订阅当前应用所有依赖的服务；&lt;/li>
&lt;li>断开与注册中心的连接。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>执行所有&lt;code>Protocol&lt;/code>的&lt;code>destroy()&lt;/code>，主要包括：&lt;/p>
&lt;ul>
&lt;li>销毁所有&lt;code>Invoker&lt;/code>和&lt;code>Exporter&lt;/code>；&lt;/li>
&lt;li>关闭Server，向所有已连接Client发送当前Server只读事件；&lt;/li>
&lt;li>关闭独享/共享Client，断开连接，取消超时和重试任务；&lt;/li>
&lt;li>释放所有相关资源。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>执行完毕，关闭JVM。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="注意事项">注意事项&lt;/h2>
&lt;ul>
&lt;li>使用&lt;code>SIGKILL&lt;/code>关闭应用不会执行优雅停机；&lt;/li>
&lt;li>优雅停机不保证会等待所有已发送/到达请求结束；&lt;/li>
&lt;li>配置的优雅停机等待时间&lt;code>timeout&lt;/code>不是所有步骤等待时间的总和，而是每一个&lt;code>destroy&lt;/code>执行的最大时间。例如配置等待时间为5秒，则关闭Server、关闭Client等步骤会分别等待5秒。&lt;/li>
&lt;/ul></description></item><item><title>Blog: Dubbo的泛化调用</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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> &lt;span style="color:#719e07">(&lt;/span>GenericService&lt;span style="color:#719e07">)&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;userService&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// primary param and return value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>String name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">)&lt;/span> userService&lt;span style="color:#719e07">.&lt;/span>$invoke&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;delete&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#dc322f">int&lt;/span>&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()},&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&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>
&lt;h2 id="参数或返回值是-pojo-的场景">参数或返回值是 POJO 的场景&lt;/h2>
&lt;p>比如方法签名是 &lt;code>User get(Params params);&lt;/code>其中 User 有 id 和 name 两个属性，Params 有 query 一个属性。&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>String&lt;span style="color:#719e07">[]&lt;/span> parameterTypes &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.generic.api.Params&amp;#34;&lt;/span>&lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> params &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>param&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;class&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.generic.api.Params&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>param&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;query&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;a=b&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Object user &lt;span style="color:#719e07">=&lt;/span> userService&lt;span style="color:#719e07">.&lt;/span>$invoke&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;get&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> parameterTypes&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[]{&lt;/span>param&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;sample one result: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> user&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上述代码的输出结果为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sample one result: &lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#268bd2">name&lt;/span>&lt;span style="color:#719e07">=&lt;/span>charles, &lt;span style="color:#268bd2">id&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1, &lt;span style="color:#268bd2">class&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.samples.generic.api.User&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里，Dubbo 框架会自动将 POJO 的返回值转换成 Map。可以看到，返回值 &lt;code>user&lt;/code> 是一个 HashMap，里面分别存放了 name、id、class 三个 k/v。&lt;/p>
&lt;h4 id="泛接口实现">泛接口实现&lt;/h4>
&lt;p>泛接口实现方式主要用于服务端没有 API 接口的情况，参数及返回值中的所有 POJO 均用 Map 表示，通常用于框架集成，如实现一个通用的远程服务 Mock 框架，可通过实现 GenericService 接口处理所有服务请求。&lt;/p>
&lt;h3 id="服务端实现-genericservice">服务端实现 GenericService&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">GenericServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> GenericService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Object &lt;span style="color:#268bd2">$invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String method&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> parameterTypes&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> GenericException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>method&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;hi&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hi, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> args&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>method&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;hello&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hello, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> args&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;welcome&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/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>ApplicationConfig application &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;api-generic-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>RegistryConfig registry &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>registry&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application&lt;span style="color:#719e07">.&lt;/span>setRegistry&lt;span style="color:#719e07">(&lt;/span>registry&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>GenericService genericService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> GenericServiceImpl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>GenericService&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;&lt;/span>GenericService&lt;span style="color:#719e07">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>application&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.generic.api.HelloService&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>genericService&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>GenericService&lt;span style="color:#719e07">&amp;gt;&lt;/span> service2 &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>GenericService&lt;span style="color:#719e07">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service2&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>application&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service2&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.generic.api.HiService&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service2&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>genericService&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>service2&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样，也可以使用 XML 配置的方式暴露服务；此时服务端是没有依赖 HiService 和 HelloService 这两个接口的。&lt;/p>
&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>ApplicationConfig application &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;api-generic-consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>RegistryConfig registry &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>registry&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://127.0.0.1:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application&lt;span style="color:#719e07">.&lt;/span>setRegistry&lt;span style="color:#719e07">(&lt;/span>registry&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>GenericService&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;&lt;/span>GenericService&lt;span style="color:#719e07">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 弱类型接口名
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>reference&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>HiService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>application&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>HiService hiService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>HiService&lt;span style="color:#719e07">)&lt;/span> reference&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>hiService&lt;span style="color:#719e07">.&lt;/span>hi&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>GenericService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference2 &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>GenericService&lt;span style="color:#719e07">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 弱类型接口名
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>reference2&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>HelloService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>reference2&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>application&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>HelloService helloService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>HelloService&lt;span style="color:#719e07">)&lt;/span> reference2&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>helloService&lt;span style="color:#719e07">.&lt;/span>hello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;community&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样，消费端也可以使用 XML 配置的方式引用服务，然后进行调用。这里可以看到调用方式为普通的服务调用，并非泛化调用。当然使用泛化调用也是可以的。&lt;/p>
&lt;p>到这里为止，一个简易的服务 Mock 平台就成功上线了！&lt;/p>
&lt;h2 id="其他">其他&lt;/h2>
&lt;ul>
&lt;li>本文介绍的泛化调用和泛接口实现，都是在原生的 &lt;code>Dubbo&lt;/code> 协议之上的。在 2.6.2 版本之前，其他协议如 http/hessian 等是不支持泛化调用的，2.6.3 版本将会对这两个协议的泛化调用做支持。&lt;/li>
&lt;li>本文中提到的相关示例代码可以在 dubbo-samples中找到：https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-generic&lt;/li>
&lt;/ul></description></item><item><title>Blog: Spring Boot Dubbo应用启停源码分析</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>&lt;span style="color:#719e07">(&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 style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboConsumerDemo&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SpringApplication&lt;span style="color:#719e07">.&lt;/span>run&lt;span style="color:#719e07">(&lt;/span>DubboConsumerDemo&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span>args&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其实要回答这样一个问题，我们首先需要把这个问题进行一个抽象，即一个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>
&lt;ol>
&lt;li>所有的非daemon进程完全终止&lt;/li>
&lt;li>某个线程调用了&lt;code>System.exit()&lt;/code>或&lt;code>Runtime.exit()&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>因此针对上面的情况，我们判断，一定是有某个非daemon线程没有退出导致。我们知道，通过jstack可以看到所有的线程信息，包括他们是否是daemon线程，可以通过jstack找出那些是非deamon的线程。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>➜ jstack &lt;span style="color:#2aa198">57785&lt;/span> | grep tid | grep -v &lt;span style="color:#2aa198">&amp;#34;daemon&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;container-0&amp;#34;&lt;/span> &lt;span style="color:#586e75">#37 prio=5 os_prio=31 tid=0x00007fbe312f5800 nid=0x7103 waiting on condition [0x0000700010144000]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;container-1&amp;#34;&lt;/span> &lt;span style="color:#586e75">#49 prio=5 os_prio=31 tid=0x00007fbe3117f800 nid=0x7b03 waiting on condition [0x0000700010859000]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;DestroyJavaVM&amp;#34;&lt;/span> &lt;span style="color:#586e75">#83 prio=5 os_prio=31 tid=0x00007fbe30011000 nid=0x2703 waiting on condition [0x0000000000000000]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;VM Thread&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe3005e800 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x3703 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;GC Thread#0&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe30013800 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x5403 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;GC Thread#1&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe30021000 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x5303 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;GC Thread#2&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe30021800 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x2d03 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;GC Thread#3&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe30022000 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x2f03 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;G1 Main Marker&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe30040800 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x5203 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;G1 Conc#0&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe30041000 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x4f03 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;G1 Refine#0&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe31044800 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x4e03 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;G1 Refine#1&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe31045800 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x4d03 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;G1 Refine#2&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe31046000 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x4c03 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;G1 Refine#3&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe31047000 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x4b03 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;G1 Young RemSet Sampling&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe31047800 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x3603 runnable
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;VM Periodic Task Thread&amp;#34;&lt;/span> &lt;span style="color:#268bd2">os_prio&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">31&lt;/span> &lt;span style="color:#268bd2">tid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x00007fbe31129000 &lt;span style="color:#268bd2">nid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>0x6703 waiting on condition
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>此处通过grep tid 找出所有的线程摘要，通过grep -v找出不包含daemon关键字的行&lt;/p>
&lt;/blockquote>
&lt;p>通过上面的结果，我们发现了一些信息：&lt;/p>
&lt;ul>
&lt;li>有两个线程&lt;code>container-0&lt;/code>, &lt;code>container-1&lt;/code>非常可疑，他们是非daemon线程，处于wait状态&lt;/li>
&lt;li>有一些GC相关的线程，和VM打头的线程，也是非daemon线程，但他们很有可能是JVM自己的线程，在此暂时忽略。&lt;/li>
&lt;/ul>
&lt;p>综上，我们可以推断，很可能是因为&lt;code>container-0&lt;/code>和&lt;code>container-1&lt;/code>导致JVM没有退出。现在我们通过源码，搜索一下到底是谁创建的这两个线程。&lt;/p>
&lt;p>通过对spring-boot的源码分析，我们在&lt;code>org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer&lt;/code>的&lt;code>startDaemonAwaitThread&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:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startDaemonAwaitThread&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread awaitThread &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Thread&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;container-&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#719e07">(&lt;/span>containerCounter&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">run&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TomcatEmbeddedServletContainer&lt;span style="color:#719e07">.&lt;/span>this&lt;span style="color:#719e07">.&lt;/span>tomcat&lt;span style="color:#719e07">.&lt;/span>getServer&lt;span style="color:#719e07">().&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awaitThread&lt;span style="color:#719e07">.&lt;/span>setContextClassLoader&lt;span style="color:#719e07">(&lt;/span>getClass&lt;span style="color:#719e07">().&lt;/span>getClassLoader&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awaitThread&lt;span style="color:#719e07">.&lt;/span>setDaemon&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awaitThread&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这个方法加个断点，看下调用堆栈：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>initialize:115, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;init&amp;gt;:84, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>getTomcatEmbeddedServletContainer:554, TomcatEmbeddedServletContainerFactory (org.springframework.boot.context.embedded.tomcat)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>getEmbeddedServletContainer:179, TomcatEmbeddedServletContainerFactory (org.springframework.boot.context.embedded.tomcat)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>createEmbeddedServletContainer:164, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>onRefresh:134, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>refresh:537, AbstractApplicationContext (org.springframework.context.support)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>refresh:122, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>refresh:693, SpringApplication (org.springframework.boot)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>refreshContext:360, SpringApplication (org.springframework.boot)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>run:303, SpringApplication (org.springframework.boot)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>run:1118, SpringApplication (org.springframework.boot)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>run:1107, SpringApplication (org.springframework.boot)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main:35, DubboConsumerDemo (com.alibaba.boot.dubbo.demo.consumer.bootstrap)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到，spring-boot应用在启动的过程中，由于默认启动了Tomcat暴露HTTP服务，所以执行到了上述方法，而Tomcat启动的所有的线程，默认都是daemon线程，例如监听请求的Acceptor，工作线程池等等，如果这里不加控制的话，启动完成之后JVM也会退出。因此需要显式地启动一个线程，在某个条件下进行持续等待，从而避免线程退出。&lt;/p>
&lt;p>下面我们在深挖一下，在Tomcat的&lt;code>this.tomcat.getServer().await()&lt;/code>这个方法中，线程是如何实现不退出的。这里为了阅读方便，去掉了不相关的代码。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">await&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span>&lt;span style="color:#719e07">(&lt;/span> port&lt;span style="color:#719e07">==-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span> &lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awaitThread &lt;span style="color:#719e07">=&lt;/span> Thread&lt;span style="color:#719e07">.&lt;/span>currentThread&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span>&lt;span style="color:#719e07">(!&lt;/span>stopAwait&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span> &lt;span style="color:#2aa198">10000&lt;/span> &lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span>&lt;span style="color:#719e07">(&lt;/span> InterruptedException ex &lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// continue and check the flag
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awaitThread &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在await方法中，实际上当前线程在一个while循环中每10秒检查一次 &lt;code>stopAwait&lt;/code>这个变量，它是一个&lt;code>volatile&lt;/code>类型变量，用于确保被另一个线程修改后，当前线程能够立即看到这个变化。如果没有变化，就会一直处于while循环中。这就是该线程不退出的原因，也就是整个spring-boot应用不退出的原因。&lt;/p>
&lt;p>因为Springboot应用同时启动了8080和8081(management port)两个端口，实际是启动了两个Tomcat，因此会有两个线程&lt;code>container-0&lt;/code>和&lt;code>container-1&lt;/code>。&lt;/p>
&lt;p>接下来，我们再看看，这个Spring-boot应用又是如何退出的呢？&lt;/p>
&lt;h2 id="dubboconsumer退出分析">DubboConsumer退出分析&lt;/h2>
&lt;p>在前面的描述中提到，有一个线程持续的在检查&lt;code>stopAwait&lt;/code>这个变量，那么我们自然想到，在Stop的时候，应该会有一个线程去修改&lt;code>stopAwait&lt;/code>，打破这个while循环，那又是谁在修改这个变量呢？&lt;/p>
&lt;p>通过对源码分析，可以看到只有一个方法修改了&lt;code>stopAwait&lt;/code>,即&lt;code>org.apache.catalina.core.StandardServer#stopAwait&lt;/code>，我们在此处加个断点，看看是谁在调用。&lt;/p>
&lt;blockquote>
&lt;p>注意，当我们在Intellij IDEA的Debug模式，加上一个断点后，需要在命令行下使用&lt;code>kill -s INT $PID&lt;/code>或者&lt;code>kill -s TERM $PID&lt;/code>才能触发断点，点击IDE上的Stop按钮，不会触发断点。这是IDEA的bug&lt;/p>
&lt;/blockquote>
&lt;p>可以看到有一个名为&lt;code>Thread-3&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>stopAwait:&lt;span style="color:#2aa198">390&lt;/span>&lt;span style="color:#719e07">,&lt;/span> StandardServer &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>catalina&lt;span style="color:#719e07">.&lt;/span>core&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>stopInternal:&lt;span style="color:#2aa198">819&lt;/span>&lt;span style="color:#719e07">,&lt;/span> StandardServer &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>catalina&lt;span style="color:#719e07">.&lt;/span>core&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>stop:&lt;span style="color:#2aa198">226&lt;/span>&lt;span style="color:#719e07">,&lt;/span> LifecycleBase &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>catalina&lt;span style="color:#719e07">.&lt;/span>util&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>stop:&lt;span style="color:#2aa198">377&lt;/span>&lt;span style="color:#719e07">,&lt;/span> Tomcat &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>catalina&lt;span style="color:#719e07">.&lt;/span>startup&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>stopTomcat:&lt;span style="color:#2aa198">241&lt;/span>&lt;span style="color:#719e07">,&lt;/span> TomcatEmbeddedServletContainer &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>springframework&lt;span style="color:#719e07">.&lt;/span>boot&lt;span style="color:#719e07">.&lt;/span>context&lt;span style="color:#719e07">.&lt;/span>embedded&lt;span style="color:#719e07">.&lt;/span>tomcat&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>stop:&lt;span style="color:#2aa198">295&lt;/span>&lt;span style="color:#719e07">,&lt;/span> TomcatEmbeddedServletContainer &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>springframework&lt;span style="color:#719e07">.&lt;/span>boot&lt;span style="color:#719e07">.&lt;/span>context&lt;span style="color:#719e07">.&lt;/span>embedded&lt;span style="color:#719e07">.&lt;/span>tomcat&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>stopAndReleaseEmbeddedServletContainer:&lt;span style="color:#2aa198">306&lt;/span>&lt;span style="color:#719e07">,&lt;/span> EmbeddedWebApplicationContext &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>springframework&lt;span style="color:#719e07">.&lt;/span>boot&lt;span style="color:#719e07">.&lt;/span>context&lt;span style="color:#719e07">.&lt;/span>embedded&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>onClose:&lt;span style="color:#2aa198">155&lt;/span>&lt;span style="color:#719e07">,&lt;/span> EmbeddedWebApplicationContext &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>springframework&lt;span style="color:#719e07">.&lt;/span>boot&lt;span style="color:#719e07">.&lt;/span>context&lt;span style="color:#719e07">.&lt;/span>embedded&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>doClose:&lt;span style="color:#2aa198">1014&lt;/span>&lt;span style="color:#719e07">,&lt;/span> AbstractApplicationContext &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>springframework&lt;span style="color:#719e07">.&lt;/span>context&lt;span style="color:#719e07">.&lt;/span>support&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>run:&lt;span style="color:#2aa198">929&lt;/span>&lt;span style="color:#719e07">,&lt;/span> AbstractApplicationContext$2 &lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>springframework&lt;span style="color:#719e07">.&lt;/span>context&lt;span style="color:#719e07">.&lt;/span>support&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过源码分析，原来是通过Spring注册的&lt;code>ShutdownHook&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">@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">registerShutdownHook&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>shutdownHook &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// No shutdown hook registered yet.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>shutdownHook &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Thread&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">run&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>startupShutdownMonitor&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doClose&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Runtime&lt;span style="color:#719e07">.&lt;/span>getRuntime&lt;span style="color:#719e07">().&lt;/span>addShutdownHook&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>shutdownHook&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过查阅Java的API文档[2], 我们可以知道ShutdownHook将在下面两种情况下执行&lt;/p>
&lt;blockquote>
&lt;p>The Java virtual machine &lt;em>shuts down&lt;/em> in response to two kinds of events:&lt;/p>
&lt;ul>
&lt;li>The program &lt;em>exits&lt;/em> normally, when the last non-daemon thread exits or when the &lt;code>exit&lt;/code> (equivalently, &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#exit-int-">&lt;code>System.exit&lt;/code>&lt;/a>) method is invoked, or&lt;/li>
&lt;li>The virtual machine is &lt;em>terminated&lt;/em> in response to a user interrupt, such as typing &lt;code>^C&lt;/code>, or a system-wide event, such as user logoff or system shutdown.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ol>
&lt;li>调用了System.exit()方法&lt;/li>
&lt;li>响应外部的信号，例如Ctrl+C（其实发送的是SIGINT信号），或者是&lt;code>SIGTERM&lt;/code>信号（默认&lt;code>kill $PID&lt;/code>发送的是&lt;code>SIGTERM&lt;/code>信号）&lt;/li>
&lt;/ol>
&lt;p>因此，正常的应用在停止过程中(&lt;code>kill -9 $PID&lt;/code>除外)，都会执行上述ShutdownHook，它的作用不仅仅是关闭tomcat，还有进行其他的清理工作，在此不再赘述。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;ol>
&lt;li>在&lt;code>DubboConsumer&lt;/code>启动的过程中，通过启动一个独立的非daemon线程循环检查变量的状态，确保进程不退出&lt;/li>
&lt;li>在&lt;code>DubboConsumer&lt;/code>停止的过程中，通过执行spring容器的shutdownhook，修改了变量的状态，使得程序正常退出&lt;/li>
&lt;/ol>
&lt;h2 id="问题">问题&lt;/h2>
&lt;p>在DubboProvider的例子中，我们看到Provider并没有启动Tomcat提供HTTP服务，那又是如何实现不退出的呢？我们将在下一篇文章中回答这个问题。&lt;/p>
&lt;h3 id="彩蛋">彩蛋&lt;/h3>
&lt;p>在&lt;code>Intellij IDEA&lt;/code>中运行了如下的单元测试，创建一个线程执行睡眠1000秒的操作，我们惊奇的发现，代码并没有线程执行完就退出了，这又是为什么呢？（被创建的线程是非daemon线程）&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Test&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">test&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Thread&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> Runnable&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">run&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread&lt;span style="color:#719e07">.&lt;/span>sleep&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1000000&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>InterruptedException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}).&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>[1] &lt;a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.8">https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.8&lt;/a>&lt;/p>
&lt;p>[2] &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook">https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook&lt;/a>&lt;/p></description></item><item><title>Blog: 通过QoS对服务进行动态控制</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/qos-architecture.png" alt="undefined">&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>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo&amp;gt;help
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+---------+----------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| help | help command |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+---------+----------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| ls | ls service |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+---------+----------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| offline | offline dubbo |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+---------+----------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| online | online dubbo |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+---------+----------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| quit | quit telnet console |
&lt;/span>&lt;/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;/p>
&lt;p>也可以对单个命令进行help操作，可以看到该命令对应的示例&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo&amp;gt;help online
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| COMMAND NAME | online |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| EXAMPLE | online dubbo |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| | online xx.xx.xxx.service |
&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>ls&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>dubbo&amp;gt;ls
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>As Provider side:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| Provider Service Name |PUB|
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|org.apache.dubbo.demo.provider.DemoService| Y |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>As Consumer side:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+---------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|Consumer Service Name|NUM|
&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>org.apache.dubbo.demo.provider.DemoService&lt;/code>，第二列里面的&lt;code>PUB=Y&lt;/code>代表改服务已经发布到注册中心，可供消费端进行调用。&lt;/p>
&lt;p>假设我们需要动态的对该服务进行下线操作，可以通过&lt;code>offline&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>dubbo&amp;gt;offline org.apache.dubbo.demo.provider.DemoService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>OK
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到命令返回了OK，我们再通过ls看下当前的状态：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo&amp;gt;ls
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>As Provider side:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| Provider Service Name |PUB|
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|org.apache.dubbo.demo.provider.DemoService| N |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>As Consumer side:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+---------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|Consumer Service Name|NUM|
&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>org.apache.dubbo.demo.provider.DemoService&lt;/code>的&lt;code>PUB&lt;/code>已经被设置成了&lt;code>N&lt;/code>。&lt;/p>
&lt;p>通过&lt;code>quit&lt;/code>命令退出当前的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>dubbo&amp;gt;quit
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>BYE!
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Connection closed by foreign host.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="通过http方式访问qos">通过HTTP方式访问QOS&lt;/h3>
&lt;p>在上面的例子中，我们已经对&lt;code>org.apache.dubbo.demo.provider.DemoService&lt;/code>进行了下线操作，下面，我们通过对Http方式对上面的服务进行注册操作：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>$ curl -i http://localhost:22222/online?service=org.apache.dubbo.demo.provider.DemoService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>HTTP/1.1 200 OK
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Content-Type: text/plain
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Content-Length: 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>OK%
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>注意online操作对应的参数，需要以&lt;code>key=value&lt;/code>的形式提供，但实际上key会被忽略&lt;/p>
&lt;/blockquote>
&lt;p>看到操作返回了OK，下面通过ls命令查看下当前的状态&lt;/p>
&lt;div class="highlight">&lt;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>$ curl -i http://localhost:22222/ls
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>HTTP/1.1 200 OK
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Content-Type: text/plain
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Content-Length: 365
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>As Provider side:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| Provider Service Name |PUB|
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|org.apache.dubbo.demo.provider.DemoService| Y |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>As Consumer side:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+---------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|Consumer Service Name|NUM|
&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>PUB&lt;/code>状态已经变成了&lt;code>Y&lt;/code>。&lt;/p>
&lt;h2 id="qos相关参数说明">QoS相关参数说明&lt;/h2>
&lt;p>QoS提供了一些启动参数，来对启动进行配置，他们主要包括：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数&lt;/th>
&lt;th>说明&lt;/th>
&lt;th>默认值&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>qosEnable&lt;/td>
&lt;td>是否启动QoS&lt;/td>
&lt;td>true&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qosPort&lt;/td>
&lt;td>启动QoS绑定的端口&lt;/td>
&lt;td>22222&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qosAcceptForeignIp&lt;/td>
&lt;td>是否允许远程访问&lt;/td>
&lt;td>false&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>注意，从2.6.4/2.7.0开始，qosAcceptForeignIp默认配置改为false，如果qosAcceptForeignIp设置为true，有可能带来安全风险，请仔细评估后再打开。&lt;/p>
&lt;/blockquote>
&lt;p>QoS参数可以通过如下方式进行配置&lt;/p>
&lt;ul>
&lt;li>系统属性&lt;/li>
&lt;li>dubbo.properties&lt;/li>
&lt;li>XML方式&lt;/li>
&lt;li>Spring-boot自动装配方式&lt;/li>
&lt;/ul>
&lt;p>其中，上述方式的优先顺序为系统属性 &amp;gt; dubbo.properties &amp;gt; XML/Spring-boot自动装配方式。&lt;/p>
&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>-Ddubbo.application.qos.enable=true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-Ddubbo.application.qos.port=33333
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-Ddubbo.application.qos.accept.foreign.ip=false
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="使用dubboproperties文件进行配置">使用dubbo.properties文件进行配置&lt;/h3>
&lt;p>在项目的&lt;code>src/main/resources&lt;/code>目录下添加dubbo.properties文件，内容如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo.application.qos.enable=true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qos.port=33333
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qos.accept.foreign.ip=false
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="使用xml方法进行配置">使用XML方法进行配置&lt;/h3>
&lt;p>如果要通过XML配置响应的QoS相关的参数，可以进行如下配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-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=&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> 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://dubbo.apache.org/schema/dubbo&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
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> http://www.springframework.org/schema/beans/spring-beans.xsd
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/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 style="color:#268bd2">&amp;lt;dubbo:application&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;demo-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:parameter&lt;/span> key=&lt;span style="color:#2aa198">&amp;#34;qos.enable&amp;#34;&lt;/span> value=&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:parameter&lt;/span> key=&lt;span style="color:#2aa198">&amp;#34;qos.accept.foreign.ip&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;false&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:parameter&lt;/span> key=&lt;span style="color:#2aa198">&amp;#34;qos.port&amp;#34;&lt;/span> value=&lt;span style="color:#2aa198">&amp;#34;33333&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:application&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;multicast://224.5.6.7:1234&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;20880&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.demo.provider.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;bean&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> class=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.demo.provider.DemoServiceImpl&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/beans&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="使用spring-boot自动装配方式配置">使用spring-boot自动装配方式配置&lt;/h3>
&lt;p>如果是spring-boot的应用，可以在&lt;code>application.properties&lt;/code>或者&lt;code>application.yml&lt;/code>上配置:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo.application.qosEnable=true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosPort=33333
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosAcceptForeignIp=false
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: Dubbo的负载均衡</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>invoker&lt;/th>
&lt;th>weight&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>A&lt;/td>
&lt;td>10&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>B&lt;/td>
&lt;td>20&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>C&lt;/td>
&lt;td>20&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>D&lt;/td>
&lt;td>30&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A，B，C和D总的权重是10 + 20 + 20 + 30 = 80。将80个数分布在如下的图中:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>+-----------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| | | | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+-----------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>1 10 30 50 80
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|-----A----|---------B----------|----------C---------|---------------D--------------|
&lt;/span>&lt;/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>---------------------15
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-------------------------------------------37
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-----------------------------------------------------------54
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面的图中一共有4块区域，长度分别是A，B，C和D的权重。使用random.nextInt(10 + 20 + 20 + 30)，从80个数中随机选择一个。然后再判断该数分布在哪个区域。比如，如果随机到37，37是分布在C区域的，那么就选择 Invoker C。15是在B区域，54是在D区域。&lt;/p>
&lt;p>&lt;strong>随机负载均衡源码&lt;/strong>&lt;/p>
&lt;p>下面是随机负载均衡的源码，为了方便阅读和理解，我把无关部分都去掉了。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public class RandomLoadBalance extends AbstractLoadBalance {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private final Random random = new Random();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protected &amp;lt;T&amp;gt; Invoker&amp;lt;T&amp;gt; doSelect(List&amp;lt;Invoker&amp;lt;T&amp;gt;&amp;gt; invokers, URL url, Invocation invocation) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int length = invokers.size(); // Invoker 总数
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int totalWeight = 0; // 所有 Invoker 的权重的和
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 判断是不是所有的 Invoker 的权重都是一样的
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 如果权重都一样，就简单了。直接用Random生成索引就可以了。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> boolean sameWeight = true;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> for (int i = 0; i &amp;lt; length; i++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int weight = getWeight(invokers.get(i), invocation);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> totalWeight += weight; // Sum
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if (sameWeight &amp;amp;&amp;amp; i &amp;gt; 0 &amp;amp;&amp;amp; weight != getWeight(invokers.get(i - 1), invocation)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sameWeight = false;
&lt;/span>&lt;/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> if (totalWeight &amp;gt; 0 &amp;amp;&amp;amp; !sameWeight) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 如果不是所有的 Invoker 权重都相同，那么基于权重来随机选择。权重越大的，被选中的概率越大
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int offset = random.nextInt(totalWeight);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> for (int i = 0; i &amp;lt; length; i++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> offset -= getWeight(invokers.get(i), invocation);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if (offset &amp;lt; 0) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return invokers.get(i);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 如果所有 Invoker 权重相同
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return invokers.get(random.nextInt(length));
&lt;/span>&lt;/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="2轮询负载均衡">2.轮询负载均衡&lt;/h3>
&lt;p>轮询负载均衡，就是依次的调用所有的 Provider。和随机负载均衡策略一样，轮询负载均衡策略也有权重的概念。 轮询负载均衡算法可以让RPC调用严格按照我们设置的比例来分配。不管是少量的调用还是大量的调用。但是轮询负载均衡算法也有不足的地方，存在慢的 Provider 累积请求的问题，比如：第二台机器很慢，但没挂，当请求调到第二台时就卡在那，久而久之，所有请求都卡在调到第二台上，导致整个系统变慢。&lt;/p>
&lt;h3 id="3最少活跃调用数负载均衡">3.最少活跃调用数负载均衡&lt;/h3>
&lt;p>官方解释：&lt;/p>
&lt;blockquote>
&lt;p>最少活跃调用数，相同活跃数的随机，活跃数指调用前后计数差，使慢的机器收到更少。&lt;/p>
&lt;/blockquote>
&lt;p>这个解释好像说的不是太明白。目的是让更慢的机器收到更少的请求，但具体怎么实现的还是不太清楚。举个例子：每个服务维护一个活跃数计数器。当A机器开始处理请求，该计数器加1，此时A还未处理完成。若处理完毕则计数器减1。而B机器接受到请求后很快处理完毕。那么A,B的活跃数分别是1，0。当又产生了一个新的请求，则选择B机器去执行(B活跃数最小)，这样使慢的机器A收到少的请求。&lt;/p>
&lt;p>处理一个新的请求时，Consumer 会检查所有 Provider 的活跃数，如果具有最小活跃数的 Invoker 只有一个，直接返回该 Invoker：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>if (leastCount == 1) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 如果只有一个最小则直接返回
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return invokers.get(leastIndexs[0]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果最小活跃数的 Invoker 有多个，且权重不相等同时总权重大于0，这时随机生成一个权重，范围在 (0，totalWeight) 间内。最后根据随机生成的权重，来选择 Invoker。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>if (! sameWeight &amp;amp;&amp;amp; totalWeight &amp;gt; 0) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 如果权重不相同且权重大于0则按总权重数随机
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int offsetWeight = random.nextInt(totalWeight);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 并确定随机值落在哪个片断上
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> for (int i = 0; i &amp;lt; leastCount; i++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int leastIndex = leastIndexs[i];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if (offsetWeight &amp;lt;= 0)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return invokers.get(leastIndex);
&lt;/span>&lt;/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="4一致性hash算法">4.一致性Hash算法&lt;/h3>
&lt;p>使用一致性 Hash 算法，让相同参数的请求总是发到同一 Provider。 当某一台 Provider 崩溃时，原本发往该 Provider 的请求，基于虚拟节点，平摊到其它 Provider，不会引起剧烈变动。 算法参见：&lt;a href="http://en.wikipedia.org/wiki/Consistent_hashing">http://en.wikipedia.org/wiki/Consistent_hashing&lt;/a>。&lt;/p>
&lt;p>缺省只对第一个参数Hash，如果要修改，请配置:&lt;/p>
&lt;div class="highlight">&lt;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:parameter key=&amp;#34;hash.arguments&amp;#34; value=&amp;#34;0,1&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>缺省用160份虚拟节点，如果要修改，请配置:&lt;/p>
&lt;div class="highlight">&lt;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:parameter key=&amp;#34;hash.nodes&amp;#34; value=&amp;#34;320&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>一致性Hash算法可以和缓存机制配合起来使用。比如有一个服务getUserInfo(String userId)。设置了Hash算法后，相同的userId的调用，都会发送到同一个 Provider。这个 Provider 上可以把用户数据在内存中进行缓存，减少访问数据库或分布式缓存的次数。如果业务上允许这部分数据有一段时间的不一致，可以考虑这种做法。减少对数据库，缓存等中间件的依赖和访问次数，同时减少了网络IO操作，提高系统性能。&lt;/p>
&lt;h2 id="负载均衡配置">负载均衡配置&lt;/h2>
&lt;p>如果不指定负载均衡，默认使用随机负载均衡。我们也可以根据自己的需要，显式指定一个负载均衡。 可以在多个地方类来配置负载均衡，比如 Provider 端，Consumer端，服务级别，方法级别等。&lt;/p>
&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;dubbo:service interface=&amp;#34;...&amp;#34; loadbalance=&amp;#34;roundrobin&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该服务的所有方法都使用roundrobin负载均衡。&lt;/p>
&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;dubbo:reference interface=&amp;#34;...&amp;#34; loadbalance=&amp;#34;roundrobin&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该服务的所有方法都使用roundrobin负载均衡。&lt;/p>
&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;dubbo:service interface=&amp;#34;...&amp;#34;&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:method name=&amp;#34;hello&amp;#34; loadbalance=&amp;#34;roundrobin&amp;#34;/&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/dubbo:service&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>只有该服务的hello方法使用roundrobin负载均衡。&lt;/p>
&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;dubbo:reference interface=&amp;#34;...&amp;#34;&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;dubbo:method name=&amp;#34;hello&amp;#34; loadbalance=&amp;#34;roundrobin&amp;#34;/&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/dubbo:reference&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>只有该服务的hello方法使用roundrobin负载均衡。&lt;/p>
&lt;p>和Dubbo其他的配置类似，多个配置是有覆盖关系的：&lt;/p>
&lt;ol>
&lt;li>方法级优先，接口级次之，全局配置再次之。&lt;/li>
&lt;li>如果级别一样，则消费方优先，提供方次之。&lt;/li>
&lt;/ol>
&lt;p>所以，上面4种配置的优先级是:&lt;/p>
&lt;ol>
&lt;li>客户端方法级别配置。&lt;/li>
&lt;li>客户端接口级别配置。&lt;/li>
&lt;li>服务端方法级别配置。&lt;/li>
&lt;li>服务端接口级别配置。&lt;/li>
&lt;/ol>
&lt;h2 id="扩展负载均衡">扩展负载均衡&lt;/h2>
&lt;p>Dubbo的4种负载均衡的实现，大多数情况下能满足要求。有时候，因为业务的需要，我们可能需要实现自己的负载均衡策略。本章只说明如何配置负载均衡算法。关于Dubbo扩展机制的更多内容，请前往&lt;a href="https://cn.dubbo.apache.org/zh/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>。&lt;/p>
&lt;ol>
&lt;li>实现LoadBalance接口, 以下是Dubbo的LoadBalance接口:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>@SPI(RandomLoadBalance.NAME)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public interface LoadBalance {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Adaptive(&amp;#34;loadbalance&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;T&amp;gt; Invoker&amp;lt;T&amp;gt; select(List&amp;lt;Invoker&amp;lt;T&amp;gt;&amp;gt; invokers, URL url, Invocation invocation) throws RpcException;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这是SPI的接口，select方法的参数如下:&lt;/p>
&lt;ul>
&lt;li>invokers: 所有的服务 Provider 列表。&lt;/li>
&lt;li>url: 一些配置信息，比如接口名，是否check，序列化方式。&lt;/li>
&lt;li>invocation: RPC调用的信息，包括方法名，方法参数类型，方法参数。 下面是我们自己实现的一个LoadBalance，实现很简单，选择第一个 Invoker:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.demo.dubbo;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class DemoLoadBalance implements LoadBalance {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Override
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public &amp;lt;T&amp;gt; Invoker&amp;lt;T&amp;gt; &lt;span style="color:#719e07">select&lt;/span>(List&amp;lt;Invoker&amp;lt;T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> invokers, URL url, Invocation invocation) throws RpcException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.&lt;span style="color:#b58900">println&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;[DemoLoadBalance]Select the first invoker...&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invokers.&lt;span style="color:#268bd2">get&lt;/span>(&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>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>添加资源文件 添加文件:&lt;code>src/main/resource/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>demo=my=com.demo.dubbo.DemoLoadBalance
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>配置使用自定义LoadBalance&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>&amp;lt;dubbo:reference id=&amp;#34;helloService&amp;#34; interface=&amp;#34;com.demo.dubbo.api.IHelloService&amp;#34; loadbalance=&amp;#34;demo&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在Consumer端的&lt;code>dubbo:reference&lt;/code>中配置&lt;code>&amp;lt;loadbalance=&amp;quot;demo&amp;quot;&amp;gt;&lt;/code>&lt;/p>
&lt;p>经过上面的3个步骤，我们编写了一个自定义的LoadBalance，并告诉Dubbo使用它了。启动Dubbo，我们就能看到Dubbo已经使用了自定义的DemoLoadBalance。&lt;/p></description></item><item><title>Blog: Dubbo 注解驱动</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 上的 issue &lt;a href="https://github.com/alibaba/dubbo/issues/794">https://github.com/alibaba/dubbo/issues/794&lt;/a>：&lt;/p>
&lt;blockquote>
&lt;p>关于dubbo @Service注解生成ServiceBean时, interface获取成spring 的代理对象的bug&lt;/p>
&lt;blockquote>
&lt;p>在项目里， 我使用了&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Transactional&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@com.alibaba.dubbo.config.annotation.Service&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">SUserJpushServiceImp&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>的形式， 来暴露服务。但是在发布服务的时候， interface class 是通过
&lt;code>serviceConfig.setInterface(bean.getClass().getInterfaces()[0]);&lt;/code>
的形式获取， 刚好， 我的service都使用了@Transactional注解， 对象被代理了。所以获取到的interface是Spring的代理接口&amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;/blockquote>
&lt;p>不少热心的小伙伴不仅发现这个历史遗留问题，而且提出了一些修复方案。同时，为了更好地适配 Spring 生命周期以及将 Dubbo 完全向注解驱动编程模型过渡，因此，引入了全新 Dubbo 组件扫描注解 - &lt;code>@DubboComponentScan&lt;/code>。&lt;/p>
&lt;blockquote>
&lt;p>注： &lt;code>&amp;lt;dubbo:annotation&amp;gt; &lt;/code> Spring AOP 问题将在 &lt;code>2.5.9&lt;/code> 中修复：https://github.com/alibaba/dubbo/issues/1125&lt;/p>
&lt;/blockquote>
&lt;h5 id="3-reference-不支持字段继承性">3. @Reference 不支持字段继承性&lt;/h5>
&lt;p>假设有一个 Spring Bean &lt;code>AnnotationAction&lt;/code> 直接通过字段&lt;code>annotationService&lt;/code> 标记 &lt;code>@Reference&lt;/code> 引用 &lt;code>AnnotationService&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> com.alibaba.dubbo.examples.annotation.action&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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> com.alibaba.dubbo.config.annotation.Reference&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.examples.annotation.api.AnnotationService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.stereotype.Component&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Component&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;annotationAction&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AnnotationAction&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Reference&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> AnnotationService annotationService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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> String &lt;span style="color:#268bd2">doSayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> annotationService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当&lt;code>AnnotationAction&lt;/code> 被 XML 元素 &lt;code>&amp;lt;dubbo:annotation&amp;gt;&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:annotation&lt;/span> package=&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.examples.annotation.action&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;code>annotationService&lt;/code> 能够引用到 &lt;code>AnnotationService&lt;/code>，执行 &lt;code>doSayHello&lt;/code> 方法能够正常返回。&lt;/p>
&lt;p>如果将字段&lt;code>annotationService&lt;/code> 抽取到&lt;code>AnnotationAction&lt;/code> 的父类&lt;code>BaseAction&lt;/code> 后，&lt;code>AnnotationService&lt;/code> 无法再被引用，改造如下所示：&lt;/p>
&lt;p>&lt;code>AnnotationAction.java&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">@Component&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;annotationAction&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AnnotationAction&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> BaseAction &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&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> String &lt;span style="color:#268bd2">doSayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getAnnotationService&lt;span style="color:#719e07">().&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>BaseAction.java&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">abstract&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">BaseAction&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Reference&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> AnnotationService annotationService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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">protected&lt;/span> AnnotationService &lt;span style="color:#268bd2">getAnnotationService&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> annotationService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>改造后，再次执行 &lt;code>doSayHello&lt;/code> 方法，&lt;code>NullPointerException&lt;/code> 将会被抛出。说明&lt;code>&amp;lt;dubbo:annotation&amp;gt;&lt;/code> 并不支持&lt;code>@Reference&lt;/code> 字段继承性。&lt;/p>
&lt;p>了解了历史问题，集合整体愿景，下面介绍&lt;code>@DubboComponentScan&lt;/code> 的设计原则。&lt;/p>
&lt;h4 id="设计原则">设计原则&lt;/h4>
&lt;p>Spring Framework 3.1 引入了新 Annotation - &lt;code>@ComponentScan&lt;/code> ， 完全替代了 XML 元素 &lt;code> &amp;lt;context:component-scan&amp;gt;&lt;/code> 。同样， &lt;code>@DubboComponentScan&lt;/code> 作为 Dubbo &lt;code>2.5.7&lt;/code> 新增的 Annotation，也是XML 元素 &lt;code>&amp;lt;dubbo:annotation&amp;gt;&lt;/code> 的替代方案。&lt;/p>
&lt;p>在命名上（类名以及属性方法），为了简化使用和关联记忆，Dubbo 组件扫描 Annotation &lt;code>@DubboComponentScan&lt;/code>，借鉴了 Spring Boot 1.3 引入的 &lt;code>@ServletComponentScan&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> DubboComponentScan &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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"> * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * declarations e.g.: {@code @DubboComponentScan(&amp;#34;org.my.pkg&amp;#34;)} instead of
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * {@code @DubboComponentScan(basePackages=&amp;#34;org.my.pkg&amp;#34;)}.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return the base packages to scan
&lt;/span>&lt;/span>&lt;/span>&lt;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:#719e07">[]&lt;/span> &lt;span style="color:#268bd2">value&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#719e07">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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. {@link #value()} is an
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * alias for (and mutually exclusive with) this attribute.
&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 #basePackageClasses()} 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"> */&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">basePackages&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#719e07">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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 #basePackages()} 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"> */&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> basePackageClasses&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#719e07">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>注意：&lt;code>basePackages()&lt;/code> 和 &lt;code>value()&lt;/code> 均能支持占位符（placeholder）指定的包名&lt;/p>
&lt;/blockquote>
&lt;p>在职责上，&lt;code>@DubboComponentScan&lt;/code> 相对于 Spring Boot &lt;code>@ServletComponentScan&lt;/code> 更为繁重，原因在于处理 Dubbo &lt;code>@Service&lt;/code> 类暴露 Dubbo 服务外，还有帮助 Spring Bean &lt;code>@Reference&lt;/code>字段或者方法注入 Dubbo 服务代理。&lt;/p>
&lt;p>在场景上，Spring Framework &lt;code>@ComponentScan&lt;/code> 组件扫描逻辑更为复杂。而在 &lt;code>@DubboComponentScan&lt;/code> 只需关注 &lt;code>@Service&lt;/code> 和 &lt;code>@Reference&lt;/code> 处理。&lt;/p>
&lt;p>在功能上， &lt;code>@DubboComponentScan&lt;/code> 不但需要提供完整 Spring AOP 支持的能力，而且还得具备&lt;code>@Reference &lt;/code> 字段可继承性的能力。&lt;/p>
&lt;p>了解基本设计原则后，下面通过完整的示例，简介&lt;code>@DubboComponentScan&lt;/code> 使用方法以及注意事项。&lt;/p>
&lt;h4 id="使用方法">使用方法&lt;/h4>
&lt;p>后续通过服务提供方（&lt;code>@Serivce&lt;/code>）以及服务消费方（&lt;code>@Reference&lt;/code>）两部分来介绍&lt;code>@DubboComponentScan&lt;/code> 使用方法。&lt;/p>
&lt;p>假设，服务提供方和服务消费分均依赖服务接口&lt;code>DemoService&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.demo&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="服务提供方serivce">服务提供方（&lt;code>@Serivce&lt;/code>）&lt;/h5>
&lt;h6 id="实现-demoservice">实现 &lt;code>DemoService&lt;/code>&lt;/h6>
&lt;p>服务提供方实现&lt;code>DemoService&lt;/code> - &lt;code>AnnotationDemoService&lt;/code> ，同时标注 Dubbo &lt;code>@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-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.demo.provider&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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> com.alibaba.dubbo.config.annotation.Service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.demo.DemoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Annotation {@link 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"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&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">AnnotationDemoService&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="服务提供方-annotation-配置">服务提供方 Annotation 配置&lt;/h6>
&lt;p>将 &lt;code>AnnotationDemoService&lt;/code> 暴露成Dubbo 服务，需要依赖 Spring Bean：&lt;code>ApplicationConfig&lt;/code>、&lt;code>ProtocolConfig&lt;/code> 以及 &lt;code>RegistryConfig&lt;/code> 。这三个 Spring Bean 过去可通过 XML 文件方式组装 Spring Bean：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&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
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198"> &amp;#34;&lt;/span>&lt;span style="color:#268bd2">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">&amp;lt;!-- 当前应用信息配置 --&amp;gt;&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;dubbo-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:#586e75">&amp;lt;!-- 连接注册中心配置 --&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> id=&lt;span style="color:#2aa198">&amp;#34;my-registry&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;N/A&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:protocol&lt;/span> name=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> port=&lt;span style="color:#2aa198">&amp;#34;12345&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/beans&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>以上装配方式不予推荐，推荐使用 Annotation 配置，因此可以换成 Spring &lt;code>@Configuration&lt;/code> 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:#719e07">package&lt;/span> com.alibaba.dubbo.demo.config&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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> com.alibaba.dubbo.config.ApplicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.config.ProtocolConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.config.RegistryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.context.annotation.Bean&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.context.annotation.Configuration&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@DubboComponentScan&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.demo.provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#586e75">// 扫描 Dubbo 组件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="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">@Bean&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-annotation-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> ApplicationConfig &lt;span style="color:#268bd2">applicationConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-annotation-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/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">@Bean&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;my-registry&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> RegistryConfig &lt;span style="color:#268bd2">registryConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RegistryConfig registryConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;N/A&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> registryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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">@Bean&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> ProtocolConfig &lt;span style="color:#268bd2">protocolConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ProtocolConfig protocolConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">12345&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocolConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&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-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.demo.bootstrap&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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> com.alibaba.dubbo.demo.DemoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.demo.config.ProviderConfiguration&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.context.annotation.AnnotationConfigApplicationContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 服务提供方引导类
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建 Annotation 配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注册配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>ProviderConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 启动上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取 DemoService Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> DemoService demoService &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 执行 sayHello 方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String message &lt;span style="color:#719e07">=&lt;/span> demoService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;World&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 控制台输出信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>ProviderBootstrap&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>Hello , World
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>以上直接结果说明 &lt;code>@DubboComponentScan(&amp;quot;com.alibaba.dubbo.demo.provider&amp;quot;)&lt;/code> 扫描后，标注 Dubbo &lt;code>@Service&lt;/code> 的 &lt;code>AnnotationDemoService&lt;/code> 被注册成 Spring Bean，可从 Spring ApplicationContext 自由获取。&lt;/p>
&lt;h5 id="服务消费方reference">服务消费方（&lt;code>@Reference&lt;/code>）&lt;/h5>
&lt;h6 id="服务-demoservice">服务 &lt;code>DemoService&lt;/code>&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-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.demo.consumer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.config.annotation.Reference&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.demo.DemoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Annotation 驱动 {@link 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"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AnnotationDemoServiceConsumer&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Reference&lt;/span>&lt;span style="color:#719e07">(&lt;/span>url &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo://127.0.0.1:12345&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DemoService demoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">doSayHell&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> demoService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="服务消费方-annotation-配置">服务消费方 Annotation 配置&lt;/h6>
&lt;p>与服务提供方配置类似，服务消费方也许 Dubbo 相关配置 Bean - &lt;code>ConsumerConfiguration&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> com.alibaba.dubbo.demo.config&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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> com.alibaba.dubbo.config.ApplicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.config.RegistryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.demo.consumer.AnnotationDemoServiceConsumer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.context.annotation.Bean&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.context.annotation.Configuration&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#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"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Configuration&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">class&lt;/span> &lt;span style="color:#268bd2">ConsumerConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 当前应用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&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">@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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-annotation-consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/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">@Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> RegistryConfig &lt;span style="color:#268bd2">registryConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RegistryConfig registryConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;N/A&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> registryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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"> * 注册 AnnotationDemoServiceConsumer，@DubboComponentScan 将处理其中 @Reference 字段。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 如果 AnnotationDemoServiceConsumer 非 Spring Bean 的话，
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 即使 @DubboComponentScan 指定 package 也不会进行处理，与 Spring @Autowired 同理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&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">@Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> AnnotationDemoServiceConsumer &lt;span style="color:#268bd2">annotationDemoServiceConsumer&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationDemoServiceConsumer&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="服务消费方引导类">服务消费方引导类&lt;/h6>
&lt;p>服务消费方需要先引导服务提供方，下面的实例将会启动两个 Spring 应用上下文，首先引导服务提供方 Spring 应用上下文，同时，需要复用前面Annotation 配置 &lt;code>ProviderConfiguration&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 启动服务提供方上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startProviderContext&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建 Annotation 配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationConfigApplicationContext providerContext &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注册配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> providerContext&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>ProviderConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 启动服务提供方上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> providerContext&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后引导服务消费方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-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"> * @return AnnotationConfigApplicationContext
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> ApplicationContext &lt;span style="color:#268bd2">startConsumerContext&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建服务消费方 Annotation 配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationConfigApplicationContext consumerContext &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注册服务消费方配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> consumerContext&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>ConsumerConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 启动服务消费方上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> consumerContext&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 返回服务消费方 Annotation 配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> consumerContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>完整的引导类实现：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.dubbo.demo.bootstrap&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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> com.alibaba.dubbo.demo.config.ConsumerConfiguration&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.demo.config.ProviderConfiguration&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.demo.consumer.AnnotationDemoServiceConsumer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.context.ApplicationContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.context.annotation.AnnotationConfigApplicationContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 服务消费端引导类
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ConsumerBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 启动服务提供方上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> startProviderContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 启动并且返回服务消费方上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationContext consumerContext &lt;span style="color:#719e07">=&lt;/span> startConsumerContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取 AnnotationDemoServiceConsumer Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationDemoServiceConsumer consumer &lt;span style="color:#719e07">=&lt;/span> consumerContext&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>AnnotationDemoServiceConsumer&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 执行 doSayHello 方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String message &lt;span style="color:#719e07">=&lt;/span> consumer&lt;span style="color:#719e07">.&lt;/span>doSayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;World&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 输出执行结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 启动并且返回服务消费方上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return AnnotationConfigApplicationContext
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> ApplicationContext &lt;span style="color:#268bd2">startConsumerContext&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建服务消费方 Annotation 配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationConfigApplicationContext consumerContext &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注册服务消费方配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> consumerContext&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>ConsumerConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 启动服务消费方上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> consumerContext&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 返回服务消费方 Annotation 配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> consumerContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 启动服务提供方上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startProviderContext&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建 Annotation 配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationConfigApplicationContext providerContext &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注册配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> providerContext&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>ProviderConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 启动服务提供方上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> providerContext&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>运行&lt;code>ConsumerBootstrap&lt;/code>结果，仍然符合期望，&lt;code>AnnotationDemoServiceConsumer&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>Hello , World
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="spring-aop-支持">Spring AOP 支持&lt;/h4>
&lt;p>前面提到 &lt;code>&amp;lt;dubbo:annotation&amp;gt; &lt;/code> 注册 Dubbo &lt;code>@Service&lt;/code> 组件后，在 Spring AOP 支持方面存在问题。事务作为 Spring AOP 的功能扩展，自然也会在 &lt;code>&amp;lt;dubbo:annotation&amp;gt; &lt;/code>中不支持。&lt;/p>
&lt;p>&lt;code>@DubboComponentScan&lt;/code> 针对以上问题，实现了对 Spring AOP 是完全兼容。将上述服务提供方 Annotation 配置做出一定的调整，标注&lt;code>@EnableTransactionManagement&lt;/code> 以及自定义实现&lt;code>PlatformTransactionManager&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">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@DubboComponentScan&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.demo.provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#586e75">// 扫描 Dubbo 组件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@EnableTransactionManagement&lt;/span> &lt;span style="color:#586e75">// 激活事务管理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略其他配置 Bean 定义
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 自定义事务管理器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&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">@Primary&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> PlatformTransactionManager &lt;span style="color:#268bd2">transactionManager&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> PlatformTransactionManager&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> TransactionStatus &lt;span style="color:#268bd2">getTransaction&lt;/span>&lt;span style="color:#719e07">(&lt;/span>TransactionDefinition definition&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> TransactionException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;get transaction ...&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> SimpleTransactionStatus&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">commit&lt;/span>&lt;span style="color:#719e07">(&lt;/span>TransactionStatus status&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> TransactionException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;commit transaction ...&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#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">rollback&lt;/span>&lt;span style="color:#719e07">(&lt;/span>TransactionStatus status&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> TransactionException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;rollback transaction ...&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同时调整 &lt;code>AnnotationDemoService&lt;/code> - 增加&lt;code>@Transactional&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">@Service&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Transactional&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">AnnotationDemoService&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 省略实现，保持不变
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>再次运行&lt;code>ConsumerBootstrap&lt;/code> , 观察控制台输出内容：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>get transaction ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>commit transaction ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Hello , World
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输入内容中多处了两行，说明自定义 &lt;code>PlatformTransactionManager&lt;/code> &lt;code>getTransaction(TransactionDefinition)&lt;/code> 以及 &lt;code>commit(TransactionStatus) &lt;/code> 方法被执行，进而说明 &lt;code>AnnotationDemoService&lt;/code> 的&lt;code>sayHello(String)&lt;/code> 方法执行时，事务也伴随执行。&lt;/p>
&lt;h4 id="注意事项">注意事项&lt;/h4>
&lt;p>&lt;code>ConsumerConfiguration&lt;/code> 上的 &lt;code>@DubboComponentScan&lt;/code> 并没有指定 &lt;code>basePackages&lt;/code> 扫描，这种情况会将&lt;code>ConsumerConfiguration&lt;/code> 当做 &lt;code>basePackageClasses&lt;/code> ，即扫描&lt;code>ConsumerConfiguration&lt;/code> 所属的 package &lt;code>com.alibaba.dubbo.demo.config&lt;/code> 以及子 package。由于当前示例中，不存在标注 Dubbo &lt;code>@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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>WARN : [DUBBO] No Spring Bean annotating Dubbo&amp;#39;s @Service was found in Spring BeanFactory, dubbo version: 2.0.0, current host: 127.0.0.1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>以上信息大可不必担忧，因为 &lt;code>@DubboComponentScan&lt;/code> 除了扫描 Dubbo &lt;code>@Service&lt;/code> 组件以外，还将处理 &lt;code>@Reference&lt;/code>字段注入。然而读者特别关注&lt;code>@Reference&lt;/code>字段注入的规则。&lt;/p>
&lt;p>以上实现为例，&lt;code>AnnotationDemoServiceConsumer&lt;/code> 必须申明为 Spring &lt;code>@Bean&lt;/code> 或者 &lt;code>@Component&lt;/code>（或者其派生注解），否则 &lt;code>@DubboComponentScan&lt;/code> 不会主动将标注 &lt;code>@Reference&lt;/code>字段所在的声明类提成为 Spring Bean，换句话说，如果 &lt;code>@Reference&lt;/code>字段所在的声明类不是 Spring Bean 的话， &lt;code>@DubboComponentScan&lt;/code> 不会处理&lt;code>@Reference&lt;/code>注入，其原理与 Spring &lt;code>@Autowired&lt;/code> 一致。&lt;/p>
&lt;p>以上使用不当可能会导致相关问题，如 GitHub 上曾有小伙伴提问：https://github.com/alibaba/dubbo/issues/825&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>li362692680&lt;/strong> 提问：&lt;/p>
&lt;blockquote>
&lt;p>@DubboComponentScan注解在消费端扫描包时扫描的是 @Service注解？？不是@Reference注解？？
启动时报
DubboComponentScanRegistrar-85]-[main]-[INFO] 0 annotated @Service Components { [] }&lt;/p>
&lt;/blockquote>
&lt;p>笔者(&lt;strong>mercyblitz&lt;/strong>)回复：&lt;/p>
&lt;blockquote>
&lt;p>&lt;code>@Reference&lt;/code> 类似于 &lt;code>@Autowired&lt;/code> 一样，首先其申明的类必须被 Spring 上下文当做一个Bean，因此，Dubbo 并没有直接将 &lt;code>@Reference&lt;/code> 字段所在的类提升成 Bean。&lt;/p>
&lt;p>综上所述，这并不是一个问题，而是用法不当！&lt;/p>
&lt;/blockquote>
&lt;/blockquote>
&lt;h4 id="已知问题">已知问题&lt;/h4>
&lt;p>最新发布的 Dubbo &lt;code>2.5.8&lt;/code> 中，&lt;code>@DubboComponentScan&lt;/code> 在以下特殊场景下存在 Spring &lt;code>@Service&lt;/code> 不兼容情况：&lt;/p>
&lt;blockquote>
&lt;p>假设有两个服务实现类 &lt;code>A&lt;/code> 和 &lt;code>B&lt;/code>，同时存放在&lt;code>com.acme&lt;/code> 包下：&lt;/p>
&lt;ul>
&lt;li>&lt;code>A&lt;/code> 标注 Dubbo &lt;code>@Service&lt;/code>&lt;/li>
&lt;li>&lt;code>B&lt;/code> 标注 Dubbo &lt;code>@Service&lt;/code> 和 Spring &lt;code>@Service&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>当 Spring &lt;code>@ComponentScan&lt;/code> 先扫描&lt;code>com.acme&lt;/code> 包时，&lt;code>B&lt;/code> 被当做 Spring Bean 的候选类。随后，&lt;code>@DubboComponentScan&lt;/code> 也扫描相同的包。当应用启动时，&lt;code>A&lt;/code> 和 &lt;code>B&lt;/code> 虽然都是 Spring Bean，可仅 &lt;code>A&lt;/code> 能够暴露 Dubbo 服务，&lt;code>B&lt;/code> 则丢失。&lt;/p>
&lt;/blockquote>
&lt;p>问题版本：&lt;code>2.5.7&lt;/code>、&lt;code>2.5.8&lt;/code>&lt;/p>
&lt;p>问题详情：https://github.com/alibaba/dubbo/issues/1120&lt;/p>
&lt;p>修复版本：&lt;code>2.5.9&lt;/code>（下个版本）&lt;/p></description></item><item><title>Blog: 第一个 Dubbo 应用</title><link>https://cn.dubbo.apache.org/zh/blog/2018/08/07/dubbo-101/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://cn.dubbo.apache.org/zh/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 src="https://cn.dubbo.apache.org/imgs/blog/rmi-flow.png" alt="RMI Flow">&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>
&lt;p>下面通过示例代码简单的展示 RMI 中的服务注册和发现&lt;/p>
&lt;h4 id="服务端的服务注册">服务端的服务注册&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>Hello obj &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HelloImpl&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>Hello stub &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Hello&lt;span style="color:#719e07">)&lt;/span> UnicastRemoteObject&lt;span style="color:#719e07">.&lt;/span>exportObject&lt;span style="color:#719e07">(&lt;/span>obj&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>Registry registry &lt;span style="color:#719e07">=&lt;/span> LocateRegistry&lt;span style="color:#719e07">.&lt;/span>createRegistry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1099&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>registry&lt;span style="color:#719e07">.&lt;/span>rebind&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> stub&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说明：&lt;/p>
&lt;ol>
&lt;li>初始化服务对象实例，&lt;/li>
&lt;li>通过 &lt;em>UnicastRemoteObject.exportObject&lt;/em> 生成可以与服务端通讯的 Stub 对象，&lt;/li>
&lt;li>创建一个本地的 RMI 注册服务，监听端口为 1099。该注册服务运行在服务端，也可以单独启动一个注册服务的进程，&lt;/li>
&lt;li>将 Stub 对象绑定到注册服务上，这样，客户端可以通过 &lt;em>Hello&lt;/em> 这个名字查找到该远程对象。&lt;/li>
&lt;/ol>
&lt;h4 id="客户端的服务发现">客户端的服务发现&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>Registry registry &lt;span style="color:#719e07">=&lt;/span> LocateRegistry&lt;span style="color:#719e07">.&lt;/span>getRegistry&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>Hello stub &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Hello&lt;span style="color:#719e07">)&lt;/span> registry&lt;span style="color:#719e07">.&lt;/span>lookup&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>String response &lt;span style="color:#719e07">=&lt;/span> stub&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说明：&lt;/p>
&lt;ol>
&lt;li>获取注册服务实例，在本例中，由于没有传入任何参数，假定要获取的注册服务实例部署在本机，并监听在 1099 端口上，&lt;/li>
&lt;li>从注册服务中查找服务名为 &lt;em>Hello&lt;/em> 的远程对象，&lt;/li>
&lt;li>通过获取的 Stub 对象发起一次 RMI 调用并获得结果。&lt;/li>
&lt;/ol>
&lt;p>理解 RMI 的工作原理和基本概念，对掌握现代分布式服务框架很有帮助，建议进一步的阅读 RMI 官方教材 &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>。&lt;/p>
&lt;h2 id="dubbo-基本概念">Dubbo 基本概念&lt;/h2>
&lt;p>现代的分布式服务框架的基本概念与 RMI 是类似的，同样是使用 Java 的 Interface 作为服务契约，通过注册中心来完成服务的注册和发现，远程通讯的细节也是通过代理类来屏蔽。具体来说，Dubbo 在工作时有以下四个角色参与：&lt;/p>
&lt;ol>
&lt;li>服务提供者 - 启动时在指定端口上暴露服务，并将服务地址和端口注册到注册中心上&lt;/li>
&lt;li>服务消费者 - 启动时向注册中心订阅自己感兴趣的服务，以便获得服务提供方的地址列表&lt;/li>
&lt;li>注册中心 - 负责服务的注册和发现，负责保存服务提供方上报的地址信息，并向服务消费方推送&lt;/li>
&lt;li>监控中心 - 负责收集服务提供方和消费方的运行状态，比如服务调用次数、延迟等，用于监控&lt;/li>
&lt;li>运行容器 - 负责服务提供方的初始化、加载以及运行的生命周期管理&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-architecture.png" alt="dubbo-architecture">&lt;/p>
&lt;p>&lt;strong>部署阶段&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>服务提供者在指定端口暴露服务，并向注册中心注册服务信息。&lt;/li>
&lt;li>服务消费者向注册中心发起服务地址列表的订阅。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>运行阶段&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>注册中心向服务消费者推送地址列表信息。&lt;/li>
&lt;li>服务消费者收到地址列表后，从其中选取一个向目标服务发起调用。&lt;/li>
&lt;li>调用过程服务消费者和服务提供者的运行状态上报给监控中心。&lt;/li>
&lt;/ul>
&lt;h2 id="基于-api-的-dubbo-应用">基于 API 的 Dubbo 应用&lt;/h2>
&lt;p>Dubbo 的应用一般都是通过 Spring 来组装的。为了快速获得一个可以工作的 Dubbo 应用，这里的示例摒弃了复杂的配置，而改用面向 Dubbo API 的方式来构建服务提供者和消费者，另外，注册中心和监控中心在本示例中也不需要安装和配置。&lt;/p>
&lt;p>在生产环境，Dubbo 的服务需要一个分布式的服务注册中心与之配合，比如，ZooKeeper。为了方便开发，Dubbo 提供了直连&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>以及组播&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>两种方式，从而避免额外搭建注册中心的工作。在本例中，将使用组播的方式来完成服务的注册和发现。&lt;/p>
&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">interface&lt;/span> &lt;span style="color:#268bd2">GreetingsService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>说明&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>定义了一个简单的服务契约 &lt;em>GreetingsService&lt;/em>，其中只有一个方法 &lt;em>sayHi&lt;/em> 可供调用，入参是 &lt;em>String&lt;/em> 类型，返回值也是 &lt;em>String&lt;/em> 类型。&lt;/li>
&lt;/ol>
&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">GreetingsServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> GreetingsService &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &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">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hi, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>说明&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>服务提供者需要实现服务契约 &lt;em>GreetingsService&lt;/em> 接口。&lt;/li>
&lt;li>该实现简单的返回一个欢迎信息，如果入参是 &lt;em>dubbo&lt;/em>，则返回 &lt;em>hi, dubbo&lt;/em>。&lt;/li>
&lt;/ol>
&lt;h3 id="实现-dubbo-服务提供方">实现 Dubbo 服务提供方&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">Application&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>GreetingsService&lt;span style="color:#719e07">&amp;gt;&lt;/span> service &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;first-dubbo-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>setRegistry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;multicast://224.5.6.7:1234&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>GreetingsService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> GreetingsServiceImpl&lt;span style="color:#719e07">());&lt;/span> &lt;span style="color:#586e75">// #5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> service&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #6
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #7
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>说明&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>创建一个 &lt;em>ServiceConfig&lt;/em> 的实例，泛型参数信息是服务接口类型，即 &lt;em>GreetingsService&lt;/em>。&lt;/li>
&lt;li>生成一个 &lt;em>ApplicationConfig&lt;/em> 的实例，并将其装配进 &lt;em>ServiceConfig&lt;/em>。&lt;/li>
&lt;li>生成一个 &lt;em>RegistryConfig&lt;/em> 实例，并将其装配进 &lt;em>ServiceConfig&lt;/em>，这里使用的是组播方式，参数是 &lt;code>multicast://224.5.6.7:1234&lt;/code>。合法的组播地址范围为：&lt;em>224.0.0.0 - 239.255.255.255&lt;/em>&lt;/li>
&lt;li>将服务契约 &lt;em>GreetingsService&lt;/em> 装配进 &lt;em>ServiceConfig&lt;/em>。&lt;/li>
&lt;li>将服务提供者提供的实现 &lt;em>GreetingsServiceImpl&lt;/em> 的实例装配进 &lt;em>ServiceConfig&lt;/em>。&lt;/li>
&lt;li>&lt;em>ServiceConfig&lt;/em> 已经具备足够的信息，开始对外暴露服务，默认监听端口是 &lt;em>20880&lt;/em>。&lt;/li>
&lt;li>为了防止服务端退出，按任意键或者 &lt;em>ctrl-c&lt;/em> 退出。&lt;/li>
&lt;/ol>
&lt;h3 id="实现-dubbo-服务调用方">实现 Dubbo 服务调用方&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">Application&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>GreetingsService&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 style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> reference&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;first-dubbo-client&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> reference&lt;span style="color:#719e07">.&lt;/span>setRegistry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;multicast://224.5.6.7:1234&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> reference&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>GreetingsService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GreetingsService greetingsService &lt;span style="color:#719e07">=&lt;/span> reference&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String message &lt;span style="color:#719e07">=&lt;/span> greetingsService&lt;span style="color:#719e07">.&lt;/span>sayHi&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #6
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>message&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #7
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>说明&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>创建一个 &lt;em>ReferenceConfig&lt;/em> 的实例，同样，泛型参数信息是服务接口类型，即 &lt;em>GreetingService&lt;/em>。&lt;/li>
&lt;li>生成一个 &lt;em>ApplicationConfig&lt;/em> 的实例，并将其装配进 &lt;em>ReferenceConfig&lt;/em>。&lt;/li>
&lt;li>生成一个 &lt;em>RegistryConfig&lt;/em> 实例，并将其装配进 &lt;em>ReferenceConfig&lt;/em>，注意这里的组播地址信息需要与服务提供方的相同。&lt;/li>
&lt;li>将服务契约 &lt;em>GreetingsService&lt;/em> 装配进 &lt;em>ReferenceConfig&lt;/em>。&lt;/li>
&lt;li>从 &lt;em>ReferenceConfig&lt;/em> 中获取到 &lt;em>GreetingService&lt;/em> 的代理。&lt;/li>
&lt;li>通过 &lt;em>GreetingService&lt;/em> 的代理发起远程调用，传入的参数为 &lt;em>dubbo&lt;/em>。&lt;/li>
&lt;li>打印返回结果 &lt;em>hi, dubbo&lt;/em>。&lt;/li>
&lt;/ol>
&lt;h3 id="运行">运行&lt;/h3>
&lt;p>完整的示例在 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-api">https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-api&lt;/a> 上提供。在完整的示例中，由于配置了 &lt;em>exec-maven-plugin&lt;/em>，可以很方便的在命令行下通过 maven 的方式执行。当然，您也可以在 IDE 里直接执行，但是需要注意的是，由于使用了组播的方式来发现服务，运行时需要指定 &lt;em>-Djava.net.preferIPv4Stack=true&lt;/em>。&lt;/p>
&lt;h4 id="构建示例">构建示例&lt;/h4>
&lt;p>通过以下的命令来同步示例代码并完成构建：&lt;/p>
&lt;ol>
&lt;li>同步代码：git clone &lt;a href="https://github.com/apache/dubbo-samples.git">https://github.com/apache/dubbo-samples.git&lt;/a>&lt;/li>
&lt;li>构建：mvn clean package&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ git clone https://github.com/apache/dubbo-samples.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">cd&lt;/span> dubbo-samples/java/dubbo-samples-api/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ mvn clean package
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO&lt;span style="color:#719e07">]&lt;/span> Scanning &lt;span style="color:#719e07">for&lt;/span> projects...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> ------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> Building dubbo-samples-api 1.0-SNAPSHOT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> ------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> --- maven-clean-plugin:2.5:clean &lt;span style="color:#719e07">(&lt;/span>default-clean&lt;span style="color:#719e07">)&lt;/span> @ dubbo-samples-api ---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> ------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> BUILD SUCCESS
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> ------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> Total time: 2.182 s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> Finished at: 2018-05-28T14:56:08+08:00
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> Final Memory: 20M/353M
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> ------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当看到 &lt;em>BUILD SUCCESS&lt;/em> 的时候表明构建完成，下面就可以开始进入运行阶段了。&lt;/p>
&lt;h4 id="运行服务端">运行服务端&lt;/h4>
&lt;p>通过运行以下的 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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ mvn -Djava.net.preferIPv4Stack&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#b58900">true&lt;/span> -Dexec.mainClass&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.samples.server.Application exec:java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> Scanning &lt;span style="color:#719e07">for&lt;/span> projects...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> ------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> Building dubbo-samples-api 1.0-SNAPSHOT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> ------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> --- exec-maven-plugin:1.6.0:java &lt;span style="color:#719e07">(&lt;/span>default-cli&lt;span style="color:#719e07">)&lt;/span> @ dubbo-samples-api ---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>log4j:WARN No appenders could be found &lt;span style="color:#719e07">for&lt;/span> logger &lt;span style="color:#719e07">(&lt;/span>com.alibaba.dubbo.common.logger.LoggerFactory&lt;span style="color:#719e07">)&lt;/span>.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>log4j:WARN Please initialize the log4j system properly.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig &lt;span style="color:#719e07">for&lt;/span> more info.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>first-dubbo-provider is running.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当 &lt;em>first-dubbo-provider is running.&lt;/em> 出现时，代表服务提供者已经启动就绪，等待客户端的调用。&lt;/p>
&lt;h4 id="运行客户端">运行客户端&lt;/h4>
&lt;p>通过运行以下的 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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ mvn -Djava.net.preferIPv4Stack&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#b58900">true&lt;/span> -Dexec.mainClass&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.samples.client.Application exec:java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> Scanning &lt;span style="color:#719e07">for&lt;/span> projects...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> ------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> Building dubbo-samples-api 1.0-SNAPSHOT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> ------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>INFO&lt;span style="color:#719e07">]&lt;/span> --- exec-maven-plugin:1.6.0:java &lt;span style="color:#719e07">(&lt;/span>default-cli&lt;span style="color:#719e07">)&lt;/span> @ dubbo-samples-api ---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>log4j:WARN No appenders could be found &lt;span style="color:#719e07">for&lt;/span> logger &lt;span style="color:#719e07">(&lt;/span>com.alibaba.dubbo.common.logger.LoggerFactory&lt;span style="color:#719e07">)&lt;/span>.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>log4j:WARN Please initialize the log4j system properly.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig &lt;span style="color:#719e07">for&lt;/span> more info.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>hi, dubbo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到， &lt;em>hi, dubbo&lt;/em> 是从服务提供者返回的执行结果。&lt;/p>
&lt;h2 id="快速生成-dubbo-应用">快速生成 Dubbo 应用&lt;/h2>
&lt;p>Dubbo 还提供了一个公共服务快速搭建基于 Spring Boot 的 Dubbo 应用。访问 &lt;a href="http://start.dubbo.io">http://start.dubbo.io&lt;/a> 并按照下图所示来生成示例工程：&lt;/p>
&lt;p>&lt;img src="https://cn.dubbo.apache.org/imgs/blog/dubbo-initializr.png" alt="dubbo initializr">&lt;/p>
&lt;p>&lt;strong>说明&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>在 &lt;em>Group&lt;/em> 中提供 maven groupId，默认值是 &lt;em>com.example&lt;/em>。&lt;/li>
&lt;li>在 &lt;em>Artifact&lt;/em> 中提供 maven artifactId，默认值是 &lt;em>demo&lt;/em>。&lt;/li>
&lt;li>在 &lt;em>DubboServiceName&lt;/em> 中提供服务名，默认值是 &lt;em>com.example.HelloService&lt;/em>。&lt;/li>
&lt;li>在 &lt;em>DubboServiceVersion&lt;/em> 中提供服务的版本，默认值是 &lt;em>1.0.0&lt;/em>。&lt;/li>
&lt;li>在 &lt;em>Client/Server&lt;/em> 中选取本次构建的工程是服务提供者 (Server) 还是服务消费者 (Client)，默认值是 &lt;em>server&lt;/em>。&lt;/li>
&lt;li>使用 &lt;em>embeddedZookeeper&lt;/em> 作为服务注册发现，默认为勾选。&lt;/li>
&lt;li>是否激活 qos 端口，默认为不勾选，如果勾选可以通过 &lt;em>22222&lt;/em> 端口访问。&lt;/li>
&lt;li>点击 &lt;em>Generate Project&lt;/em> 即可下载生成好的工程。&lt;/li>
&lt;/ol>
&lt;p>在本例中展示的是服务提供者，同样的，通过在生成界面选取 &lt;em>client&lt;/em> 来生成对应的服务消费者。&lt;/p>
&lt;h3 id="运行-1">运行&lt;/h3>
&lt;p>用 IDE 打开生成好的工程，可以发现应用是一个典型的 Spring Boot 应用。程序的入口如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> EmbeddedZooKeeper&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">2181&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">).&lt;/span>start&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> SpringApplication&lt;span style="color:#719e07">.&lt;/span>run&lt;span style="color:#719e07">(&lt;/span>DemoApplication&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> args&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>说明&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>在 &lt;em>2181&lt;/em> 端口上启动嵌入式 &lt;em>ZooKeeper&lt;/em>。&lt;/li>
&lt;li>启动 &lt;em>Spring Boot&lt;/em> 上下文。&lt;/li>
&lt;/ol>
&lt;p>可以直接在 IDE 中运行，输出结果如下：&lt;/p>
&lt;div class="highlight">&lt;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>2018-05-28 16:59:38.072 INFO &lt;span style="color:#2aa198">59943&lt;/span> --- &lt;span style="color:#719e07">[&lt;/span> main&lt;span style="color:#719e07">]&lt;/span> a.b.d.c.e.WelcomeLogoApplicationListener :
&lt;/span>&lt;/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>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> :: Dubbo Spring Boot &lt;span style="color:#719e07">(&lt;/span>v0.1.0&lt;span style="color:#719e07">)&lt;/span> : https://github.com/dubbo/dubbo-spring-boot-project
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> :: Dubbo &lt;span style="color:#719e07">(&lt;/span>v2.0.1&lt;span style="color:#719e07">)&lt;/span> : https://github.com/alibaba/dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> :: Google group : http://groups.google.com/group/dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2018-05-28 16:59:38.079 INFO &lt;span style="color:#2aa198">59943&lt;/span> --- &lt;span style="color:#719e07">[&lt;/span> main&lt;span style="color:#719e07">]&lt;/span> e.OverrideDubboConfigApplicationListener : Dubbo Config was overridden by externalized configuration &lt;span style="color:#719e07">{&lt;/span>dubbo.application.name&lt;span style="color:#719e07">=&lt;/span>dubbo-demo-server, dubbo.application.qosAcceptForeignIp&lt;span style="color:#719e07">=&lt;/span>false, dubbo.application.qosEnable&lt;span style="color:#719e07">=&lt;/span>true, dubbo.application.qosPort&lt;span style="color:#719e07">=&lt;/span>22222, dubbo.registry.address&lt;span style="color:#719e07">=&lt;/span>zookeeper://localhost:2181?client&lt;span style="color:#719e07">=&lt;/span>curator, dubbo.registry.id&lt;span style="color:#719e07">=&lt;/span>my-registry, dubbo.scan.basePackages&lt;span style="color:#719e07">=&lt;/span>com.example&lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#586e75">#1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2018-05-28 16:59:39.624 INFO &lt;span style="color:#2aa198">59943&lt;/span> --- &lt;span style="color:#719e07">[&lt;/span> main&lt;span style="color:#719e07">]&lt;/span> com.example.demo.DemoApplication : Started DemoApplication in 1.746 seconds &lt;span style="color:#719e07">(&lt;/span>JVM running &lt;span style="color:#719e07">for&lt;/span> 2.963&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>说明&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>输出中打印的以 &lt;em>dubbo.&lt;/em> 开头的配置信息，定义在 &lt;em>main/resources/application.properties&lt;/em> 中。&lt;/li>
&lt;/ol>
&lt;h3 id="通过-telnet-管理服务">通过 Telnet 管理服务&lt;/h3>
&lt;p>生成工程的时候如果选择了激活 &lt;em>qos&lt;/em> 的话，就可以通过 &lt;em>telnet&lt;/em> 或者 &lt;em>nc&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ telnet localhost &lt;span style="color:#2aa198">22222&lt;/span>
&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 &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;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;span style="display:flex;">&lt;span>dubbo&amp;gt;ls
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>As Provider side:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| Provider Service Name |PUB|
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|com.example.HelloService:1.0.0| Y |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+------------------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>As Consumer side:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+---------------------+---+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|Consumer Service Name|NUM|
&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>qos&lt;/em> 支持以下几个命令，更详细的信息请查阅官方文档&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>：&lt;/p>
&lt;ul>
&lt;li>&lt;em>ls&lt;/em>：列出消费者、提供者信息&lt;/li>
&lt;li>&lt;em>online&lt;/em>：上线服务&lt;/li>
&lt;li>&lt;em>offline&lt;/em>：下线服务&lt;/li>
&lt;li>&lt;em>help&lt;/em>：联机帮助&lt;/li>
&lt;/ul>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>在本文中，从 RMI 开始，介绍了 Java 领域分布式调用的基本概念，也就是基于接口编程、通过代理将远程调用伪装成本地、通过注册中心完成服务的注册和发现。&lt;/p>
&lt;p>然后为了简单起见，使用简单的组播注册方式和直接面向 Dubbo API 编程的方式介绍了如何开发一个 Dubbo 的完整应用。深入的了解 &lt;em>ServiceConfig&lt;/em> 和 &lt;em>ReferenceConfig&lt;/em> 的用法，对于进一步的使用 Spring XML 配置、乃至 Spring Boot 的编程方式有这很大的帮助。&lt;/p>
&lt;p>最后，简单的介绍了如何通过 Dubbo 团队提供的公共服务 start.dubbo.io 快速搭建基于 Spring Boot 的 Dubbo 应用，并通过 &lt;em>qos&lt;/em> 来做 Dubbo 服务的简单运维。&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://docs.oracle.com/javase/6/docs/technotes/guides/rmi/hello/hello-world.html">Getting Started Using JavaTM RMI&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a href="https://cn.dubbo.apache.org/zh/docsv2.7/user/examples/explicit-target/">直连提供者&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>&lt;a href="https://cn.dubbo.apache.org/zh/docsv2.7/user/references/registry/multicast/">Multicast 注册中心&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>&lt;a href="https://cn.dubbo.apache.org/zh/docsv2.7/user/references/qos/">在线运维命令&lt;/a>&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Blog: 使用jdk17编译运行dubbo 2.7.14项目</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;div class="highlight">&lt;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 -jar dubbo-demo-annotation-provider/target/dubbo-demo-annotation-provider-2.7.14.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到类似的报错:&lt;br>
&lt;code>Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not &amp;quot;opens java.lang&amp;quot; to unnamed module @8807e25&lt;/code>&lt;br>
关键词 &lt;code>module &lt;/code>&lt;strong>java.base&lt;/strong>&lt;code>does not &amp;quot;opens&lt;/code>&lt;strong>java.lang&lt;/strong>&lt;code>&amp;quot; to unnamed module @8807e25&lt;/code>，根据&lt;a href="https://openjdk.java.net/jeps/403">openjdk的说明&lt;/a>，我们只需要添加&lt;code>--add-opens &lt;/code>&lt;strong>java.base&lt;/strong>&lt;code>/&lt;/code>&lt;strong>java.lang&lt;/strong>&lt;code>=ALL-UNNAMED&lt;/code>参数即可解决问题&lt;br>
对应的报错应该都可以用类似得方法去解决，经过测试，demo中的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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>--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;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如上参数即可运行&lt;br>
在两个终端中分别运行provider和consumer:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>java --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 -jar dubbo-demo-annotation-provider/target/dubbo-demo-annotation-provider-2.7.14.jar
&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>java --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 -jar dubbo-demo-annotation-consumer/target/dubbo-demo-annotation-consumer-2.7.14.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以发现报出了zookeeper的错误，不难想到，升级依赖的zookeeper的版本大概率可以解决问题&amp;hellip;&lt;br>
在provider和consumer的 &lt;code>pom.xml&lt;/code> 的依赖第一项添加最新版本的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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span> &amp;lt;dependency&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;groupId&amp;gt;org.apache.zookeeper&amp;lt;/groupId&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;artifactId&amp;gt;zookeeper&amp;lt;/artifactId&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;version&amp;gt;3.7.0&amp;lt;/version&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;exclusions&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;exclusion&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;groupId&amp;gt;io.netty&amp;lt;/groupId&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;artifactId&amp;gt;netty&amp;lt;/artifactId&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/exclusion&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/exclusions&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/dependency&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>再次运行provider和consumer&lt;br>
可以看到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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>result :Hello world, response from provider: *.*.*.*/&amp;lt;unresolved&amp;gt;:20880
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>provider端也有对应的日志&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>Hello world, request from consumer: /*.*.*.*:43346
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: 在 Dubbo 中使用注解</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">;&lt;/span>
&lt;/span>&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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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>&lt;span style="color:#719e07">(&lt;/span>annotation &lt;span style="color:#719e07">=&lt;/span> DubboComponentScan&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> attribute &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;basePackages&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> &lt;span style="color:#268bd2">scanBasePackages&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#719e07">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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>&lt;span style="color:#719e07">(&lt;/span>annotation &lt;span style="color:#719e07">=&lt;/span> DubboComponentScan&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> attribute &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;basePackageClasses&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]&lt;/span> scanBasePackageClasses&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#719e07">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&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>
&lt;p>扫描到 Dubbo 的服务提供方和消费者之后，对其做相应的组装并初始化，并最终完成服务暴露或者引用的工作。&lt;/p>
&lt;p>当然，如果不使用外部化配置（External Configuration）的话，也可以直接使用 &lt;code>@DubboComponentScan&lt;/code>。&lt;/p>
&lt;h3 id="service">@Service&lt;/h3>
&lt;p>&lt;code>@Service&lt;/code> 用来配置 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">@Service&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AnnotatedGreetingService&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> GreetingService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过 &lt;code>@Service&lt;/code> 上提供的属性，可以进一步的定制化 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:#719e07">package&lt;/span> org.apache.dubbo.config.annotation&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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">@Documented&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Retention&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RetentionPolicy&lt;span style="color:#719e07">.&lt;/span>RUNTIME&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Target&lt;/span>&lt;span style="color:#719e07">({&lt;/span>ElementType&lt;span style="color:#719e07">.&lt;/span>TYPE&lt;span style="color:#719e07">})&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@Inherited&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> Service &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> interfaceClass&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#dc322f">void&lt;/span>&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">interfaceName&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">version&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">group&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">export&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #6
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">register&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #7
&lt;/span>&lt;/span>&lt;/span>&lt;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">application&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #8
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">module&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #9
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">provider&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #10
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> &lt;span style="color:#268bd2">protocol&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#719e07">{};&lt;/span> &lt;span style="color:#586e75">// #11
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">monitor&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #12
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> &lt;span style="color:#268bd2">registry&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#719e07">{};&lt;/span> &lt;span style="color:#586e75">// #13
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中比较重要的有：&lt;/p>
&lt;ol>
&lt;li>@Service 只能定义在一个类上，表示一个服务的具体实现&lt;/li>
&lt;li>interfaceClass：指定服务提供方实现的 interface 的类&lt;/li>
&lt;li>interfaceName：指定服务提供方实现的 interface 的类名&lt;/li>
&lt;li>version：指定服务的版本号&lt;/li>
&lt;li>group：指定服务的分组&lt;/li>
&lt;li>export：是否暴露服务&lt;/li>
&lt;li>registry：是否向注册中心注册服务&lt;/li>
&lt;li>application：应用配置&lt;/li>
&lt;li>module：模块配置&lt;/li>
&lt;li>provider：服务提供方配置&lt;/li>
&lt;li>protocol：协议配置&lt;/li>
&lt;li>monitor：监控中心配置&lt;/li>
&lt;li>registry：注册中心配置&lt;/li>
&lt;/ol>
&lt;p>另外，需要注意的是，application、module、provider、protocol、monitor、registry（从 8 到 13）需要提供的是对应的 spring bean 的名字，而这些 bean 的组装要么通过传统的 XML 配置方式完成，要么通过现代的 Java Config 来完成。在本文中，将会展示 Java Config 的使用方式。&lt;/p>
&lt;h3 id="reference">@Reference&lt;/h3>
&lt;p>&lt;code>@Reference&lt;/code> 用来配置 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">@Component&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">GreetingServiceConsumer&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@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 style="color:#719e07">;&lt;/span>
&lt;/span>&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> String &lt;span style="color:#268bd2">doSayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> greetingService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过 &lt;code>@Reference&lt;/code> 上提供的属性，可以进一步的定制化 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:#719e07">package&lt;/span> org.apache.dubbo.config.annotation&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&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">@Documented&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Retention&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RetentionPolicy&lt;span style="color:#719e07">.&lt;/span>RUNTIME&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Target&lt;/span>&lt;span style="color:#719e07">({&lt;/span>ElementType&lt;span style="color:#719e07">.&lt;/span>FIELD&lt;span style="color:#719e07">,&lt;/span> ElementType&lt;span style="color:#719e07">.&lt;/span>METHOD&lt;span style="color:#719e07">,&lt;/span> ElementType&lt;span style="color:#719e07">.&lt;/span>ANNOTATION_TYPE&lt;span style="color:#719e07">})&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">@interface&lt;/span> Reference &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> interfaceClass&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#dc322f">void&lt;/span>&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">interfaceName&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">version&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">group&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">url&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #6
&lt;/span>&lt;/span>&lt;/span>&lt;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">application&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #7
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">module&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #8
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">consumer&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #9
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">protocol&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #10
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">monitor&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#586e75">// #11
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> &lt;span style="color:#268bd2">registry&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#719e07">{};&lt;/span> &lt;span style="color:#586e75">// #12
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中比较重要的有：&lt;/p>
&lt;ol>
&lt;li>@Reference 可以定义在类中的一个字段上，也可以定义在一个方法上，甚至可以用来修饰另一个 annotation，表示一个服务的引用。通常 @Reference 定义在一个字段上&lt;/li>
&lt;li>interfaceClass：指定服务的 interface 的类&lt;/li>
&lt;li>interfaceName：指定服务的 interface 的类名&lt;/li>
&lt;li>version：指定服务的版本号&lt;/li>
&lt;li>group：指定服务的分组&lt;/li>
&lt;li>url：通过指定服务提供方的 URL 地址直接绕过注册中心发起调用&lt;/li>
&lt;li>application：应用配置&lt;/li>
&lt;li>module：模块配置&lt;/li>
&lt;li>consumer：服务消费方配置&lt;/li>
&lt;li>protocol：协议配置&lt;/li>
&lt;li>monitor：监控中心配置&lt;/li>
&lt;li>registry：注册中心配置&lt;/li>
&lt;/ol>
&lt;p>另外，需要注意的是，application、module、consumer、protocol、monitor、registry（从 7 到 12）需要提供的是对应的 spring bean 的名字，而这些 bean 的组装要么通过传统的 XML 配置方式完成，要么通过现代的 Java Config 来完成。在本文中，将会展示 Java Config 的使用方式。&lt;/p>
&lt;h2 id="示例实战">示例实战&lt;/h2>
&lt;p>了解了 &lt;code>@EnableDubbo&lt;/code>， &lt;code>@Service&lt;/code>，&lt;code>@Reference&lt;/code> 的作用，下面以一个实际的例子来展示如何使用 annotation 来开发 Dubbo 应用。以下的代码可以在 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-annotation">https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-annotation&lt;/a> 中找到。&lt;/p>
&lt;h3 id="1-接口定义">1. 接口定义&lt;/h3>
&lt;p>定义一个简单的 &lt;code>GreetingService&lt;/code> 接口，里面只有一个简单的方法 &lt;code>sayHello&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">GreetingService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-服务端服务实现">2. 服务端：服务实现&lt;/h3>
&lt;p>实现 &lt;code>GreetingService&lt;/code> 接口，并通过 &lt;code>@Service&lt;/code> 来标注其为 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">@Service&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AnnotatedGreetingService&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> GreetingService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3-服务端组装服务提供方">3. 服务端：组装服务提供方&lt;/h3>
&lt;p>通过 Spring 中 Java Config 的技术（&lt;code>@Configuration&lt;/code>）和 annotation 扫描（&lt;code>@EnableDubbo&lt;/code>）来发现、组装、并向外提供 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">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>scanBasePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.impl&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> ProviderConfig &lt;span style="color:#268bd2">providerConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ProviderConfig providerConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ProviderConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> providerConfig&lt;span style="color:#719e07">.&lt;/span>setTimeout&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">1000&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> providerConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> ApplicationConfig &lt;span style="color:#268bd2">applicationConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-annotation-provider&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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">@Bean&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> RegistryConfig &lt;span style="color:#268bd2">registryConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RegistryConfig registryConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;localhost&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">2181&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> registryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> ProtocolConfig &lt;span style="color:#268bd2">protocolConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ProtocolConfig protocolConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">20880&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocolConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说明：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>通过 &lt;code>@EnableDubbo&lt;/code> 指定在 &lt;code>com.alibaba.dubbo.samples.impl&lt;/code> 下扫描所有标注有 &lt;code>@Service&lt;/code> 的类&lt;/p>
&lt;/li>
&lt;li>
&lt;p>通过 &lt;code>@Configuration&lt;/code> 将 ProviderConfiguration 中所有的 &lt;code>@Bean&lt;/code> 通过 Java Config 的方式组装出来并注入给 Dubbo 服务，也就是标注有 &lt;code>@Service&lt;/code> 的类。这其中就包括了：&lt;/p>
&lt;ol>
&lt;li>ProviderConfig：服务提供方配置&lt;/li>
&lt;li>ApplicationConfig：应用配置&lt;/li>
&lt;li>RegistryConfig：注册中心配置&lt;/li>
&lt;li>ProtocolConfig：协议配置&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;h3 id="4-服务端启动服务">4. 服务端：启动服务&lt;/h3>
&lt;p>在 &lt;code>main&lt;/code> 方法中通过启动一个 Spring Context 来对外提供 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">class&lt;/span> &lt;span style="color:#268bd2">ProviderBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> EmbeddedZooKeeper&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">2181&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">).&lt;/span>start&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">(&lt;/span>ProviderConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>in&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说明：&lt;/p>
&lt;ol>
&lt;li>启动一个嵌入式的 zookeeper 在 2181 端口上提供注册中心的服务&lt;/li>
&lt;li>初始化一个 &lt;code>AnnotationConfigApplicationContext&lt;/code> 的示例，并将 &lt;code>ProviderConfiguration&lt;/code> 传入以完成 Dubbo 服务的自动发现和装配&lt;/li>
&lt;li>启动 Spring Context，开始提供对外的 Dubbo 服务&lt;/li>
&lt;li>因为是服务端，需要通过阻塞主线程来防止进程退出&lt;/li>
&lt;/ol>
&lt;p>启动服务端的 &lt;code>main&lt;/code> 方法，将会看到下面的输出，代表服务端启动成功，并在注册中心（ZookeeperRegistry）上注册了 &lt;code>GreetingService&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-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>01/08/18 02:12:51:051 CST&lt;span style="color:#719e07">]&lt;/span> main INFO transport.AbstractServer: &lt;span style="color:#719e07">[&lt;/span>DUBBO&lt;span style="color:#719e07">]&lt;/span> Start NettyServer &lt;span style="color:#b58900">bind&lt;/span> /0.0.0.0:20880, &lt;span style="color:#b58900">export&lt;/span> /192.168.99.1:20880, dubbo version: 2.6.2, current host: 192.168.99.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>01/08/18 02:12:51:051 CST&lt;span style="color:#719e07">]&lt;/span> main INFO zookeeper.ZookeeperRegistry: &lt;span style="color:#719e07">[&lt;/span>DUBBO&lt;span style="color:#719e07">]&lt;/span> Register: dubbo://192.168.99.1:20880/com.alibaba.dubbo.samples.api.GreetingService?anyhost&lt;span style="color:#719e07">=&lt;/span>true&amp;amp;&lt;span style="color:#268bd2">application&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbo-annotation-provider&amp;amp;default.timeout&lt;span style="color:#719e07">=&lt;/span>1000&amp;amp;&lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.6.2&amp;amp;&lt;span style="color:#268bd2">generic&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;&lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.samples.api
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="5-客户端引用服务">5. 客户端：引用服务&lt;/h3>
&lt;p>通过 &lt;code>@Reference&lt;/code> 来标记 &lt;code>GreetingService&lt;/code> 接口的成员变量 greetingService 是一个 Dubbo 服务的引用，也就是说，可以简单的通过该接口向远端的服务提供方发起调用，而客户端并没有实现 &lt;code>GreetingService&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">@Component&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;annotatedConsumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">GreetingServiceConsumer&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@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 style="color:#719e07">;&lt;/span>
&lt;/span>&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> String &lt;span style="color:#268bd2">doSayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> greetingService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="6-客户端组装服务消费者">6. 客户端：组装服务消费者&lt;/h3>
&lt;p>与 &lt;strong>3. 服务端：组装服务提供方&lt;/strong> 类似，通过 Spring 中 Java Config 的技术（&lt;code>@Configuration&lt;/code>）和 annotation 扫描（&lt;code>@EnableDubbo&lt;/code>）来发现、组装 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">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>scanBasePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.action&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@ComponentScan&lt;/span>&lt;span style="color:#719e07">(&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.action&amp;#34;&lt;/span>&lt;span style="color:#719e07">})&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ConsumerConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> ApplicationConfig &lt;span style="color:#268bd2">applicationConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-annotation-consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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">@Bean&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> ConsumerConfig &lt;span style="color:#268bd2">consumerConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConsumerConfig consumerConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConsumerConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerConfig&lt;span style="color:#719e07">.&lt;/span>setTimeout&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">3000&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> consumerConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Bean&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> RegistryConfig &lt;span style="color:#268bd2">registryConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RegistryConfig registryConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;localhost&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setPort&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">2181&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> registryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说明：&lt;/p>
&lt;ul>
&lt;li>通过 &lt;code>@EnableDubbo&lt;/code> 指定在 &lt;code>com.alibaba.dubbo.samples.impl&lt;/code> 下扫描所有标注有 `@Reference 的类&lt;/li>
&lt;li>通过 &lt;code>@Configuration&lt;/code> 将 ConsumerConfiguration 中所有的 &lt;code>@Bean&lt;/code> 通过 Java Config 的方式组装出来并注入给 Dubbo 服务消费者，也就是标注有 `@Reference 的类。这其中就包括了：
&lt;ol>
&lt;li>ApplicationConfig：应用配置&lt;/li>
&lt;li>ConsumerConfig：服务消费者配置&lt;/li>
&lt;li>RegistryConfig：注册中心配置，注意：这里的配置需要与服务提供方启动的 EmbeddedZooKeeper 的配置信息保持一致&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;h3 id="7-客户端发起远程调用">7. 客户端：发起远程调用&lt;/h3>
&lt;p>在 &lt;code>main&lt;/code> 方法中通过启动一个 Spring Context，从其中查找到组装好的 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">class&lt;/span> &lt;span style="color:#268bd2">ConsumerBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">(&lt;/span>ConsumerConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span> &lt;span style="color:#586e75">// #2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GreetingServiceConsumer greetingServiceConsumer &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>GreetingServiceConsumer&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #3
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String hello &lt;span style="color:#719e07">=&lt;/span> greetingServiceConsumer&lt;span style="color:#719e07">.&lt;/span>doSayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;annotation&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;result: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> hello&lt;span style="color:#719e07">);&lt;/span> &lt;span style="color:#586e75">// #5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说明：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>初始化一个 &lt;code>AnnotationConfigApplicationContext&lt;/code> 的示例，并将 &lt;code>ConsumerConfiguration&lt;/code> 传入以完成 Dubbo 服务消费者的自动发现和装配&lt;/p>
&lt;/li>
&lt;li>
&lt;p>启动 Spring Context&lt;/p>
&lt;/li>
&lt;li>
&lt;p>从 Context 中查找出类型为 &lt;code>GreetingServiceConsumer&lt;/code> 的 Bean&lt;/p>
&lt;/li>
&lt;li>
&lt;p>调用 &lt;code>doSayHello&lt;/code> 方法，最终通过 Dubbo 的服务引用（由 &lt;code>@Reference&lt;/code> 标注）发起一次远程调用&lt;/p>
&lt;/li>
&lt;li>
&lt;p>打印调用结果&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>启动客户端的 &lt;code>main&lt;/code> 方法，将会看到下面的输出，其中返回结果为 result: hello, annotation：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>01/08/18 02:38:40:040 CST&lt;span style="color:#719e07">]&lt;/span> main INFO config.AbstractConfig: &lt;span style="color:#719e07">[&lt;/span>DUBBO&lt;span style="color:#719e07">]&lt;/span> Refer dubbo service com.alibaba.dubbo.samples.api.GreetingService from url zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?anyhost&lt;span style="color:#719e07">=&lt;/span>true&amp;amp;&lt;span style="color:#268bd2">application&lt;/span>&lt;span style="color:#719e07">=&lt;/span>dubbo-annotation-consumer&amp;amp;&lt;span style="color:#268bd2">check&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;default.timeout&lt;span style="color:#719e07">=&lt;/span>3000&amp;amp;&lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">=&lt;/span>2.6.2&amp;amp;&lt;span style="color:#268bd2">generic&lt;/span>&lt;span style="color:#719e07">=&lt;/span>false&amp;amp;&lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>com.alibaba.dubbo.samples.api.GreetingService&amp;amp;&lt;span style="color:#268bd2">methods&lt;/span>&lt;span style="color:#719e07">=&lt;/span>sayHello&amp;amp;&lt;span style="color:#268bd2">pid&lt;/span>&lt;span style="color:#719e07">=&lt;/span>33001&amp;amp;register.ip&lt;span style="color:#719e07">=&lt;/span>192.168.99.1&amp;amp;remote.timestamp&lt;span style="color:#719e07">=&lt;/span>1533105502086&amp;amp;&lt;span style="color:#268bd2">side&lt;/span>&lt;span style="color:#719e07">=&lt;/span>consumer&amp;amp;&lt;span style="color:#268bd2">timestamp&lt;/span>&lt;span style="color:#719e07">=&lt;/span>1533105519216, dubbo version: 2.6.2, current host: 192.168.99.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">[&lt;/span>01/08/18 02:38:40:040 CST&lt;span style="color:#719e07">]&lt;/span> main INFO annotation.ReferenceBeanBuilder: &amp;lt;dubbo:reference &lt;span style="color:#268bd2">object&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.common.bytecode.proxy0@673be18f&amp;#34;&lt;/span> &lt;span style="color:#268bd2">singleton&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.api.GreetingService&amp;#34;&lt;/span> &lt;span style="color:#268bd2">uniqueServiceName&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.api.GreetingService&amp;#34;&lt;/span> &lt;span style="color:#268bd2">generic&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span> &lt;span style="color:#268bd2">id&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.alibaba.dubbo.samples.api.GreetingService&amp;#34;&lt;/span> /&amp;gt; has been built.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, annotation
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="总结">总结&lt;/h2>
&lt;p>通过本文的学习，读者可以掌握 Dubbo 专属的 annotation &lt;code>@EnableDubbo&lt;/code>、&lt;code>@Service&lt;/code>、&lt;code>@Reference&lt;/code> 的基本概念，并通过一个简单 Dubbo 应用的实战开发掌握其基本的用法。&lt;/p>
&lt;p>Spring 除了传统的 XML 配置之外，还提供了注解驱动、外部化配置、以及自动装配等更现代的配置方式。本文专注在介绍通过注解方式来开发 Dubbo 应用，可以看到，与 XML 配置相比，注解方式编程更加简洁明快。在今后的博文中，会进一步的介绍在 Dubbo 中使用外部化配置、以及自动装配的方法。&lt;/p></description></item><item><title>Blog: Dubbo 2.7.x repackage 后的兼容实现方案</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">;&lt;/span>
&lt;/span>&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&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>ApplicationConfig &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ApplicationConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ApplicationConfig&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="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 style="color:#719e07">;&lt;/span>
&lt;/span>&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&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>ProtocolConfig &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ProtocolConfig&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ProtocolConfig&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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 style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> port&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">,&lt;/span> port&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到：&lt;/p>
&lt;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>
&lt;h2 id="spring注解">Spring注解&lt;/h2>
&lt;p>Spring注解诸如&lt;code>@EnableDubbo&lt;/code>、&lt;code>@Service&lt;/code>以及&lt;code>@Reference&lt;/code>，由于不能使用继承，故这些注解类是通过代码拷贝来实现的；用于处理这些注解的Spring BeanPostProcessor以及Parser等相关的类，也是通过拷贝来实现；&lt;/p>
&lt;p>这类兼容代码分别位于兼容包的以下几个package中：&lt;/p>
&lt;ul>
&lt;li>com.alibaba.dubbo.config.annotation&lt;/li>
&lt;li>com.alibaba.dubbo.config.spring.context.annotation&lt;/li>
&lt;li>org.apache.dubbo.config.spring&lt;/li>
&lt;/ul>
&lt;p>所以这里要特别强调的是，这类代码在2.7.x里存在2份，因此有修改的同时需要同步修改。&lt;/p>
&lt;h2 id="扩展spi">扩展SPI&lt;/h2>
&lt;p>Dubbo的SPI扩展机制，可以通过&lt;a href="https://cn.dubbo.apache.org/zh/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>这篇博客详细了解。&lt;/p>
&lt;p>以Filter扩展为例，简单来说就是：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>MyFilter需要实现Filter接口&lt;/p>
&lt;/li>
&lt;li>
&lt;p>在META-INF/dubbo下，增加META-INF/dubbo/com.alibaba.dubbo.rpc.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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>myFilter=com.test.MyFilter
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>看似简单的两点，对Dubbo框架来说，需要：&lt;/p>
&lt;ol>
&lt;li>正确加载配置文件META-INF/dubbo/com.alibaba.dubbo.rpc.Filter&lt;/li>
&lt;li>正确加载MyFilter类并执行invoke方法&lt;/li>
&lt;/ol>
&lt;p>下面分别介绍Dubbo框架怎么实现以上几点。&lt;/p>
&lt;h3 id="正确加载meta-infdubbocomalibabadubborpcfilter">正确加载META-INF/dubbo/com.alibaba.dubbo.rpc.Filter&lt;/h3>
&lt;p>Dubbo SPI机制在查找配置文件时，是根据扩展点的类名来查找的，以Filter为例，在包名变为org.apache.dubbo后，查询的目录变成：&lt;/p>
&lt;ul>
&lt;li>META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter&lt;/li>
&lt;li>META-INF/dubbo/org.apache.dubbo.rpc.Filter&lt;/li>
&lt;li>META-INF/services/org.apache.dubbo.rpc.Filter&lt;/li>
&lt;/ul>
&lt;p>但是用户之前按老的包实现的Filter，其配置是放在类似&lt;code>META-INF/dubbo/com.alibaba.dubbo.rpc.Filter&lt;/code>的，如果框架不做特殊处理，是不会加载老配置的。&lt;/p>
&lt;p>因此在&lt;code>ExtensionLoader&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">// synchronized in getExtensionClasses
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">private&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> loadExtensionClasses&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> SPI defaultAnnotation &lt;span style="color:#719e07">=&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getAnnotation&lt;span style="color:#719e07">(&lt;/span>SPI&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>defaultAnnotation &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String value &lt;span style="color:#719e07">=&lt;/span> defaultAnnotation&lt;span style="color:#719e07">.&lt;/span>value&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">((&lt;/span>value &lt;span style="color:#719e07">=&lt;/span> value&lt;span style="color:#719e07">.&lt;/span>trim&lt;span style="color:#719e07">()).&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> names &lt;span style="color:#719e07">=&lt;/span> NAME_SEPARATOR&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>names&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;more than 1 default extension name on extension &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> Arrays&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">(&lt;/span>names&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>names&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> cachedDefaultName &lt;span style="color:#719e07">=&lt;/span> names&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> extensionClasses &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadDirectory&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> DUBBO_INTERNAL_DIRECTORY&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadDirectory&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> DUBBO_INTERNAL_DIRECTORY&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">().&lt;/span>replace&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;org.apache&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.alibaba&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadDirectory&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> DUBBO_DIRECTORY&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadDirectory&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> DUBBO_DIRECTORY&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">().&lt;/span>replace&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;org.apache&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.alibaba&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadDirectory&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> SERVICES_DIRECTORY&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loadDirectory&lt;span style="color:#719e07">(&lt;/span>extensionClasses&lt;span style="color:#719e07">,&lt;/span> SERVICES_DIRECTORY&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">().&lt;/span>replace&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;org.apache&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.alibaba&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> extensionClasses&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到，除了加载新配置外，老配置文件也会进行扫描。&lt;/p>
&lt;h3 id="正确加载myfilter类">正确加载MyFilter类&lt;/h3>
&lt;p>&lt;code>com.alibaba.dubbo.rpc.Filter&lt;/code>接口除了要继承自&lt;code>org.apache.dubbo.rpc.Filter&lt;/code>以外，其唯一的方法invoke也需要做特殊处理。我们看看它的方法签名：&lt;/p>
&lt;p>&lt;code>Result invoke(Invoker&amp;lt;?&amp;gt; invoker, Invocation invocation) throws RpcException;&lt;/code>&lt;/p>
&lt;p>这里参数、返回值、异常都会被实现类&lt;code>MyFilter&lt;/code>用到，因此这些类也需要有兼容类；而参数、返回值不同，对于接口来说是不同的方法，因此：&lt;/p>
&lt;ul>
&lt;li>需要在com.alibaba.dubbo.rpc.Filter里，定义老的invoke方法，MyFilter会覆盖这个方法；&lt;/li>
&lt;li>org.apache.dubbo.rpc.Filter里的invoke方法，需要找一个地方来实现桥接，框架调用Filter链执行到新的invoke方法时，新的参数如何转换成老参数，老返回值如何转换成新的返回值；&lt;/li>
&lt;/ul>
&lt;p>这里就用到了JDK8的新特性：接口default方法。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&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">interface&lt;/span> &lt;span style="color:#268bd2">Filter&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Filter &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>Invocation invocation&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">throws&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Result&lt;span style="color:#719e07">.&lt;/span>CompatibleResult result &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Result&lt;span style="color:#719e07">.&lt;/span>CompatibleResult&lt;span style="color:#719e07">)&lt;/span> invoke&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> Invoker&lt;span style="color:#719e07">.&lt;/span>CompatibleInvoker&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>invoker&lt;span style="color:#719e07">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Invocation&lt;span style="color:#719e07">.&lt;/span>CompatibleInvocation&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">.&lt;/span>getDelegate&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到，default方法里，对参数进行了包装，然后调用老的invoke方法，并将返回值进行解包后返回给Dubbo框架。这里Result.CompatibleResult、Invocation.CompatibleInvocation以及Invoker.CompatibleInvoker都用到了代理模式。&lt;/p>
&lt;p>感兴趣的同学可以详细看一下以下几个类：&lt;/p>
&lt;ul>
&lt;li>com.alibaba.dubbo.rpc.Invocation&lt;/li>
&lt;li>com.alibaba.dubbo.rpc.Invoker&lt;/li>
&lt;li>com.alibaba.dubbo.rpc.Result&lt;/li>
&lt;/ul>
&lt;h2 id="后续todo-list">后续todo list&lt;/h2>
&lt;p>目前兼容包仅仅是对常见的API及SPI做了支持，列表如下：&lt;/p>
&lt;ul>
&lt;li>com.alibaba.dubbo.rpc.Filter / Invocation / Invoker / Result / RpcContext / RpcException&lt;/li>
&lt;li>com.alibaba.dubbo.config.*Config&lt;/li>
&lt;li>com.alibaba.dubbo.config.annotation.Reference / Service&lt;/li>
&lt;li>com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo&lt;/li>
&lt;li>com.alibaba.dubbo.common.Constants / URL&lt;/li>
&lt;li>com.alibaba.dubbo.common.extension.ExtensionFactory&lt;/li>
&lt;li>com.alibaba.dubbo.common.serialize.Serialization / ObjectInput / ObjectOutput&lt;/li>
&lt;li>com.alibaba.dubbo.cache.CacheFactory / Cache&lt;/li>
&lt;li>com.alibaba.dubbo.rpc.service.EchoService / GenericService&lt;/li>
&lt;/ul>
&lt;p>大家如果在试用的过程中发现有任何问题请及时提出；同时如果对其他扩展点有兼容需求，也请大家提出来，也非常欢迎大家自己解决并贡献出来。&lt;/p></description></item><item><title>Blog: Dubbo 上下文信息</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>用途&lt;/th>
&lt;th>作用范围&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">getRequest&lt;/td>
&lt;td>获取 RPC 请求对象&lt;/td>
&lt;td>Consumer&lt;/td>
&lt;td>获取底层 RPC 请求对象，例如 HttpServletRequest，其他情况为 null&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getResponse&lt;/td>
&lt;td>获取 RPC 请求响应&lt;/td>
&lt;td>Consumer&lt;/td>
&lt;td>获取底层 RPC 响应对象，例如HttpServletResponse，其他情况为 null&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">isProviderSide&lt;/td>
&lt;td>当前是否属于 Provider 上下文&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>服务被调用时为 true，调用其他服务时为false&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">isConsumerSide&lt;/td>
&lt;td>当前是否属于 Consumer 上下文&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>服务被调用时为 false，调用其他服务时为 true&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getUrls&lt;/td>
&lt;td>获取当前能调用的 Url 列表&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>Consumer 端会根据不同的 Failover 策略实时变化&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getRemotePort&lt;/td>
&lt;td>获取远端端口&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>Consumer 端为最后一次调用的 Provider 端口，Provider 为当前请求的 Consumer 端口&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getRemoteHost&lt;/td>
&lt;td>获取远端主机地址&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getRemoteHostName&lt;/td>
&lt;td>获取远端主机名&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getRemoteAddressString&lt;/td>
&lt;td>获取远端地址&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getRemoteAddress&lt;/td>
&lt;td>获取远端地址&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getLocalPort&lt;/td>
&lt;td>获取本端端口&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getLocalHost&lt;/td>
&lt;td>获取本端主机地址&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getLocalHostName&lt;/td>
&lt;td>获取本端主机名&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getLocalAddressString&lt;/td>
&lt;td>获取本端地址&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">getLocalAddress&lt;/td>
&lt;td>获取本端地址&lt;/td>
&lt;td>Both&lt;/td>
&lt;td>&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>
&lt;h4 id="对端传递">对端传递&lt;/h4>
&lt;p>调用&lt;code>setAttachment&lt;/code>和&lt;code>getAttachment&lt;/code>即可完成对端数据传递，这些数据会经过 RPC 传递到对端。例如 Consumer 向 Provider 传递&lt;code>span_id&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>Dubbo已经支持从 Provider 端向 Consumer 端传递参数，读写方式和 Consumer 端调用时的方式一样。&lt;/li>
&lt;/ul>
&lt;h3 id="异步调用">异步调用&lt;/h3>
&lt;p>在异步调用时，可通过&lt;code>getCompletableFuture&lt;/code>或&lt;code>getFuture&lt;/code>获取相关的 Future，异步调用相关文档请参阅：&lt;a href="https://cn.dubbo.apache.org/zh/docs/advanced/async-call/">异步调用&lt;/a>&lt;/p>
&lt;h2 id="注意事项">注意事项&lt;/h2>
&lt;p>Dubbo 内部使用 ThreadLocal 的方式存储每次调用的上下文信息，当接收到请求或发起请求时，当前线程会更新 RpcContext。例如，服务 A 调用服务 B，服务 B 调用服务 C，在 B 调 C 之前，RpcContext 记录的是 A 调 B 的信息，在 B 调 C 之后，RpcContext 记录的是 B 调 C 的信息。&lt;/p></description></item><item><title>Blog: 第一个 Dubbo Filter</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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:#586e75">&lt;/span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">refer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>REGISTRY_PROTOCOL&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> protocol&lt;span style="color:#719e07">.&lt;/span>refer&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> buildInvokerChain&lt;span style="color:#719e07">(&lt;/span>protocol&lt;span style="color:#719e07">.&lt;/span>refer&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">),&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>REFERENCE_FILTER_KEY&lt;span style="color:#719e07">,&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>CONSUMER&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> String key&lt;span style="color:#719e07">,&lt;/span> String group&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 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 style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Filter&lt;span style="color:#719e07">&amp;gt;&lt;/span> filters &lt;span style="color:#719e07">=&lt;/span> ExtensionLoader&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>Filter&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getActivateExtension&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">(),&lt;/span> key&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>filters&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> filters&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;gt;=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">--)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> Filter filter &lt;span style="color:#719e07">=&lt;/span> filters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>i&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">final&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> next &lt;span style="color:#719e07">=&lt;/span> last&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 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 style="color:#719e07">{&lt;/span>
&lt;/span>&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getInterface&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> URL &lt;span style="color:#268bd2">getUrl&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>isAvailable&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> filter&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>next&lt;span style="color:#719e07">,&lt;/span> invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker&lt;span style="color:#719e07">.&lt;/span>destroy&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> last&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;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>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getActivateExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> String key&lt;span style="color:#719e07">,&lt;/span> String group&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String value &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getActivateExtension&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">,&lt;/span> value &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> value&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">0&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">:&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>COMMA_SPLIT_PATTERN&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">),&lt;/span> group&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;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> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getActivateExtension&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">[]&lt;/span> values&lt;span style="color:#719e07">,&lt;/span> String group&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> exts &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&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> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> names &lt;span style="color:#719e07">=&lt;/span> values &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">:&lt;/span> Arrays&lt;span style="color:#719e07">.&lt;/span>asList&lt;span style="color:#719e07">(&lt;/span>values&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>REMOVE_VALUE_PREFIX &lt;span style="color:#719e07">+&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_KEY&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> getExtensionClasses&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Activate&lt;span style="color:#719e07">&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> cachedActivates&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String name &lt;span style="color:#719e07">=&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Activate activate &lt;span style="color:#719e07">=&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isMatchGroup&lt;span style="color:#719e07">(&lt;/span>group&lt;span style="color:#719e07">,&lt;/span> activate&lt;span style="color:#719e07">.&lt;/span>group&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> T ext &lt;span style="color:#719e07">=&lt;/span> getExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>REMOVE_VALUE_PREFIX &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> isActive&lt;span style="color:#719e07">(&lt;/span>activate&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exts&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>ext&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Collections&lt;span style="color:#719e07">.&lt;/span>sort&lt;span style="color:#719e07">(&lt;/span>exts&lt;span style="color:#719e07">,&lt;/span> ActivateComparator&lt;span style="color:#719e07">.&lt;/span>COMPARATOR&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> usrs &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&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 style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">();&lt;/span> i &lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String name &lt;span style="color:#719e07">=&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>i&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span> name&lt;span style="color:#719e07">.&lt;/span>startsWith&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>REMOVE_VALUE_PREFIX&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span> names&lt;span style="color:#719e07">.&lt;/span>contains&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>REMOVE_VALUE_PREFIX &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>DEFAULT_KEY&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>usrs&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exts&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> usrs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> usrs&lt;span style="color:#719e07">.&lt;/span>clear&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> T ext &lt;span style="color:#719e07">=&lt;/span> getExtension&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> usrs&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>ext&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>usrs&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exts&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>usrs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> exts&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过以上代码可以看到，用户自己配置的Filter中，有些是默认激活，有些是需要通过配置文件来激活。而所有Filter的加载顺序，也是先处理Dubbo的默认Filter，再来处理用户自己定义并且配置的Filter。通过&amp;quot;-&amp;ldquo;配置，可以替换掉Dubbo的原生Filter，通过这样的设计，可以灵活地替换或者修改Filter的加载顺序。&lt;/p>
&lt;h4 id="dubbo原生的filter">Dubbo原生的Filter&lt;/h4>
&lt;p>Dubbo原生的Filter很多，RpcContext，accesslog等功能都可以通过Dubbo来实现，下面我们来介绍一下Consumer端用于上下文传递的ConsumerContextFilter：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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> Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setInvoker&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setInvocation&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setLocalAddress&lt;span style="color:#719e07">(&lt;/span>NetUtils&lt;span style="color:#719e07">.&lt;/span>getLocalHost&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setRemoteAddress&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getHost&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getPort&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>invocation &lt;span style="color:#719e07">instanceof&lt;/span> RpcInvocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">((&lt;/span>RpcInvocation&lt;span style="color:#719e07">)&lt;/span>invocation&lt;span style="color:#719e07">).&lt;/span>setInvoker&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">().&lt;/span>clearAttachments&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>此Filter记录了调用过程中的状态信息，并且通过invocation对象将客户端设置的attachments参数传递到服务端。并且在调用完成后清除这些参数，这就是为什么请求状态信息可以按次记录并且进行传递。&lt;/p>
&lt;h4 id="实现一个dubbo-filter">实现一个Dubbo Filter&lt;/h4>
&lt;p>得益于Dubbo灵活的设计和良好的可扩展性，我们可以通过实现自己的Dubbo Filter来完成调用链路中的逻辑嵌入，比如，耗时统计，monitor信息统计等，下面我们来实现一个简单的Filter:&lt;/p>
&lt;p>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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>src
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-java
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-com
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-xxx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-XxxFilter.java (实现Filter接口)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-resources
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-META-INF
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> |-com.alibaba.dubbo.rpc.Filter (纯文本文件，内容为：xxx=com.xxx.XxxFilter)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>XxxFilter.java：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">XxxFilter&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Filter &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// before filter ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Result result &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// after filter ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>META-INF/dubbo/com.alibaba.dubbo.rpc.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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>xxx=com.xxx.XxxFilter
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&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:#586e75">&amp;lt;!-- 消费方调用过程拦截 --&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> filter=&lt;span style="color:#2aa198">&amp;#34;xxx&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;!-- 消费方调用过程缺省拦截器，将拦截所有reference --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:consumer&lt;/span> filter=&lt;span style="color:#2aa198">&amp;#34;xxx&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;!-- 提供方调用过程拦截 --&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> filter=&lt;span style="color:#2aa198">&amp;#34;xxx&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;!-- 提供方调用过程缺省拦截器，将拦截所有service --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:provider&lt;/span> filter=&lt;span style="color:#2aa198">&amp;#34;xxx&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-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Activate&lt;/span>&lt;span style="color:#719e07">(&lt;/span>group &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&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">XxxFilter&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Filter &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>使用 xml 的配置方式会更加灵活，粒度更细。&lt;/p>
&lt;p>在before和after中，可以实现自己的业务逻辑来赋予该filter一定的功能。编写和配置完成后，该filter就会被Dubbo框架激活并且在调用链中执行。&lt;/p></description></item><item><title>Blog: 回声测试</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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>&lt;span style="color:#719e07">(&lt;/span>Object message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&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>&lt;span style="color:#586e75">&lt;/span>DemoService demoService&lt;span style="color:#719e07">=&lt;/span> ctx&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 强制转型为EchoService
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>EchoService echoService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>EchoService&lt;span style="color:#719e07">)&lt;/span> demoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 回声测试可用性
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>String status &lt;span style="color:#719e07">=&lt;/span> echoService&lt;span style="color:#719e07">.&lt;/span>$echo&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;OK&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">assert&lt;/span>&lt;span style="color:#719e07">(&lt;/span>status&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;OK&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&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>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-Java" data-lang="Java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T &lt;span style="color:#268bd2">getProxy&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> generic&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]&lt;/span> interfaces &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String config &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>INTERFACES&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>config &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String&lt;span style="color:#719e07">[]&lt;/span> types &lt;span style="color:#719e07">=&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>COMMA_SPLIT_PATTERN&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>types &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> types&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[&lt;/span>types&lt;span style="color:#719e07">.&lt;/span>length &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">2&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>getInterface&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces&lt;span style="color:#719e07">[&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> EchoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> types&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// TODO can we load successfully for a different classloader?.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> interfaces&lt;span style="color:#719e07">[&lt;/span>i &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">2&lt;/span>&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> ReflectUtils&lt;span style="color:#719e07">.&lt;/span>forName&lt;span style="color:#719e07">(&lt;/span>types&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>interfaces &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]{&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getInterface&lt;span style="color:#719e07">(),&lt;/span> EchoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>GenericService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>isAssignableFrom&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">.&lt;/span>getInterface&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> generic&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> len &lt;span style="color:#719e07">=&lt;/span> interfaces&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]&lt;/span> temp &lt;span style="color:#719e07">=&lt;/span> interfaces&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[&lt;/span>len &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>arraycopy&lt;span style="color:#719e07">(&lt;/span>temp&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> interfaces&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">,&lt;/span> len&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> interfaces&lt;span style="color:#719e07">[&lt;/span>len&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> com&lt;span style="color:#719e07">.&lt;/span>alibaba&lt;span style="color:#719e07">.&lt;/span>dubbo&lt;span style="color:#719e07">.&lt;/span>rpc&lt;span style="color:#719e07">.&lt;/span>service&lt;span style="color:#719e07">.&lt;/span>GenericService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> getProxy&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> interfaces&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过这种方式，任何服务引用都可以被转型成 EchoService 来使用。
上面解释了客户端的实现，另外一边，用户在服务端也并没有实现 EchoService，那么客户端 EchoService 发出的调用在服务端是如何处理的呢？框架使用 Filter 机制来处理 EchoService 请求。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:#268bd2">@Activate&lt;/span>&lt;span style="color:#719e07">(&lt;/span>group &lt;span style="color:#719e07">=&lt;/span> Constants&lt;span style="color:#719e07">.&lt;/span>PROVIDER&lt;span style="color:#719e07">,&lt;/span> order &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">110000&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">EchoFilter&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Filter &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&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> Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> Invocation inv&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>inv&lt;span style="color:#719e07">.&lt;/span>getMethodName&lt;span style="color:#719e07">().&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>$ECHO&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> inv&lt;span style="color:#719e07">.&lt;/span>getArguments&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> inv&lt;span style="color:#719e07">.&lt;/span>getArguments&lt;span style="color:#719e07">().&lt;/span>length &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RpcResult&lt;span style="color:#719e07">(&lt;/span>inv&lt;span style="color:#719e07">.&lt;/span>getArguments&lt;span style="color:#719e07">()[&lt;/span>&lt;span style="color:#2aa198">0&lt;/span>&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>inv&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>请求经过 EchoFilter.invoke 方法时，如果判定为 $echo 调用，则直接返回请求参数，否则继续执行 Filter 链。EchoFilter 默认加入到每一个服务提供者的 Filter 链里 EchoFilter.invoke 方法时，如果判定为 $echo 调用，则直接返回请求参数，否则继续执行 Filter 链。EchoFilter 默认加入到每一个服务提供者的 Filter 链里。这样每一个服务提供者自动具备了响应 EchoService 的能力。&lt;/p>
&lt;p>通过上述分析，我们了解了框架是如何通过动态代理和 Filter 机制，使得用户可以透明地使用 EchoService 功能。&lt;/p></description></item><item><title>Blog: 以 Dubbo 为例，聊聊如何向开源项目做贡献</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># 设置源仓库&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git remote add upstream https://github.com/apache/dubbo.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># 拉取源仓库的更新&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git fetch upstream
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># 将自己仓库的主分支合并源仓库的更新&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git checkout master
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git merge upstream/master
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>pull request&lt;/strong>&lt;/p>
&lt;p>pull request 经常被缩写为 PR，指的是一次向源仓库请求合并的行为，如上是我 fork 了 dubbo 的仓库之后才存在的操作按钮。&lt;/p>
&lt;p>&lt;strong>源仓库视角的 pull request&lt;/strong>&lt;/p>
&lt;p>管理者会对 pull request 涉及的改动进行 review，以确保你的代码是符合规范的，逻辑有没有偏差，以及符合框架的功能需求。&lt;/p>
&lt;h3 id="22-travis-ci">2.2 Travis CI&lt;/h3>
&lt;p>一些自动化的 CI 流程被植入在每一次 pull request 的构建之中，用于给开源仓库去校验提交者的代码是否符合既定的规范，如：是否有编译问题，单元测试是否通过，覆盖率是否达标，代码风格是否合规等等。&lt;/p>
&lt;p>一般情况下，必须通过 CI，你的 pull request 才会被管理 review。&lt;/p>
&lt;h3 id="23-mailing-list">2.3 Mailing list&lt;/h3>
&lt;p>每个开源项目都会有自己的贡献规范，可以参考首页的 Contributing，来获取具体的信息。dubbo 作为一个孵化中的 apache 项目，遵守了 apache 的传统，在 &lt;a href="https://github.com/apache/dubbo/blob/master/CONTRIBUTING.md">Contributing&lt;/a> 中描述道：当你有新特性想要贡献给 Dubbo 时，官方推荐使用 Mailing list 的方式描述一遍你想要做的改动。&lt;/p>
&lt;p>Mailing list 简单来说，就是一个邮件通知机制，所有的 Dubbo 开发者都会订阅该邮箱：dev@dubbo.apache.org。有任何新特性的改动，或者什么建议想要通知其他开发者，都可以通过向该邮箱发送邮件来达到这个目的，相同地，你也会收到其转发的其他开发者的邮件。&lt;/p>
&lt;p>或者你是一个 Dubbo 的使用者，你想要得知开发者的改造方向，也可以订阅，这个&lt;a href="https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide">指南&lt;/a>可以帮助你订阅 Dubbo 的 Mailing list。&lt;/p>
&lt;blockquote>
&lt;p>作为一个 modern developer，你可能觉得 mailing list 的交流方式存在滞后性，这样的沟通方式不是特别的高效，但它作为 apache 项目的推荐交流方式存在其特殊的原因，在此不多赘述。总之遵循一个原则：bug fix或者讨论，可以在 github issue 中进行，影响较大的特性和讨论则推荐在 mailing list 中展开。&lt;/p>
&lt;/blockquote>
&lt;h2 id="3-其他贡献形式">3 其他贡献形式&lt;/h2>
&lt;p>不仅仅只有贡献代码，修复 bug 等行为才算作为开源做贡献，以下这些行为也属于主要形式：&lt;/p>
&lt;h3 id="31-撰写文档">3.1 撰写文档&lt;/h3>
&lt;p>&lt;a href="https://cn.dubbo.apache.org/">Dubbo文档&lt;/a>是其开源组成成分的重要一环，其内容源文件位于：&lt;a href="https://github.com/apache/dubbo-website">https://github.com/apache/dubbo-website&lt;/a>。同样也是一个 Git 仓库，任何你想要对 dubbo 知识点的补充，都可以在这儿提交 pull request，只需要一些 markdown 的语法知识，和一些可有可无的 npm 语法即可。如果你觉得贡献代码对于现在的自己仍然有点难度，不妨从贡献文档开始接触开源。&lt;/p>
&lt;h3 id="32-issue">3.2 ISSUE&lt;/h3>
&lt;p>无论是 Github 中的 Issue 还是 mailing list 中的讨论，无论是提出问题，汇报 bug，还是回答问题（bugfix 则不仅仅需要 Issue 了），协助管理者 review pull request，都是贡献的一种形式，勿以善小而不为。&lt;/p>
&lt;h3 id="33-其他行为">3.3 其他行为&lt;/h3>
&lt;p>任何你能够想到的，可以帮助开源项目变得更好的的行为，都属于开源贡献。例如，给每个 Issue 打上合适的 tag，关闭重复的 Issue，链接相关联的 Issue，线下组织沙龙，回答 Stack Overflow 上相关的问题，以及文档中一个错别字的修改等等。&lt;/p>
&lt;h2 id="4-开源最佳实践">4 开源最佳实践&lt;/h2>
&lt;h3 id="41-有效沟通">4.1 有效沟通&lt;/h3>
&lt;p>无论你处于什么样的目的：仅仅是一次性的贡献，亦或是永久性的加入社区，都的和他人进行沟通和交往，这是你要在开源圈发展必须修炼的技能。&lt;/p>
&lt;p>在你开启一个issue或PR之前，或者是在聊天室问问题之前，请牢记下面所列出的几点建议，会让你的工作更加的高效。&lt;/p>
&lt;p>&lt;strong>给出上下文&lt;/strong> 以便于让其他人能够快速的理解。比方说你运行程序时遇到一个错误，要解释你是如何做的，并描述如何才能再现错误现象。又比方说你是提交一个新的想法，要解释你为什么这么想，对于项目有用处吗（不仅仅是只有你！）&lt;/p>
&lt;blockquote>
&lt;p>😇 &lt;em>“当我做 Y 的时候 X 不能工作”&lt;/em>&lt;/p>
&lt;p>😢 &lt;em>“X 出问题! 请修复它。”&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>在进一步行动前，做好准备工作。&lt;/strong> 不知道没关系，但是要展现你尝试过、努力过。在寻求帮助之前，请确认阅读了项目的 README、文档、问题（开放的和关闭的）、邮件列表，并搜索了网络。当你表现出很强烈的求知欲的时候，人们是非常欣赏这点的，会很乐意的帮助你。&lt;/p>
&lt;blockquote>
&lt;p>😇 &lt;em>“我不确定 X 是如何实现的，我查阅了相关的帮助文档，然而毫无所获。”&lt;/em>&lt;/p>
&lt;p>😢 &lt;em>“我该怎么做 X ?”&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>保持请求内容短小而直接。&lt;/strong> 正如发送一份邮件，每一次的贡献，无论是多么的简单，都是需要他人去查阅的。很多项目都是请求的人多，提供帮助的人少。相信我，保持简洁，你能得到他人帮助的机会会大大的增加。&lt;/p>
&lt;blockquote>
&lt;p>😇 &lt;em>“我很乐意写 API 教程。”&lt;/em>&lt;/p>
&lt;p>😢 &lt;em>” 有一天我驾驶汽车行驶在高速公路上，在某个加油站加油的时候，突发奇想，我们应该这么做，不过在我进一步解释之前，我先和大家展示一下。。。”&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>让所有的沟通都是在公开场合下进行。&lt;/strong> 哪怕是很不起眼的小事，也不要去给维护者发私信，除非是你要分享一些敏感信息（诸如安全问题或严重的过失）。你若能够保持谈话是公开的，很多人可以你们交换的意见中学习和受益。&lt;/p>
&lt;blockquote>
&lt;p>😇 &lt;em>(评论) “@维护者 你好！我们该如何处理这个PR？”&lt;/em>&lt;/p>
&lt;p>😢 &lt;em>(邮件) “你好，非常抱歉给发信，但是我实在很希望你能看一下我提交的PR。”&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>大胆的提问（但是要谨慎！）。&lt;/strong> 每个人参与社区，开始的时候都是新手，哪怕是非常有经验的贡献者也一样，在刚进入一个新的项目的时候，也是新手。出于同样的原因,甚至长期维护人员并不总是熟悉一个项目的每一部分。给他们同样的耐心,你也会得到同样的回报。&lt;/p>
&lt;blockquote>
&lt;p>😇 &lt;em>“感谢查看了这个错误，我按照您的建议做了，这是输出结果。”&lt;/em>&lt;/p>
&lt;p>😢 &lt;em>“你为什么不修复我的问题？这难道不是你的项目吗？”&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>尊重社区的决定。&lt;/strong> 你的想法可能会和社区的优先级、愿景等有差异，他们可能对于你的想法提供了反馈和最后的决定的理由，这时你应该去积极的讨论，并寻求妥协的办法，维护者必须慎重的考虑你的想法。但是如果你实在是不能同意社区的做法，你可以坚持自己！保持自己的分支，或者另起炉灶。&lt;/p>
&lt;blockquote>
&lt;p>😇 &lt;em>“你不能支持我的用例，我蛮失望，但是你的解释仅仅是对一小部分用户起作用，我理解是为什么。感谢你的耐心倾听。”&lt;/em>&lt;/p>
&lt;p>😢 &lt;em>“你为什么不支持我的用例？这是不可接受的！”&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>以上几点，要铭记在心。&lt;/strong> 开源是由来自世界各地的人们共同协作实现的。面临的问题是跨语言、跨文化、不同的地理为止、不同的时区，另外，撰写文字的沟通更是难上加难，无法传达语气和情绪。请让这些会话都充满善意吧！在以下情形中请保持礼貌：推动一个想法、请求更多的上下文、进一步澄清你的立场。既然你在互联网找到了自己的所需，那么请尝试让它变得更好！&lt;/p>
&lt;h3 id="42-创建-issue">4.2 创建 issue&lt;/h3>
&lt;p>你应该在遇到下列情况下，去创建一个 issue：&lt;/p>
&lt;ul>
&lt;li>报告你自己无法解决的错误&lt;/li>
&lt;li>讨论一个高级主题或想法&lt;/li>
&lt;li>期望实现某新的特性，或者其它项目的想法&lt;/li>
&lt;/ul>
&lt;p>在 issue 的沟通中几点实用的技巧:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>如果你刚好看到一个开放的issue，恰是你打算解决的，&lt;/strong> 添加评论，告诉他人你将对此展开工作，并及时响应。这样的话，可以避免他人重复劳动。&lt;/li>
&lt;li>&lt;strong>如果说某个issue已经开放很久了，&lt;/strong> 这可能是已经有人正在解决中，又或者是早已经解决过了，所以也请添加评论，在打算开始工作之前，最好是确认一下。&lt;/li>
&lt;li>&lt;strong>如果你创建了一个issue，但是没多久自己解决了，&lt;/strong> 也要添加评论，让其他人知道，然后关闭该issue。记录本身就是对社区的贡献。&lt;/li>
&lt;/ul>
&lt;h3 id="43-创建-pull-request">4.3 创建 pull request&lt;/h3>
&lt;p>在下面的情形时，请你务必使用 PR：&lt;/p>
&lt;ul>
&lt;li>提交补丁 (例如，纠正拼写错误、损坏的链接、或者是其它较明显的错误）&lt;/li>
&lt;li>开始一项别人请求的任务，或者是过去在issue中早就讨论过的&lt;/li>
&lt;/ul>
&lt;p>一个 PR 并不代表着工作已经完成。它通常是尽早的开启一个PR，是为了其他人可以观看或者给作者反馈意见。只需要在子标题标记为“WIP”（正在进行中）。作者可以在后面添加很多评论。&lt;/p>
&lt;p>如果说项目是托管在 GitHub上的，以下是我们总结出的提交RP的建议：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Fork 代码仓库&lt;/strong> 并克隆到本地，在本地的仓库配置“上游”为远端仓库。这样你可以在提交你的PR时保持和“上游”同步，会减少很多解决冲突的时间。(更多关于同步的说明，请参考&lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork">这里&lt;/a>.)&lt;/li>
&lt;li>&lt;strong>创建一个分支&lt;/strong> 用于自己编辑。&lt;/li>
&lt;li>&lt;strong>参考任何相关的issue&lt;/strong> 或者在你的RP中支持文档(比如. “Closes #37.”)&lt;/li>
&lt;li>&lt;strong>包含之前和之后的快照&lt;/strong> 如果你的改动是包含了不同的 HTML/CSS。在你的PR中拖拉相应的图片。&lt;/li>
&lt;li>&lt;strong>测试你的改动！&lt;/strong> 若测试用例存在的话，跑一遍，以覆盖你的更改，若没有的话，则创建相应的用例。无论测试是否存在，一定要确保你的改动不会破坏掉现有的项目。&lt;/li>
&lt;li>&lt;strong>和项目现有的风格保持一致&lt;/strong> 尽你最大的努力，这也就是意味着在使用缩进、分号、以及注释很可能和你自己的风格大相径庭，但是为了节省维护者的精力，以及未来他人更好的理解和维护，还请你容忍一下。&lt;/li>
&lt;/ul>
&lt;h2 id="5-成为一个开源贡献者">5 成为一个开源贡献者&lt;/h2>
&lt;p>如果你有志于参与开源事业，可以尝试从自己最熟悉的项目开始，开源并不是属于高级开发者的专属词汇，它就是由你我这样的人在需求，修复，构建中演进下去的。Let&amp;rsquo;s try it !&lt;/p></description></item><item><title>Blog: Dubbo 外部化配置</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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 style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-annotation-consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&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>配置类&lt;/th>
&lt;th>标签&lt;/th>
&lt;th>用途&lt;/th>
&lt;th>解释&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>ProtocolConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;dubbo:protocol/&amp;gt;&lt;/code>&lt;/td>
&lt;td>协议配置&lt;/td>
&lt;td>用于配置提供服务的协议信息，协议由提供方指定，消费方被动接受&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ApplicationConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;dubbo:application/&amp;gt;&lt;/code>&lt;/td>
&lt;td>应用配置&lt;/td>
&lt;td>用于配置当前应用信息，不管该应用是提供者还是消费者&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ModuleConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;dubbo:module/&amp;gt;&lt;/code>&lt;/td>
&lt;td>模块配置&lt;/td>
&lt;td>用于配置当前模块信息，可选&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>RegistryConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;dubbo:registry/&amp;gt;&lt;/code>&lt;/td>
&lt;td>注册中心配置&lt;/td>
&lt;td>用于配置连接注册中心相关信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>MonitorConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;dubbo:monitor/&amp;gt;&lt;/code>&lt;/td>
&lt;td>监控中心配置&lt;/td>
&lt;td>用于配置连接监控中心相关信息，可选&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ProviderConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;dubbo:provider/&amp;gt;&lt;/code>&lt;/td>
&lt;td>提供方配置&lt;/td>
&lt;td>当 ProtocolConfig 和 ServiceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ConsumerConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;dubbo:consumer/&amp;gt;&lt;/code>&lt;/td>
&lt;td>消费方配置&lt;/td>
&lt;td>当 ReferenceConfig 某属性没有配置时，采用此缺省值，可选&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>MethodConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;dubbo:method/&amp;gt;&lt;/code>&lt;/td>
&lt;td>方法配置&lt;/td>
&lt;td>用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ArgumentConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;dubbo:argument/&amp;gt;&lt;/code>&lt;/td>
&lt;td>参数配置&lt;/td>
&lt;td>用于指定方法参数配置&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>通过申明对应的 Spring 扩展标签，在 Spring 应用上下文中将自动生成相应的配置 Bean。&lt;/p>
&lt;p>在 Dubbo 官方用户手册的&lt;a href="https://cn.dubbo.apache.org/zh/docs3-v2/java-sdk/reference-manual/config/properties/">“属性配置”&lt;/a>章节中，&lt;code>dubbo.properties&lt;/code> 配置属性能够映射到 &lt;code>ApplicationConfig&lt;/code> 、&lt;code>ProtocolConfig&lt;/code> 以及 &lt;code>RegistryConfig&lt;/code> 的字段。从某种意义上来说，&lt;code>dubbo.properties&lt;/code> 也是 Dubbo 的外部化配置。&lt;/p>
&lt;p>其中，引用“映射规则”的内容：&lt;/p>
&lt;blockquote>
&lt;h2 id="映射规则">映射规则&lt;/h2>
&lt;p>将 XML 配置的标签名，加属性名，用点分隔，多个属性拆成多行&lt;/p>
&lt;ul>
&lt;li>比如：&lt;code>dubbo.application.name=foo&lt;/code>等价于&lt;code>&amp;lt;dubbo:application name=&amp;quot;foo&amp;quot; /&amp;gt;&lt;/code>&lt;/li>
&lt;li>比如：&lt;code>dubbo.registry.address=10.20.153.10:9090&lt;/code>等价于&lt;code>&amp;lt;dubbo:registryaddress=&amp;quot;10.20.153.10:9090&amp;quot; /&amp;gt;&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>如果 XML 有多行同名标签配置，可用 id 号区分，如果没有 id 号将对所有同名标签生效&lt;/p>
&lt;ul>
&lt;li>比如：&lt;code>dubbo.protocol.rmi.port=1234&lt;/code>等价于&lt;code>&amp;lt;dubbo:protocol id=&amp;quot;rmi&amp;quot; name=&amp;quot;rmi&amp;quot; port=&amp;quot;1099&amp;quot; /&amp;gt;&lt;/code>&lt;/li>
&lt;li>比如：&lt;code>dubbo.registry.china.address=10.20.153.10:9090&lt;/code>等价于&lt;code>&amp;lt;dubbo:registry id=&amp;quot;china&amp;quot;address=&amp;quot;10.20.153.10:9090&amp;quot; /&amp;gt;&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>下面是 dubbo.properties 的一个典型配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo.application.name=foo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.owner=bar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address=10.20.153.10:9090
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/blockquote>
&lt;p>根据“映射规则”，Dubbo 即支持单配置 Bean 映射，也支持多 Bean 映射。综合以上需求，既要兼容 Dubbo 已有的一个或多个 Bean 字段映射绑定，也支持外部化配置。&lt;/p>
&lt;blockquote>
&lt;p>特别提醒：外部化配置（External Configuration）并非 Spring Boot 特有，即使在 Spring Framework 场景下亦能支持。也就是说 Dubbo 外部化配置即可在 Spring Framework 中工作，也能在 Spring Boot 中运行。&lt;/p>
&lt;/blockquote>
&lt;p>Dubbo 外部化配置（External Configuration） 支持起始版本为：&lt;code>2.5.8&lt;/code>&lt;/p>
&lt;h3 id="enabledubboconfig">&lt;code>@EnableDubboConfig&lt;/code>&lt;/h3>
&lt;h4 id="起始版本258">起始版本：&lt;code>2.5.8&lt;/code>&lt;/h4>
&lt;h4 id="使用说明">使用说明&lt;/h4>
&lt;h5 id="enabledubboconfig-定义">&lt;code>@EnableDubboConfig&lt;/code> 定义&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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> EnableDubboConfig &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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"> * It indicates whether binding to multiple Spring Beans.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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 default value is &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @revised 2.5.9
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">multiple&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>multiple&lt;/code> : 表示是否支持多Dubbo 配置 Bean 绑定。默认值为 &lt;code>false&lt;/code> ，即单 Dubbo 配置 Bean 绑定&lt;/li>
&lt;/ul>
&lt;h5 id="单-dubbo-配置-bean-绑定">单 Dubbo 配置 Bean 绑定&lt;/h5>
&lt;p>为了更好地向下兼容，&lt;code>@EnableDubboConfig&lt;/code> 提供外部化配置属性与 Dubbo 配置类之间的绑定，其中映射关系如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>配置类&lt;/th>
&lt;th>外部化配置属性前缀&lt;/th>
&lt;th>用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>ProtocolConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>dubbo.protocol&lt;/code>&lt;/td>
&lt;td>协议配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ApplicationConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>dubbo.application&lt;/code>&lt;/td>
&lt;td>应用配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ModuleConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>dubbo.module&lt;/code>&lt;/td>
&lt;td>模块配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>RegistryConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>dubbo.registry&lt;/code>&lt;/td>
&lt;td>注册中心配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>MonitorConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>dubbo.monitor&lt;/code>&lt;/td>
&lt;td>监控中心配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ProviderConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>dubbo.provider&lt;/code>&lt;/td>
&lt;td>提供方配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ConsumerConfig&lt;/code>&lt;/td>
&lt;td>&lt;code>dubbo.consumer&lt;/code>&lt;/td>
&lt;td>消费方配置&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>当标注 &lt;code>@EnableDubboConfig&lt;/code> 的类被扫描注册后，同时 Spring（Spring Boot）应用配置（&lt;code>PropertySources&lt;/code>）中存在&lt;code>dubbo.application.*&lt;/code> 时，&lt;code>ApplicationConfig&lt;/code> Bean 将被注册到在 Spring 上下文。否则，不会被注册。如果出现&lt;code>dubbo.registry.*&lt;/code>的配置，那么，&lt;code>RegistryConfig&lt;/code> Bean 将会创建，以此类推。即按需装配 Dubbo 配置 Bean。&lt;/p>
&lt;p>如果需要指定配置 Bean的 id，可通过&lt;code>**.id&lt;/code> 属性设置，以&lt;code>dubbo.application&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>&lt;span style="color:#586e75">## application&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.id &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">applicationBean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>以上配置等同于以下 Java Config 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 style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> ApplicationConfig &lt;span style="color:#268bd2">applicationBean&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setName&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-application&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> applicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>大致上配置属性与配置类绑定模式 - &lt;code>dubbo.application.* &lt;/code> 映射到 &lt;code>ApplicationConfig&lt;/code> 中的字段。&lt;/p>
&lt;blockquote>
&lt;p>注：当配置属性名称无法在配置类中找到字段时，将会忽略绑定&lt;/p>
&lt;/blockquote>
&lt;h5 id="多-dubbo-配置-bean-绑定">多 Dubbo 配置 Bean 绑定&lt;/h5>
&lt;p>Dubbo &lt;code>@Service&lt;/code> 和 &lt;code>@Reference&lt;/code> 允许 Dubbo 应用关联&lt;code>ApplicationConfig&lt;/code> Bean 或者指定多个&lt;code>RegistryConfig&lt;/code> Bean 等能力。换句话说，Dubbo 应用上下文中可能存在多个&lt;code>ApplicationConfig&lt;/code> 等 Bean定义。&lt;/p>
&lt;p>为了适应以上需要，因此从Dubbo &lt;code>2.5.9&lt;/code> 开始，&lt;code>@EnableDubboConfig&lt;/code> 支持多 Dubbo 配置 Bean 绑定，同时按照业界规约标准，与单 Dubbo 配置 Bean 绑定约定不同，配置属性前缀均为英文复数形式：&lt;/p>
&lt;blockquote>
&lt;p>详情请参考 ：https://github.com/alibaba/dubbo/issues/1141&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;code>dubbo.applications&lt;/code>&lt;/li>
&lt;li>&lt;code>dubbo.modules&lt;/code>&lt;/li>
&lt;li>&lt;code>dubbo.registries&lt;/code>&lt;/li>
&lt;li>&lt;code>dubbo.protocols&lt;/code>&lt;/li>
&lt;li>&lt;code>dubbo.monitors&lt;/code>&lt;/li>
&lt;li>&lt;code>dubbo.providers&lt;/code>&lt;/li>
&lt;li>&lt;code>dubbo.consumers&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>以&lt;code>dubbo.applications&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>dubbo.applications.${bean-name}.property-name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">${property-value}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>请读者注意，在单 Dubbo 配置 Bean 绑定时，可以通过指定&lt;code>id&lt;/code> 属性的方式，定义&lt;code>ApplicationConfig&lt;/code> Bean 的ID，即&lt;code>dubbo.application.id&lt;/code>。&lt;/p>
&lt;p>而在多 Dubbo 配置 Bean 绑定时，Bean ID 则由&lt;code>dubbo.applications.&lt;/code>与属性字段名称（&lt;code>.property-name&lt;/code>)之间的字符来表达。&lt;/p>
&lt;p>如下配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-properties" data-lang="properties">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># multiple Bean definition&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.applications.applicationBean.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.applications.applicationBean2.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.applications.applicationBean3.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该配置内容中，绑定了三个&lt;code>ApplicationConfig&lt;/code> Bean，分别是&lt;code>applicationBean&lt;/code>、&lt;code>applicationBean2&lt;/code>以及&lt;code>applicationBean3&lt;/code>&lt;/p>
&lt;h4 id="示例说明">示例说明&lt;/h4>
&lt;p>&lt;code>@EnableDubboConfig&lt;/code> 的使用方法很简答， 再次强调一点，当规约的外部配置存在时，相应的 Dubbo 配置类 才会提升为 Spring Bean。简言之，按需装配。&lt;/p>
&lt;h5 id="单-dubbo-配置-bean-绑定-1">单 Dubbo 配置 Bean 绑定&lt;/h5>
&lt;h6 id="外部化配置文件">外部化配置文件&lt;/h6>
&lt;p>将以下内容的外部化配置文件物理路径为：&lt;code>classpath:/META-INF/config.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>&lt;span style="color:#586e75"># 单 Dubbo 配置 Bean 绑定&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## application&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.id &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">applicationBean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## module&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.module.id &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">moduleBean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.module.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-module&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## registry&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">zookeeper://192.168.99.100:32770&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## protocol&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.port &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">20880&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## monitor&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.monitor.address &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">zookeeper://127.0.0.1:32770&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## provider&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.provider.host &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">127.0.0.1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## consumer&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.consumer.client &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">netty&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="enabledubboconfig-配置-bean">&lt;code>@EnableDubboConfig&lt;/code> 配置 Bean&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-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"> * Dubbo 配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableDubboConfig&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@PropertySource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;META-INF/config.properties&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Configuration&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">DubboConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;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-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"> * Dubbo 配置引导类
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboConfigurationBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注册当前配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>DubboConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// application
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// module
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ModuleConfig moduleConfig &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;moduleBean&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ModuleConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;moduleBean.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> moduleConfig&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// registry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RegistryConfig registryConfig &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>RegistryConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;registryConfig.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> registryConfig&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// protocol
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ProtocolConfig protocolConfig &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>ProtocolConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;protocolConfig.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;protocolConfig.port = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> protocolConfig&lt;span style="color:#719e07">.&lt;/span>getPort&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// monitor
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MonitorConfig monitorConfig &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>MonitorConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;monitorConfig.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> monitorConfig&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// provider
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ProviderConfig providerConfig &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>ProviderConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;providerConfig.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> providerConfig&lt;span style="color:#719e07">.&lt;/span>getHost&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// consumer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ConsumerConfig consumerConfig &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>ConsumerConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;consumerConfig.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> consumerConfig&lt;span style="color:#719e07">.&lt;/span>getClient&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;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-v" data-lang="v">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">applicationBean&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#268bd2">demo&lt;/span>&lt;span style="color:#719e07">-&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">moduleBean&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#268bd2">demo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#719e07">module&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">registryConfig&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">zookeeper&lt;/span>:&lt;span style="color:#586e75">//192.168.99.100:32770
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">protocolConfig&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">dubbo&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protocolConfig&lt;/span>.&lt;span style="color:#268bd2">port&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">20880&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">monitorConfig&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">zookeeper&lt;/span>:&lt;span style="color:#586e75">//127.0.0.1:32770
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">providerConfig&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">127.0.0.1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">consumerConfig&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">netty&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>不难发现，&lt;code>@EnableDubboConfig&lt;/code> 配置 Bean 配合外部化文件 &lt;code>classpath:/META-INF/config.properties&lt;/code>，与执行输出内容相同。&lt;/p>
&lt;h5 id="多-dubbo-配置-bean-绑定-1">多 Dubbo 配置 Bean 绑定&lt;/h5>
&lt;h6 id="外部化配置文件-1">外部化配置文件&lt;/h6>
&lt;p>将以下内容的外部化配置文件物理路径为：&lt;code>classpath:/META-INF/multiple-config.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>&lt;span style="color:#586e75"># 多 Dubbo 配置 Bean 绑定&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## dubbo.applications&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.applications.applicationBean.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.applications.applicationBean2.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.applications.applicationBean3.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="enabledubboconfig--配置-bean多">&lt;code>@EnableDubboConfig&lt;/code> 配置 Bean（多）&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-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableDubboConfig&lt;/span>&lt;span style="color:#719e07">(&lt;/span>multiple &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@PropertySource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;META-INF/multiple-config.properties&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboMultipleConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="实现引导类-1">实现引导类&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-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"> * Dubbo 配置引导类
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboConfigurationBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注册当前配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>DubboMultipleConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取 ApplicationConfig Bean：&amp;#34;applicationBean&amp;#34;、&amp;#34;applicationBean2&amp;#34; 和 &amp;#34;applicationBean3&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationConfig applicationBean &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationBean2 &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean2&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationBean3 &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean3&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> applicationBean&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean2.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> applicationBean2&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean3.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> applicationBean3&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="执行结果-1">执行结果&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>applicationBean.name = dubbo-demo-application
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>applicationBean2.name = dubbo-demo-application2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>applicationBean3.name = dubbo-demo-application3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>@EnableDubboConfig(multiple = true)&lt;/code> 执行后，运行结果说明&lt;code>ApplicationConfig&lt;/code> Bean 以及 ID 的定义方式。&lt;/p>
&lt;h3 id="enabledubboconfigbinding--enabledubboconfigbindings">&lt;code>@EnableDubboConfigBinding&lt;/code> &amp;amp; &lt;code>@EnableDubboConfigBindings&lt;/code>&lt;/h3>
&lt;p>&lt;code>@EnableDubboConfig&lt;/code>适合绝大多数外部化配置场景，然而无论是单 Bean 绑定，还是多 Bean 绑定，其&lt;strong>外部化配置属性前缀&lt;/strong>是固化的，如&lt;code>dubbo.application&lt;/code> 以及 &lt;code>dubbo.applications&lt;/code> 。&lt;/p>
&lt;p>当应用需要自定义&lt;strong>外部化配置属性前缀&lt;/strong>，&lt;code>@EnableDubboConfigBinding&lt;/code>能提供更大的弹性，支持单个外部化配置属性前缀（&lt;code>prefix&lt;/code>) 与 Dubbo 配置 Bean 类型（&lt;code>AbstractConfig&lt;/code> 子类）绑定，如果需要多次绑定时，可使用&lt;code>@EnableDubboConfigBindings&lt;/code>。&lt;/p>
&lt;blockquote>
&lt;p>尽管 Dubbo 推荐使用 Java 8 ，然而实际的情况，运行时的 JDK 的版本可能从 6到8 均有。因此，&lt;code>@EnableDubboConfigBinding&lt;/code> 没有实现&lt;code>java.lang.annotation.Repeatable&lt;/code>，即允许实现类不支持重复标注&lt;code>@EnableDubboConfigBinding&lt;/code>。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>@EnableDubboConfigBinding&lt;/code> 在支持外部化配置属性与 Dubbo 配置类绑定时，与 Dubbo 过去的映射行为不同，被绑定的 Dubbo 配置类将会提升为 Spring Bean，无需提前装配 Dubbo 配置类。同时，支持多 Dubbo 配置Bean 装配。其 Bean 的绑定规则与&lt;code>@EnableDubboConfig&lt;/code>一致。&lt;/p>
&lt;h4 id="起始版本-258">起始版本： &lt;code>2.5.8&lt;/code>&lt;/h4>
&lt;h4 id="使用说明-1">使用说明&lt;/h4>
&lt;h5 id="enabledubboconfigbinding-定义">&lt;code>@EnableDubboConfigBinding&lt;/code> 定义&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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> EnableDubboConfigBinding &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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"> * The name prefix of the properties that are valid to bind to {@link AbstractConfig Dubbo Config}.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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 name prefix of the properties to bind
&lt;/span>&lt;/span>&lt;/span>&lt;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">prefix&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return The binding type of {@link AbstractConfig Dubbo Config}.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @see AbstractConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @see ApplicationConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @see ModuleConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @see RegistryConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Class&lt;span style="color:#719e07">&amp;lt;?&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> AbstractConfig&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">type&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * It indicates whether {@link #prefix()} binding to multiple Spring Beans.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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 default value is &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">multiple&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>prefix()&lt;/code> : 指定待绑定 Dubbo 配置类的外部化配置属性的前缀，比如&lt;code>dubbo.application&lt;/code> 为 &lt;code>ApplicationConfig&lt;/code> 的外部化配置属性的前缀。&lt;code>prefix()&lt;/code> 支持占位符（Placeholder）, 并且其关联前缀值是否以&amp;quot;.&amp;quot; 作为结尾字符是可选的，即&lt;code>prefix() = &amp;quot;dubbo.application&amp;quot;&lt;/code> 与 &lt;code>prefix() = &amp;quot;dubbo.application.&amp;quot;&lt;/code> 效果相同&lt;/li>
&lt;li>&lt;code>type()&lt;/code> : 指定 Dubbo 配置类，所有 &lt;code>AbstractConfig&lt;/code> 的实现子类即可，如&lt;code>ApplicationConfig&lt;/code> 、&lt;code>RegistryConfig&lt;/code> 以及 &lt;code>ProtocolConfig&lt;/code> 等&lt;/li>
&lt;li>&lt;code>multiple()&lt;/code> : 表明是否需要将&lt;code>prefix()&lt;/code> 作为多个 &lt;code>type()&lt;/code> 类型的 Spring Bean 外部化配置属性。默认值为&lt;code>false&lt;/code>，即默认支持单个类型的 Spring 配置 Bean&lt;/li>
&lt;/ul>
&lt;p>假设标注 &lt;code>@EnableDubboConfigBinding&lt;/code> 的实现类被 Spring 应用上下文扫描并且注册后，其中&lt;code>prefix()&lt;/code> = &lt;code>dubbo.app&lt;/code> 、 &lt;code>type()&lt;/code> = &lt;code>ApplicationConfig.class&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>dubbo.app.id &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">applicationBean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.app.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Spring 应用上下文启动后，一个 ID 为 &amp;ldquo;applicationBean&amp;rdquo; 的 &lt;code>ApplicationConfig&lt;/code> Bean 被初始化，其 &lt;code>name&lt;/code> 字段被设置为 &amp;ldquo;dubbo-demo-application&amp;rdquo;。&lt;/p>
&lt;h5 id="enabledubboconfigbindings-定义">&lt;code>EnableDubboConfigBindings&lt;/code> 定义&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size: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> EnableDubboConfigBindings &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/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"> * The value of {@link EnableDubboConfigBindings}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;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 non-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> EnableDubboConfigBinding&lt;span style="color:#719e07">[]&lt;/span> &lt;span style="color:#268bd2">value&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>value&lt;/code> : 指定多个&lt;code>EnableDubboConfigBinding&lt;/code>，用于实现外部化配置属性前缀（&lt;code>prefix&lt;/code>) 与 Dubbo 配置 Bean 类型（&lt;code>AbstractConfig&lt;/code> 子类）绑定。&lt;/li>
&lt;/ul>
&lt;h4 id="示例说明-1">示例说明&lt;/h4>
&lt;h5 id="外部化配置文件-2">外部化配置文件&lt;/h5>
&lt;p>将以下内容的外部化配置文件物理路径为：&lt;code>classpath:/META-INF/bindings.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>&lt;span style="color:#586e75"># classpath:/META-INF/bindings.properties&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## 占位符值 : ApplicationConfig 外部配置属性前缀&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>applications.prefix &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo.apps.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## 多 ApplicationConfig Bean 绑定&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.apps.applicationBean.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.apps.applicationBean2.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.apps.applicationBean3.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-application3&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## 单 ModuleConfig Bean 绑定&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.module.id &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">moduleBean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.module.name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">dubbo-demo-module&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">## 单 RegistryConfig Bean 绑定&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://192.168.99.100:32770&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="enabledubboconfigbindings-配置-bean">&lt;code>EnableDubboConfigBindings&lt;/code> 配置 Bean&lt;/h5>
&lt;p>&lt;code>DubboConfiguration&lt;/code> 作为 Dubbo 配置 Bean，除通过 &lt;code>@EnableDubboConfigBinding&lt;/code> 绑定之外，还需要 &lt;code>@PropertySource&lt;/code> 指定外部化配置文件（&lt;code>classpath:/META-INF/bindings.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-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"> * Dubbo 配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@EnableDubboConfigBindings&lt;/span>&lt;span style="color:#719e07">({&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@EnableDubboConfigBinding&lt;/span>&lt;span style="color:#719e07">(&lt;/span>prefix &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${applications.prefix}&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type &lt;span style="color:#719e07">=&lt;/span> ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> multiple &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#586e75">// 多 ApplicationConfig Bean 绑定
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">@EnableDubboConfigBinding&lt;/span>&lt;span style="color:#719e07">(&lt;/span>prefix &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo.module&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#586e75">// 不带 &amp;#34;.&amp;#34; 后缀
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> type &lt;span style="color:#719e07">=&lt;/span> ModuleConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#586e75">// 单 ModuleConfig Bean 绑定
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">@EnableDubboConfigBinding&lt;/span>&lt;span style="color:#719e07">(&lt;/span>prefix &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dubbo.registry.&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#586e75">// 带 &amp;#34;.&amp;#34; 后缀
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> type &lt;span style="color:#719e07">=&lt;/span> RegistryConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#586e75">// 单 RegistryConfig Bean 绑定
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">})&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@PropertySource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;META-INF/bindings.properties&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Configuration&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">DubboConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="实现引导类-2">实现引导类&lt;/h5>
&lt;p>通过之前的使用说明，当 &lt;code>EnableDubboConfigBinding&lt;/code> 将外部配置化文件&lt;code>classpath:/META-INF/dubbo.properties&lt;/code> 绑定到 &lt;code>ApplicationConfig&lt;/code>后，其中 Spring Bean &amp;ldquo;applicationBean&amp;rdquo; 的 name 字段被设置成 &amp;ldquo;dubbo-demo-application&amp;rdquo;。同时， &lt;code>EnableDubboConfigBinding&lt;/code> 所标注的 &lt;code>DubboConfiguration&lt;/code> 需要被 Sring 应用上下文注册：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-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"> * Dubbo 配置引导类
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @author &amp;lt;a href=&amp;#34;mailto:mercyblitz@gmail.com&amp;#34;&amp;gt;Mercy&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DubboConfigurationBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建配置上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 注册当前配置 Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>DubboConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取 ApplicationConfig Bean：&amp;#34;applicationBean&amp;#34;、&amp;#34;applicationBean2&amp;#34; 和 &amp;#34;applicationBean3&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ApplicationConfig applicationBean &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationBean2 &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean2&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationBean3 &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean3&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ApplicationConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> applicationBean&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean2.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> applicationBean2&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;applicationBean3.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> applicationBean3&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取 ModuleConfig Bean：&amp;#34;moduleBean&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ModuleConfig moduleBean &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;moduleBean&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> ModuleConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;moduleBean.name = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> moduleBean&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 获取 RegistryConfig Bean
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RegistryConfig registry &lt;span style="color:#719e07">=&lt;/span> context&lt;span style="color:#719e07">.&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>RegistryConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>printf&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;registry.address = %s \n&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">.&lt;/span>getAddress&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="运行结果">运行结果&lt;/h5>
&lt;p>&lt;code>DubboConfigurationBootstrap&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-v" data-lang="v">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">applicationBean&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#268bd2">demo&lt;/span>&lt;span style="color:#719e07">-&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">applicationBean2&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#268bd2">demo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#268bd2">application2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">applicationBean3&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#268bd2">demo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#268bd2">application3&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">moduleBean&lt;/span>.&lt;span style="color:#268bd2">name&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">dubbo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#268bd2">demo&lt;/span>&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#719e07">module&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">registry&lt;/span>.&lt;span style="color:#268bd2">address&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#268bd2">zookeeper&lt;/span>:&lt;span style="color:#586e75">//192.168.99.100:32770
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出的内容与&lt;code>classpath:/META-INF/bindings.properties&lt;/code> 绑定的内容一致，符合期望。&lt;/p></description></item><item><title>Blog: Dubbo 博客文档中文排版指南</title><link>https://cn.dubbo.apache.org/zh/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://cn.dubbo.apache.org/zh/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>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>今天是 233 ° 的高温。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>新 MacBook Pro 有 15 % 的 CPU 性能提升。&lt;/p>
&lt;/blockquote>
&lt;h3 id="全角标点与其他字符之间不加空格">全角标点与其他字符之间不加空格&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>刚刚买了一部 iPhone，好开心！&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>刚刚买了一部 iPhone ，好开心！&lt;/p>
&lt;/blockquote>
&lt;h3 id="-ms-text-autospace-to-the-rescue">&lt;code>-ms-text-autospace&lt;/code> to the rescue?&lt;/h3>
&lt;p>Microsoft 有个 &lt;a href="http://msdn.microsoft.com/en-us/library/ie/ms531164(v=vs.85).aspx">&lt;code>-ms-text-autospace&lt;/code>&lt;/a> 的 CSS 属性可以实现自动为中英文之间增加空白。不过目前并未普及，另外在其他应用场景，例如 OS X、iOS 的用户界面目前并不存在这个特性，所以请继续保持随手加空格的习惯。&lt;/p>
&lt;h2 id="标点符号">标点符号&lt;/h2>
&lt;h3 id="不重复使用标点符号">不重复使用标点符号&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>德国队竟然战胜了巴西队！&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>她竟然对你说「喵」？！&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>德国队竟然战胜了巴西队！！&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>德国队竟然战胜了巴西队！！！！！！！！&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>她竟然对你说「喵」？？！！&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>她竟然对你说「喵」？！？！？？！！&lt;/p>
&lt;/blockquote>
&lt;h2 id="全角和半角">全角和半角&lt;/h2>
&lt;p>不明白什么是全角（全形）与半角（半形）符号？请查看维基百科词条『&lt;a href="http://zh.wikipedia.org/wiki/%E5%85%A8%E5%BD%A2%E5%92%8C%E5%8D%8A%E5%BD%A2">全角和半角&lt;/a>』。&lt;/p>
&lt;h3 id="使用全角中文标点">使用全角中文标点&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>嗨！你知道嘛？今天前台的小妹跟我说「喵」了哎！&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>核磁共振成像（NMRI）是什么原理都不知道？JFGI！&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>嗨! 你知道嘛? 今天前台的小妹跟我说 “喵” 了哎!&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>嗨!你知道嘛?今天前台的小妹跟我说”喵”了哎!&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>核磁共振成像 (NMRI) 是什么原理都不知道? JFGI!&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>核磁共振成像(NMRI)是什么原理都不知道?JFGI!&lt;/p>
&lt;/blockquote>
&lt;h3 id="数字使用半角字符">数字使用半角字符&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>这件蛋糕只卖 1000 元。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>这件蛋糕只卖 １０００ 元。&lt;/p>
&lt;/blockquote>
&lt;p>例外：在设计稿、宣传海报中如出现极少量数字的情形时，为方便文字对齐，是可以使用全角数字的。&lt;/p>
&lt;h3 id="遇到完整的英文整句特殊名词其內容使用半角标点">遇到完整的英文整句、特殊名词，其內容使用半角标点&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>乔布斯那句话是怎么说的？「Stay hungry, stay foolish.」&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>推荐你阅读《Hackers &amp;amp; Painters: Big Ideas from the Computer Age》，非常的有趣。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>乔布斯那句话是怎么说的？「Stay hungry，stay foolish。」&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>推荐你阅读《Hackers＆Painters：Big Ideas from the Computer Age》，非常的有趣。&lt;/p>
&lt;/blockquote>
&lt;h2 id="名词">名词&lt;/h2>
&lt;h3 id="专有名词使用正确的大小写">专有名词使用正确的大小写&lt;/h3>
&lt;p>大小写相关用法原属于英文书写范畴，不属于本 wiki 讨论內容，在这里只对部分易错用法进行简述。&lt;/p>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>使用 GitHub 登录&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>我们的客户有 GitHub、Foursquare、Microsoft Corporation、Google、Facebook, Inc.。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>使用 github 登录&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>使用 GITHUB 登录&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>使用 Github 登录&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>使用 gitHub 登录&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>使用 gｲんĤЦ8 登录&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>我们的客户有 github、foursquare、microsoft corporation、google、facebook, inc.。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>我们的客户有 GITHUB、FOURSQUARE、MICROSOFT CORPORATION、GOOGLE、FACEBOOK, INC.。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>我们的客户有 Github、FourSquare、MicroSoft Corporation、Google、FaceBook, Inc.。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>我们的客户有 gitHub、fourSquare、microSoft Corporation、google、faceBook, Inc.。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>我们的客户有 gｲんĤЦ8、ｷouЯƧquﾑгє、๓เςг๏ร๏Ŧt ς๏гק๏гคtเ๏ภn、900913、ƒ4ᄃëв๏๏к, IПᄃ.。&lt;/p>
&lt;/blockquote>
&lt;p>注意：当网页中需要配合整体视觉风格而出现全部大写／小写的情形，HTML 中请使用标准的大小写规范进行书写；并通过 &lt;code>text-transform: uppercase;&lt;/code>／&lt;code>text-transform: lowercase;&lt;/code> 对表现形式进行定义。&lt;/p>
&lt;h3 id="不要使用不地道的缩写">不要使用不地道的缩写&lt;/h3>
&lt;p>正确：&lt;/p>
&lt;blockquote>
&lt;p>我们需要一位熟悉 JavaScript、HTML5，至少理解一种框架（如 Backbone.js、AngularJS、React 等）的前端开发者。&lt;/p>
&lt;/blockquote>
&lt;p>错误：&lt;/p>
&lt;blockquote>
&lt;p>我们需要一位熟悉 Js、h5，至少理解一种框架（如 backbone、angular、RJS 等）的 FED。&lt;/p>
&lt;/blockquote>
&lt;h2 id="争议">争议&lt;/h2>
&lt;p>以下用法略带有个人色彩，即：无论是否遵循下述规则，从语法的角度来讲都是&lt;strong>正确&lt;/strong>的。&lt;/p>
&lt;h3 id="链接之间增加空格">链接之间增加空格&lt;/h3>
&lt;p>用法：&lt;/p>
&lt;blockquote>
&lt;p>请 &lt;a href="https://github.com/mzlogin/chinese-copywriting-guidelines">提交一个 issue&lt;/a> 并分配给相关同事。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>访问我们网站的最新动态，请 &lt;a href="https://github.com/mzlogin/chinese-copywriting-guidelines">点击这里&lt;/a> 进行订阅！&lt;/p>
&lt;/blockquote>
&lt;p>对比用法：&lt;/p>
&lt;blockquote>
&lt;p>请&lt;a href="https://github.com/mzlogin/chinese-copywriting-guidelines">提交一个 issue&lt;/a> 并分配给相关同事。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>访问我们网站的最新动态，请&lt;a href="https://github.com/mzlogin/chinese-copywriting-guidelines">点击这里&lt;/a>进行订阅！&lt;/p>
&lt;/blockquote>
&lt;h3 id="简体中文使用直角引号">简体中文使用直角引号&lt;/h3>
&lt;p>用法：&lt;/p>
&lt;blockquote>
&lt;p>「老师，『有条不紊』的『紊』是什么意思？」&lt;/p>
&lt;/blockquote>
&lt;p>对比用法：&lt;/p>
&lt;blockquote>
&lt;p>“老师，‘有条不紊’的‘紊’是什么意思？”&lt;/p>
&lt;/blockquote>
&lt;h2 id="工具">工具&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>仓库&lt;/th>
&lt;th>语言&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="https://github.com/vinta/paranoid-auto-spacing">vinta/paranoid-auto-spacing&lt;/a>&lt;/td>
&lt;td>JavaScript&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://github.com/huacnlee/auto-correct">huacnlee/auto-correct&lt;/a>&lt;/td>
&lt;td>Ruby&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://github.com/sparanoid/space-lover">sparanoid/space-lover&lt;/a>&lt;/td>
&lt;td>PHP (WordPress)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://github.com/NauxLiu/auto-correct">nauxliu/auto-correct&lt;/a>&lt;/td>
&lt;td>PHP&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://github.com/ricoa/copywriting-correct">ricoa/copywriting-correct&lt;/a>&lt;/td>
&lt;td>PHP&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://github.com/hotoo/pangu.vim">hotoo/pangu.vim&lt;/a>&lt;/td>
&lt;td>Vim&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://github.com/sparanoid/grunt-auto-spacing">sparanoid/grunt-auto-spacing&lt;/a>&lt;/td>
&lt;td>Node.js (Grunt)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://github.com/hjiang/scripts/blob/master/add-space-between-latin-and-cjk">hjiang/scripts/add-space-between-latin-and-cjk&lt;/a>&lt;/td>
&lt;td>Python&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="参考文献">参考文献&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://grammar.about.com/od/punctuationandmechanics/a/Guidelines-For-Using-Capital-Letters.htm">Guidelines for Using Capital Letters&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://en.wikipedia.org/wiki/Letter_case">Letter case - Wikipedia&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.oxforddictionaries.com/words/punctuation">Punctuation - Oxford Dictionaries&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://owl.english.purdue.edu/owl/section/1/6/">Punctuation - The Purdue OWL&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.wikihow.com/Use-English-Punctuation-Correctly">How to Use English Punctuation Corrently - wikiHow&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.opensuse.org/index.php?title=Help:%E6%A0%BC%E5%BC%8F">格式 - openSUSE&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://zh.wikipedia.org/wiki/%E5%85%A8%E5%BD%A2%E5%92%8C%E5%8D%8A%E5%BD%A2">全角和半角 - 维基百科&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://zh.wikipedia.org/wiki/%E5%BC%95%E8%99%9F">引号 - 维基百科&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://zh.wikipedia.org/wiki/%E7%96%91%E5%95%8F%E9%A9%9A%E5%98%86%E8%99%9F">疑问惊叹号 - 维基百科&lt;/a>&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>统一中文文案、排版的相关用法，降低团队成员之间的沟通成本，增强网站气质。原文出处：https://github.com/mzlogin/chinese-copywriting-guidelines&lt;/p>
&lt;/blockquote></description></item></channel></rss>